Navigation

Off-canvas menu / Navigation

Navigation.svelte
<script>
	import { onMount } from 'svelte';

	let isMenuOpen = false;
	const toggleMenu = () => (isMenuOpen = !isMenuOpen);

	const menuItems = [
		{ href: '/', text: 'Home' },
		{ href: '/about', text: 'Over ons' },
		{ href: '/killer', text: 'Diensten' },
		{ href: '/contact', text: 'Contact' }
	];

	onMount(() => {
		const handleResize = () => {
			if (window.innerWidth >= 768) {
				isMenuOpen = false;
			}
		};

		window.addEventListener('resize', handleResize);
		return () => window.removeEventListener('resize', handleResize);
	});
</script>

<div class="fixed min-h-screen w-full">
	<!-- Mobile menu button -->
	<button
		class="hamb fixed left-4 top-4 z-20 h-14 w-14 p-2 text-black md:hidden"
		class:active={isMenuOpen}
		on:click={toggleMenu}
		aria-label="Toggle Menu"
	>
		<span class="sr-only">Toggle Menu</span>
		<svg
			class="ham h-15 w-15 cursor-pointer duration-300"
			class:rotate-45={isMenuOpen}
			viewBox="0 0 100 100"
		>
			<path
				class="line top duration-300"
				class:stroke-dashoffset-[-64px]={isMenuOpen}
				d="m 30,33 h 40 c 3.722839,0 7.5,3.126468 7.5,8.578427 0,5.451959 -2.727029,8.421573 -7.5,8.421573 h -20"
			/>
			<path
				class="line middle origin-center duration-300"
				class:rotate-90={isMenuOpen}
				d="m 30,50 h 40"
			/>
			<path
				class="line bottom duration-300"
				class:stroke-dashoffset-[-64px]={isMenuOpen}
				d="m 70,67 h -40 c 0,0 -7.5,-0.802118 -7.5,-8.365747 0,-7.563629 7.5,-8.634253 7.5,-8.634253 h 20"
			/>
		</svg>
	</button>

	<!-- Off-canvas menu (mobile) -->
	<div
		class="fixed left-0 top-0 h-full w-64 transform bg-blue-500 pl-9 pt-16 text-white transition-transform duration-500 md:hidden"
		class:translate-x-0={isMenuOpen}
		class:-translate-x-full={!isMenuOpen}
	>
		<nav class="mt-8">
			<ul class="space-y-4">
				{#each menuItems as item}
					<li>
						<a href={item.href} class="block hover:text-blue-300" on:click={toggleMenu}
							>{item.text}</a
						>
					</li>
				{/each}
			</ul>
		</nav>
	</div>

	<!-- Desktop menu -->
	<div class="fixed hidden w-full md:block">
		<div class="container py-8">
			<nav class="hidden justify-between md:flex">
				<div>Logo</div>
				<ul class="flex gap-8">
					{#each menuItems as item}
						<li>
							<a href={item.href} class="hover:text-blue-300">{item.text}</a>
						</li>
					{/each}
				</ul>
			</nav>
		</div>
	</div>
</div>

<style lang="postcss">
	.line {
		@apply rounded fill-none stroke-black stroke-[5] duration-300;
	}

	.top {
		stroke-dasharray: 40 160;
	}
	.middle {
		stroke-dasharray: 40 142;
	}
	.bottom {
		stroke-dasharray: 40 85;
	}

	:global(.active) .top,
	:global(.active) .bottom {
		stroke-dashoffset: -64px;
	}

	:global(.hamb .ham) {
		-webkit-tap-highlight-color: transparent;
		-webkit-user-select: none;
		-moz-user-select: none;
		user-select: none;
	}
</style>

Simple Hamburger Menu

Dependencies

Built using a Svelte port of Flowbite

npm i -D flowbite-svelte flowbite
npm i -D flowbite-svelte-icons
Navigation.svelte
<script>
	import { page } from '$app/stores';
	import { Navbar, NavBrand, NavLi, NavUl, NavHamburger } from 'flowbite-svelte';
	$: activeUrl = $page.url.pathname;
</script>

<Navbar>
	<NavBrand href="/">
		<img src="/images/flowbite-svelte-icon-logo.svg" class="me-3 h-6 sm:h-9" alt="Flowbite Logo" />
		<span class="self-center whitespace-nowrap text-xl font-semibold dark:text-white">Flowbite</span
		>
	</NavBrand>
	<NavHamburger />
	<NavUl {activeUrl}>
		<NavLi href="/" class={activeUrl === "/" ? `text-red-500` : `text-black`}>Home</NavLi>
		<NavLi href="/about" class={activeUrl === "/about" ? `text-red-500` : `text-black`}>About</NavLi>
		<NavLi href="/killer" class={activeUrl === "/killer" ? `text-red-500` : `text-black`}>Killer</NavLi>
	</NavUl>
</Navbar>

Last updated