NavigationMenu
Site navigation with large animated dropdown panels.
Site navigation
Hover a trigger, or click it. Moving across the triggers swaps the panel in the shared viewport, which animates to the new size; the indicator follows.
Code
<NavigationMenu indicator>
<NavigationMenuItem label="Home">
<ul class="grid is-home">
<li class="featured">
<NavigationMenuLink href="/">Elements UI ...</NavigationMenuLink>
</li>
<li><NavigationMenuLink href="/docs">Introduction ...</NavigationMenuLink></li>
...
</ul>
</NavigationMenuItem>
<NavigationMenuItem label="Components">
<ul class="grid is-two">
<li e:for={c of components}>
<NavigationMenuLink href={c.href}>
<div class="title">{c.title}</div>
<p class="desc">{c.description}</p>
</NavigationMenuLink>
</li>
</ul>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuLink href="/docs" trigger>Docs</NavigationMenuLink>
</NavigationMenuItem>
<NavigationMenuItem label="With Icon">...</NavigationMenuItem>
</NavigationMenu>Usage
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>Examples
Without the viewport
viewport={false} opens each panel under its own trigger, with its own box. The current page's link is active. This menu keeps Escape (onEscapeKeyDown prevented) and logs presses and focus outside.
Code
<NavigationMenu viewport={false}
onEscapeKeyDown={(e) => e.preventDefault()}
onPointerDownOutside={(e) => log("pointer")}
onFocusOutside={(e) => log("focus")}
onInteractOutside={(e) => log("interact")}>
<NavigationMenuItem label="List">...</NavigationMenuItem>
<NavigationMenuItem label="Simple">...</NavigationMenuItem>
</NavigationMenu>Vertical
orientation=vertical stacks the list; panels open beside it and Up and Down move between triggers.
Code
<NavigationMenu orientation="vertical" indicator>
<NavigationMenuItem label="Products">...</NavigationMenuItem>
<NavigationMenuItem label="Resources">...</NavigationMenuItem>
</NavigationMenu>Controlled, delay and disabled
value is two-way; delay={0} opens on hover at once. A disabled trigger never opens. onSelect with preventDefault() keeps the panel open. onOpenChangeComplete fires once a panel has finished opening and once the menu has finished closing.
Code
<NavigationMenu value={state.nav}
delay={0}
onValueChange={(v) => log(v)}
onOpenChangeComplete={(open) => log(open ? "open complete" : "closed complete")}>
<NavigationMenuItem value="learn" label="Learn">...</NavigationMenuItem>
<NavigationMenuItem value="overview" label="Overview">...</NavigationMenuItem>
<NavigationMenuItem value="soon" label="Soon" disabled>...</NavigationMenuItem>
</NavigationMenu>
<Button onclick={() => state.nav = "overview"}>Open Overview</Button>Offset and arrow
alignOffset moves the viewport along the list from its aligned spot. arrow draws an arrow on the viewport's edge that points at the open trigger and follows it from panel to panel; arrowPadding keeps it off the viewport's corners.
Code
<NavigationMenu align="start" alignOffset={16}>
<NavigationMenuItem label="Shifted">...</NavigationMenuItem>
</NavigationMenu>
<NavigationMenu arrow>
<NavigationMenuItem label="Guides">...</NavigationMenuItem>
<NavigationMenuItem label="Reference">...</NavigationMenuItem>
</NavigationMenu>
<!-- the arrow keeps arrowPadding px from the viewport's corners -->
<NavigationMenu arrow arrowPadding={24} align="start" alignOffset={30}>...</NavigationMenu>Keep open on click
closeOnClick={false} on a link follows it and leaves the panel open, for links that jump within the page. Links close the menu by default.
Code
<NavigationMenu>
<NavigationMenuItem label="Sections">
<ul>
<!-- follows the link and leaves the panel open -->
<li><NavigationMenuLink href="#close-off" closeOnClick={false}>Jump, keep open</NavigationMenuLink></li>
<li><NavigationMenuLink href="#close-on">Jump and close</NavigationMenuLink></li>
</ul>
</NavigationMenuItem>
</NavigationMenu>Backdrop and positioning
backdrop dims the page under an open panel and leaves the list above it; a press on it closes the menu. positionMethod="absolute" places the viewport in page coordinates and disableAnchorTracking places it once when it opens. collisionBoundary names a box whose edges count like the screen's. When the list scrolls out of view the viewport hides (data-anchor-hidden), unless sticky="always" keeps it in view. anchor places the viewport against another element, such as a whole header. collisionPadding keeps the viewport that far from the screen's edges, and collisionAvoidance with side "flip" opens it above the list when there is no room below.
Code
<!-- dims the page under the open panel; the list stays above it -->
<NavigationMenu backdrop>...</NavigationMenu>
<!-- placed once, in page coordinates -->
<NavigationMenu positionMethod="absolute" disableAnchorTracking>...</NavigationMenu>
<!-- the panel stays inside .box, and in view when the list scrolls away -->
<NavigationMenu collisionBoundary=".box" sticky="always">...</NavigationMenu>
<!-- opens under the whole header instead of the list -->
<header id="site-header">
<NavigationMenu anchor="#site-header">...</NavigationMenu>
</header>
<!-- 24px from the screen edges; flips above the list when there is no room below -->
<NavigationMenu collisionPadding={24} collisionAvoidance={{ side: "flip" }}>...</NavigationMenu>Submenus
A NavigationMenu with sub inside a panel keeps one item open, like tabs; hover or click switches it.
Code
<NavigationMenuItem label="Products">
<NavigationMenu sub>
<NavigationMenuItem value="ui" label="UI">...links...</NavigationMenuItem>
<NavigationMenuItem value="data" label="Data">...links...</NavigationMenuItem>
</NavigationMenu>
</NavigationMenuItem>Nested flyouts, keepMounted
A vertical NavigationMenu with viewport={false} inside a panel opens its own panels as flyouts beside the panel. Escape closes the flyout first; a link closes them all. keepMounted renders the Docs panel with the page, so its links are in the document for crawlers while it is closed.
Code
<NavigationMenu label="Docs">
<NavigationMenuItem label="Docs" keepMounted>
<ul>
<li><NavigationMenuLink href="/quick-start">Quick start</NavigationMenuLink></li>
</ul>
<NavigationMenu orientation="vertical" viewport={false} label="Handbook">
<NavigationMenuItem label="Guides">
<ul>
<li><NavigationMenuLink href="/styling">Styling</NavigationMenuLink></li>
<li><NavigationMenuLink href="/animation">Animation</NavigationMenuLink></li>
</ul>
</NavigationMenuItem>
<NavigationMenuItem label="Reference">...</NavigationMenuItem>
</NavigationMenu>
</NavigationMenuItem>
</NavigationMenu>Right to left
Under dir=rtl the list runs right to left and ArrowLeft moves to the next trigger.
Code
<NavigationMenu dir="rtl" indicator>
<NavigationMenuItem label="البدء">...</NavigationMenuItem>
<NavigationMenuItem label="المكونات">...</NavigationMenuItem>
</NavigationMenu>