Navigation Menu
Horizontal top-level navigation with hover/focus-triggered popover panels.
bun x bosia@latest add navigation-menuA compound horizontal navigation menu where each item can reveal a popover of related links. Only one panel is open at a time and a small hover-intent delay prevents flicker as the cursor moves between triggers and panels.
Preview
Props
NavigationMenu
| Prop | Type | Default |
|---|---|---|
value |
string | null |
null |
openDelay |
number |
150 |
closeDelay |
number |
200 |
class |
string |
"" |
Sub-components
NavigationMenu— root<nav aria-label="Main">, context provider that tracks the single open item and handles click-outside + Escape dismissalNavigationMenuList—<ul>flex containerNavigationMenuItem—<li>that generates a stable id and hosts the hover bridgeNavigationMenuTrigger—<button>witharia-expanded,aria-controls, rotating chevron, andArrowDownto focus the first link inside the panelNavigationMenuContent— absolute-positioned popover; closes on Escape and returns focus to its triggerNavigationMenuLink— block anchor withfocus-visiblering
Usage
<script lang="ts">
import {
NavigationMenu,
NavigationMenuList,
NavigationMenuItem,
NavigationMenuTrigger,
NavigationMenuContent,
NavigationMenuLink,
} from "$lib/components/ui/navigation-menu";
</script>
<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuTrigger>Getting Started</NavigationMenuTrigger>
<NavigationMenuContent>
<ul class="grid w-[420px] gap-3">
<li>
<NavigationMenuLink href="/getting-started">
Introduction
</NavigationMenuLink>
</li>
<li>
<NavigationMenuLink href="/project-structure">
Project Structure
</NavigationMenuLink>
</li>
</ul>
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuLink
href="/docs"
class="inline-flex h-9 items-center rounded-md px-4 py-2 text-sm font-medium hover:bg-accent"
>
Docs
</NavigationMenuLink>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>Hover Delays
openDelay controls how long a pointer must rest on a trigger before the first panel opens. closeDelay gives users a grace period to move the cursor from the trigger into the panel (the "hover bridge").
Switching between already-open items is always instant — users expect snappy horizontal navigation once the menu has revealed itself.
<NavigationMenu openDelay={200} closeDelay={150}>
<!-- ... -->
</NavigationMenu>Controlled
Bind value to track or set the currently open item id.
<script lang="ts">
let value = $state<string | null>(null);
</script>
<NavigationMenu bind:value>
<!-- ... -->
</NavigationMenu>
<p>Open item: {value ?? "none"}</p>Keyboard
| Key | Action |
|---|---|
Tab |
Move focus to the next trigger or link |
ArrowDown |
On a trigger, opens the panel and focuses the first link inside |
Escape |
Closes the open panel and returns focus to its trigger |