Manual UI Templates NavigationMenu

NavigationMenu

elements man ui/templates/navigation-menu

Site navigation with large animated dropdown panels.

Example
import { NavigationMenu, NavigationMenuItem, NavigationMenuLink, navigationMenuTriggerClass, } from "@elements/ui/navigation-menu"; <NavigationMenu indicator> <NavigationMenuItem label="Getting started"> <ul class="nav-grid"> <li><NavigationMenuLink href="/docs">Introduction</NavigationMenuLink></li> <li><NavigationMenuLink href="/docs/install">Installation</NavigationMenuLink></li> </ul> </NavigationMenuItem> <NavigationMenuItem label="Components">...</NavigationMenuItem> <NavigationMenuItem> <NavigationMenuLink href="/docs" trigger>Docs</NavigationMenuLink> </NavigationMenuItem> </NavigationMenu>

Parts

Part What it is
NavigationMenu The root <nav>: owns value (the open item), renders the list, the optional indicator and the shared viewport.
NavigationMenuItem A list item. With label it is a trigger button and a panel (its default slot); without, it holds a link.
NavigationMenuLink A link. Choosing it closes the menu. active marks the current page (aria-current=page). trigger gives it the trigger look for the top list.
NavigationMenu sub A NavigationMenu with sub inside a panel is a submenu: one item always open, panels inline, switching on hover or click.
.navigation-menu-viewport The box under the open panel; it animates between panels' sizes (--navigation-menu-viewport-width and -height).
.navigation-menu-indicator The arrow under the open trigger, moved with --navigation-menu-indicator-translate-x (or -y) and sized with --navigation-menu-indicator-size.

Attributes

NavigationMenu

Attribute Type Default Description
value string (two-way) "" The open item's value; empty when closed. Bind it to open an item from code.
delay number 200 Hover time in ms before a panel opens.
skipDelay number 300 Within this many ms of a close, hovering opens at once, so moving across triggers does not wait each time.
closeDelay number 150 Ms after the pointer leaves the trigger and panel before the panel closes.
orientation "horizontal" | "vertical" "horizontal" Horizontal opens panels below; vertical stacks the list and opens panels beside it.
viewport boolean true Show panels in one shared, animated box under the list. false places each panel under its own trigger with its own box.
indicator boolean false Show an arrow under the open trigger.
arrow boolean false Draw an arrow on the viewport's edge that points at the open trigger.
arrowPadding number 5 px the viewport arrow keeps from the viewport's corners.
sub boolean false This menu is a submenu inside a panel: one item is always open and its panel shows inline.
align "start" | "center" | "end" "start" Where the viewport (or a panel) sits along the list.
sideOffset number 6 Gap between the list and the viewport, in px.
alignOffset number 0 Moves the viewport (or a panel) along the list from its aligned spot, in px.
positionMethod "fixed" | "absolute" "fixed" "absolute" places the viewport in page coordinates instead of viewport ones.
disableAnchorTracking boolean false Place the viewport once when it opens; it stays put if its anchor moves, and is placed again on a scroll or resize.
collisionBoundary string "" A CSS selector for boxes whose edges count like the screen's when placing the viewport.
sticky "partial" | "always" "partial" always keeps the viewport in view when the list scrolls away; otherwise it hides with data-anchor-hidden.
anchor string | Element | null null An element, or a selector for one, the viewport is placed against instead of the list, such as a whole header.
collisionPadding number 8 px the viewport keeps from the screen's edges and the collision boundary.
collisionAvoidance { side?: "flip" | "shift" | "none"; align?: "flip" | "shift" | "none"; fallbackAxisSide?: "start" | "end" | "none" } | null null How the viewport avoids the edges. By default it keeps its side and slides along it; side "flip" opens it above the list when there is no room below.
backdrop boolean false Render a layer under the open panel that dims the page, cut away over the list; a press on it closes the menu.
dir "" | "ltr" | "rtl" "" Reading direction for the arrow keys. Empty follows the page.
label string "Main" aria-label of the <nav> landmark.
class string "" Extra classes on the <nav>.
onValueChange (value: string) => void () => {} Called when the menu opens, switches or closes a panel itself.
onOpenChangeComplete (open: boolean) => void () => {} Called once a panel has finished opening from a closed menu (true), and once the last panel and the viewport have finished closing (false).
onEscapeKeyDown, onPointerDownOutside, onFocusOutside, onInteractOutside (e) => void () => {} Called before the open panel closes that way; e.preventDefault() keeps it open.

NavigationMenuItem

Attribute Type Default Description
value string an id Names the item for the root's value.
label string "" Makes the item a trigger with this text and its default slot a panel. The trigger slot replaces the text.
disabled boolean false The trigger does not open its panel.
keepMounted boolean false The panel renders with the page and stays in the DOM, hidden, while closed, so its links are there for crawlers.
class string "" Extra classes on the panel.
attrs Record<string, string | number | boolean | undefined> {} Other attributes for the trigger button.

NavigationMenuLink

Attribute Type Default Description
href string (required) Required. Where the link goes.
active boolean false The current page: aria-current=page and data-active.
trigger boolean false Style the link like a trigger, for a link in the top list.
closeOnClick boolean true false follows the link and leaves the menu open.
onSelect (e: Event) => void () => {} Called when the link is chosen; e.preventDefault() keeps the menu open and cancels the navigation.
class, attrs string, Record "", {} Extra classes and attributes on the <a>.

Slots

Slot Part Content
default NavigationMenu The NavigationMenuItem elements.
default NavigationMenuItem With label: the panel's content. Without: the item's link.
trigger NavigationMenuItem Rich trigger content; label is still needed (it makes the item a trigger).
default NavigationMenuLink The link's content.

Keyboard

Keys Action
Tab / Shift + Tab Moves through triggers, links and an open panel's links in page order.
Enter / Space On a trigger: opens or closes its panel.
ArrowRight / ArrowLeft The next or previous trigger or link in the list (flipped under rtl).
ArrowDown / ArrowUp The next or previous trigger or link, in either orientation, unless ArrowDown enters an open panel.
ArrowDown (horizontal), ArrowRight (vertical) On an open trigger: moves into its panel.
Home / End The first or last trigger or link in the list.
Arrow keys in a panel The next or previous link or control in the panel.
Escape Closes the panel and focuses its trigger.

Data attributes

Attribute Values On Description
data-orientation "horizontal" | "vertical" nav, list, indicator, viewport
data-state "open" | "closed" trigger, panel, viewport
data-pressed present trigger While the panel a click of the trigger opened is open (not a hover).
data-instant "click" | "dismiss" viewport, panel click when a keyboard press opened it, dismiss when Escape closed it.
data-side, data-align placement viewport, panel, arrow
data-uncentered present arrow When the corners hold the arrow off the trigger's center.
data-anchor-hidden present viewport, panel While the list is scrolled out of view (unless sticky is always).
--positioner-width / --positioner-height px viewport Its own size while open.
data-state, data-starting-style, data-ending-style "open" | "closed" .navigation-menu-backdrop With backdrop.
data-disabled present trigger
data-motion "from-start" | "from-end" | "to-start" | "to-end" panel Which way a panel enters or leaves when the open item changes.
data-state "visible" | "hidden" indicator
data-active present NavigationMenuLink The current page.

Accessibility

  • A <nav> landmark (aria-label, default Main) with a list of triggers and links: not role=menu, since site navigation is a set of disclosures (WAI-ARIA disclosure navigation).
  • Each trigger is a <button> with aria-expanded and aria-controls; its panel follows it in the DOM, so Tab reaches the panel's links right after the trigger.
  • Escape closes the panel and returns focus to its trigger; focus or a press outside the navigation closes it.
  • NavigationMenuLink active sets aria-current=page.
  • Panels are popover=manual in the top layer: never clipped, no portal.

See also