Components Navigation
NavigationMenu
StableSite or product navigation with optional flyout panels of links, using the disclosure pattern.
Use NavigationMenu for the primary navigation of a website or product, such as the Binds marketing header. Panels are revealed by disclosure buttons and contain ordinary links, which is the correct semantic for navigation; role="menu" is reserved for application commands (see DropdownMenu and Menubar). A vertical orientation expands panels inline for sidebars and mobile drawers.
Overview
import { NavigationMenu, NavigationMenuContent, NavigationMenuItem, NavigationMenuLink, NavigationMenuTrigger } from '@bindshq/stance-react';
export function Basic() {
return (
<NavigationMenu aria-label="Main">
<NavigationMenuItem>
<NavigationMenuLink href="#home" active>
Home
</NavigationMenuLink>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuTrigger>Products</NavigationMenuTrigger>
<NavigationMenuContent>
<NavigationMenuLink href="#esignify">eSignify</NavigationMenuLink>
<NavigationMenuLink href="#stance">Stance</NavigationMenuLink>
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuTrigger>Company</NavigationMenuTrigger>
<NavigationMenuContent>
<NavigationMenuLink href="#about">About Binds</NavigationMenuLink>
<NavigationMenuLink href="#privacy">Privacy</NavigationMenuLink>
<NavigationMenuLink href="#terms">Terms</NavigationMenuLink>
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuLink href="#contact">Contact</NavigationMenuLink>
</NavigationMenuItem>
</NavigationMenu>
);
}Light, dark and high contrast
Every Stance component is designed and tested in all three appearance modes.
Import
import { NavigationMenu, NavigationMenuItem, NavigationMenuTrigger, NavigationMenuContent, NavigationMenuLink } from '@bindshq/stance-react';Examples
Rich panel
Links can carry an icon and a description.
import { Icon, NavigationMenu, NavigationMenuContent, NavigationMenuItem, NavigationMenuLink, NavigationMenuTrigger } from '@bindshq/stance-react';
export function RichPanel() {
return (
<NavigationMenu aria-label="Products">
<NavigationMenuItem>
<NavigationMenuTrigger>Products</NavigationMenuTrigger>
<NavigationMenuContent>
<NavigationMenuLink
href="#esignify"
icon={<Icon name="mail" />}
description="Consistent email signatures for every employee."
>
eSignify
</NavigationMenuLink>
<NavigationMenuLink
href="#stance"
icon={<Icon name="layout-grid" />}
description="The open-source Binds design system."
>
Stance
</NavigationMenuLink>
<NavigationMenuLink
href="#integrations"
icon={<Icon name="zap" />}
description="Microsoft 365 and Exchange deployment."
>
Integrations
</NavigationMenuLink>
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuLink href="#pricing">Pricing</NavigationMenuLink>
</NavigationMenuItem>
</NavigationMenu>
);
}Vertical (sidebar)
Panels expand inline and do not open on hover.
import { NavigationMenu, NavigationMenuContent, NavigationMenuItem, NavigationMenuLink, NavigationMenuTrigger } from '@bindshq/stance-react';
export function Vertical() {
return (
<div style={{ width: 240 }}>
<NavigationMenu aria-label="Dashboard" orientation="vertical" defaultValue="templates">
<NavigationMenuItem>
<NavigationMenuLink href="#dashboard">Dashboard</NavigationMenuLink>
</NavigationMenuItem>
<NavigationMenuItem value="templates">
<NavigationMenuTrigger>Templates</NavigationMenuTrigger>
<NavigationMenuContent>
<NavigationMenuLink href="#my-templates" active>
My templates
</NavigationMenuLink>
<NavigationMenuLink href="#gallery">Gallery</NavigationMenuLink>
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuLink href="#users">Organisation users</NavigationMenuLink>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuLink href="#settings">Settings</NavigationMenuLink>
</NavigationMenuItem>
</NavigationMenu>
</div>
);
}API reference
NavigationMenu
| Prop | Type | Default | Description |
|---|---|---|---|
aria-label | string | None | Name of the navigation landmark, e.g. “Main”. Required when a page has more than one nav. |
value / defaultValue | string | null | None | The open panel (controlled or initial). |
onValueChange | (value: string | null) => void | None | Called when a panel opens or closes. |
orientation | 'horizontal' | 'vertical' | 'horizontal' | Vertical expands panels inline. |
openOnHover | boolean | true (horizontal) | Open panels on mouse hover after a short delay. |
NavigationMenuItem
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | None | Identifier for the item’s panel. Generated by default. |
NavigationMenuTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
...props | ButtonHTMLAttributes | None | Disclosure button with aria-expanded and aria-controls. |
NavigationMenuContent
| Prop | Type | Default | Description |
|---|---|---|---|
forceMount | boolean | true | Keep the panel in the DOM while hidden so its links stay indexable. |
NavigationMenuLink
| Prop | Type | Default | Description |
|---|---|---|---|
active | boolean | false | Marks the current page with aria-current="page". |
description | ReactNode | None | Secondary line in rich panels. |
icon | ReactNode | None | Leading visual in rich panels. |
asChild | boolean | false | Render your router’s link component. |
Accessibility
- Renders a nav landmark containing a list, so screen readers announce the number of entries.
- Triggers are buttons with aria-expanded and aria-controls (WAI-ARIA disclosure navigation), not menus.
- The current page link carries aria-current="page".
- Escape closes the open panel and returns focus to its trigger; moving focus out of the navigation closes it.
- Hover opening is a convenience only; every panel opens by click and keyboard as well.
Keyboard
| Keys | Action |
|---|---|
| Tab | Move through triggers and links in order. |
| ArrowLeft ArrowRight | Move between top-level entries (ArrowUp/ArrowDown when vertical). |
| Home End | First or last top-level entry. |
| Enter Space | Toggle a panel. |
| ArrowDown | On a trigger: open its panel and focus the first link. |
| ArrowUp ArrowDown | Inside a panel: move between its links. |
| Escape | Close the panel and return to its trigger. |
Usage guidelines
Do
- Keep top-level labels to one or two words.
- Mark the current section with active.
Don’t
- Put actions (buttons that change data) in navigation panels.
- Use DropdownMenu for site navigation.