Navigation Menu
Site-nav mega-dropdown — chip triggers, one shared sliding panel, grid links with color-carried hierarchy.
- Status
- Since
0.3.0- Accessibility pattern
- APG disclosure navigation
Last updated
Install
Add Navigation Menu from the VegaStack registry. The CLI verifies the item's integrity hash before writing it.
pnpm dlx shadcn@latest add @vegastack/navigation-menuThe same command installs the registry items it composes: @vegastack/floating-surface.
Usage
import {
NavigationMenu,
NavigationMenuList,
NavigationMenuItem,
NavigationMenuTrigger,
NavigationMenuContent,
NavigationMenuLink,
NavigationMenuGridLink,
NavigationMenuPanel,
} from "@/components/ui/navigation-menu";Compose triggers + contents inside the list, then render NavigationMenuPanel ONCE after it —
every item's content slides inside that single shared popup (Base UI viewport model).
Anatomy
- Triggers follow the ghost-chip grammar and stay lit while open (
data-popup-open). NavigationMenuGridLinkcarries hierarchy by color at one size — title ink, description muted — per the menu-typography rule.- The panel is the standard overlay surface: popover fill, one hairline,
--shadow-overlay, scale-0.97 + 1px nudge enter. - Keyboard/hover open behavior, focus management, and dismissal come from the Base UI primitive.
Examples
The example composes two disclosure triggers and one direct link. Both disclosure contents
render through the single shared NavigationMenuPanel.
Narrow viewports
Constrained to a phone-width column, the trigger row wraps and each panel becomes a single stacked list rather than a multi-column grid — nothing is hidden behind a breakpoint, and the popup still sizes itself to its content:
API Reference
Root and list
NavigationMenu adds no props of its own — it accepts everything the underlying element or Base UI primitive accepts (className, ref, ARIA attributes, event handlers).
Data attributes and CSS variables on NavigationMenu
| Attribute | Values |
|---|---|
data-slot | "navigation-menu" |
NavigationMenuList adds no props of its own — it accepts everything the underlying element or Base UI primitive accepts (className, ref, ARIA attributes, event handlers).
Data attributes and CSS variables on NavigationMenuList
| Attribute | Values |
|---|---|
data-slot | "navigation-menu-list" |
Items, triggers, and links
NavigationMenuItem adds no props of its own — it accepts everything the underlying element or Base UI primitive accepts (className, ref, ARIA attributes, event handlers).
Data attributes and CSS variables on NavigationMenuItem
| Attribute | Values |
|---|---|
data-slot | "navigation-menu-item" |
NavigationMenuTrigger adds no props of its own — it accepts everything the underlying element or Base UI primitive accepts (className, ref, ARIA attributes, event handlers).
Data attributes and CSS variables on NavigationMenuTrigger
| Attribute | Values |
|---|---|
data-slot | "navigation-menu-trigger" |
NavigationMenuLink adds no props of its own — it accepts everything the underlying element or Base UI primitive accepts (className, ref, ARIA attributes, event handlers).
Data attributes and CSS variables on NavigationMenuLink
| Attribute | Values |
|---|---|
data-slot | "navigation-menu-link" |
| Prop | Type | Default | Description |
|---|---|---|---|
title* | React.ReactNode | — | The entry name — ink voice. |
description | React.ReactNode | — | Muted one-line description under the title. |
icon | React.ReactNode | — | Leading icon (decorative). |
Data attributes and CSS variables on NavigationMenuGridLink
| Attribute | Values |
|---|---|
data-slot | "navigation-menu-grid-link" |
Content and shared panel
NavigationMenuContent adds no props of its own — it accepts everything the underlying element or Base UI primitive accepts (className, ref, ARIA attributes, event handlers).
Data attributes and CSS variables on NavigationMenuContent
| Attribute | Values |
|---|---|
data-slot | "navigation-menu-content" |
NavigationMenuPanel adds no props of its own — it accepts everything the underlying element or Base UI primitive accepts (className, ref, ARIA attributes, event handlers).
Accessibility
- Give the root a unique navigation label when more than one navigation landmark exists.
- Base UI owns roving focus, trigger expansion state, dismissal, and focus restoration.
- Grid-link icons are decorative; the visible title remains each link's accessible name.
- The shared portaled panel preserves a surrounding
MarketingSurfacetheme scope.
| Key | Action |
|---|---|
| Tab | Enter the navigation list and move to the next page control. |
| ← / → | Move between top-level items; direction mirrors in RTL. |
| Enter / Space | Open the focused trigger or follow a direct link. |
| Esc | Close the panel and return focus to its trigger. |
| Contract | States tested |
|---|---|
| Behaviour | default, open |
| Accessibility | native-or-base-ui-semantics, browser-accessibility-test |
| Visual | default, hover |