Light

Navigation Menu

A collection of links for navigating websites.

Spec · from metadata

When to use

  • Top-level website navigation with dropdown panels
  • Mega-menu navigation with rich content (descriptions, icons, featured items)
  • Header navigation bars with grouped link categories
  • Any site-level navigation with expandable sections

When not to use

  • Application-level action menus (File/Edit/View) -- use Menubar instead
  • Single action menus from a button -- use DropdownMenu instead
  • Mobile navigation -- use Sheet or Drawer with a hamburger menu pattern
  • Sidebar navigation -- use a custom nav with links and collapsibles

Variants

PropValuesDefaultDescription
viewporttruefalsetrueWhen true, content renders inside a shared viewport container with animated height transitions. When false, content renders inline with its own animation.

Anti-patterns

Avoid// App menu bar using NavigationMenu
<NavigationMenu>
  <NavigationMenuList>
    <NavigationMenuItem>
      <NavigationMenuTrigger>File</NavigationMenuTrigger>
      <NavigationMenuContent>
        <NavigationMenuLink>New</NavigationMenuLink>
        <NavigationMenuLink>Save</NavigationMenuLink>
      </NavigationMenuContent>
    </NavigationMenuItem>
  </NavigationMenuList>
</NavigationMenu>
Prefer<Menubar>
  <MenubarMenu>
    <MenubarTrigger>File</MenubarTrigger>
    <MenubarContent>
      <MenubarItem>New</MenubarItem>
      <MenubarItem>Save</MenubarItem>
    </MenubarContent>
  </MenubarMenu>
</Menubar>

NavigationMenu is for site navigation with links. Application action menus (File/Edit/View) should use Menubar, which has menubar ARIA semantics and proper action-oriented keyboard behavior.

Avoid// Standalone link without trigger style
<NavigationMenuItem>
  <NavigationMenuLink href="/docs">
    Documentation
  </NavigationMenuLink>
</NavigationMenuItem>
Prefer// Standalone link with trigger style for visual consistency
<NavigationMenuItem>
  <NavigationMenuLink href="/docs" className={navigationMenuTriggerStyle()}>
    Documentation
  </NavigationMenuLink>
</NavigationMenuItem>

Standalone NavigationMenuLink items should use navigationMenuTriggerStyle() to visually match the trigger buttons in the menu bar.

Accessibility

  • Screen readerRoot has role='navigation'. Links are announced with their text. Triggers indicate expanded/collapsed state. Active links use data-active attribute.
  • ContrastTriggers use bg-background default, bg-accent on hover/focus/open. Content panels use bg-popover/text-popover-foreground. Links use muted-foreground for icons.

Token bindings

TokenCategoryUsage
bg-backgroundcolorTrigger default background
bg-accentcolorTrigger hover/focus/open background
text-accent-foregroundcolorTrigger hover/focus text
bg-popovercolorViewport/content background
text-popover-foregroundcolorContent text
text-muted-foregroundcolorLink icons
bg-bordercolorIndicator arrow
shadowshadowViewport and content elevation
rounded-mdradiusTrigger, viewport, and content border radius
rounded-smradiusLink border radius

Import

import {
  NavigationMenu,
  NavigationMenuList,
  NavigationMenuItem,
  NavigationMenuTrigger,
  NavigationMenuContent,
  NavigationMenuLink,
  navigationMenuTriggerStyle,
} from "@timelycare/helix-ui"

Props

interface NavigationMenuTriggerProps {
  children: React.ReactNode
  className?: string
}

interface NavigationMenuLinkProps {
  href: string
  children: React.ReactNode
  className?: string
}

Design Tokens

Root Container

PropertyTokenValue
Gapgap-14px between items
Layoutflex items-centerHorizontal row

Trigger/Link Button

PropertyTokenValue
Heighth-936px
Paddingpx-4 py-216px horizontal, 8px vertical
Border radiusrounded-md8px
Gap (trigger only)gap-14px (text to chevron)
Fonttext-sm font-medium14px, Adelle Sans Semibold
Text colortext-foreground#09090b

Content Popover

PropertyTokenValue
Backgroundbg-popoverwhite
Borderborder1px solid border
Border radiusrounded-md8px
Paddingp-28px
Shadowshadow-smSmall drop shadow
Min width320px (single), 512px (2-column)

Menu Link Item

PropertyTokenValue
Paddingp-28px
Border radiusrounded-sm6px
Gapgap-14px (title to description)
Titletext-sm font-medium text-popover-foreground14px, semibold, #09090b
Descriptiontext-sm text-muted-foreground14px, normal, #646565

Icons

Chevron Size: h-3 w-3 (12px) Icon in Simple List: h-4 w-4 (16px), text-muted-foreground

// Chevron rotates automatically on open via data-state
<ChevronDown className="h-3 w-3 transition-transform group-data-[state=open]:rotate-180" />

Content Layout Types

TypeMin WidthStructure
List320pxSingle column, title + description
2 Column List512pxgrid grid-cols-2, wrapping
Simple List320pxSingle column, title only
Simple List with Icon320pxIcon + title
Custom518pxHero (210px) + list column

States

StateTrigger/LinkMenu Link Item
Defaultbg-transparent text-foregroundtext-popover-foreground
Hoverbg-accent text-accent-foregroundbg-accent text-accent-foreground (title only)
Focusedbg-accent text-accent-foreground ring-3 ring-ring/50
Openbg-accent text-accent-foreground

Note: Description text stays text-muted-foreground on hover.


Common Patterns

Basic Navigation

<NavigationMenu>
  <NavigationMenuList>
    <NavigationMenuItem>
      <NavigationMenuTrigger>Getting Started</NavigationMenuTrigger>
      <NavigationMenuContent>
        <ul className="grid gap-2 p-2 w-[320px]">
          <ListItem href="/docs" title="Introduction">
            Re-usable components built with Radix UI.
          </ListItem>
        </ul>
      </NavigationMenuContent>
    </NavigationMenuItem>
    <NavigationMenuItem>
      <NavigationMenuLink href="/docs" className={navigationMenuTriggerStyle()}>
        Documentation
      </NavigationMenuLink>
    </NavigationMenuItem>
  </NavigationMenuList>
</NavigationMenu>

ListItem Helper

const ListItem = ({ title, children, href }) => (
  <li>
    <NavigationMenuLink asChild>
      <a href={href} className="block p-2 rounded-sm hover:bg-accent group">
        <div className="text-sm font-medium group-hover:text-accent-foreground">{title}</div>
        <p className="text-sm text-muted-foreground">{children}</p>
      </a>
    </NavigationMenuLink>
  </li>
)

Two Column Grid

<NavigationMenuContent>
  <ul className="grid grid-cols-2 gap-2 p-2 w-[512px]">
    <ListItem title="Alert Dialog">Modal dialogs.</ListItem>
    <ListItem title="Hover Card">Preview on hover.</ListItem>
  </ul>
</NavigationMenuContent>

With Featured Hero

<NavigationMenuContent>
  <div className="flex gap-2 p-2 w-[518px]">
    <div className="w-[210px] rounded-sm bg-gradient-to-b from-muted/50 to-muted p-6 flex flex-col justify-end">
      <h3 className="text-lg font-medium">shadcn/ui</h3>
      <p className="text-sm text-muted-foreground">Beautiful components.</p>
    </div>
    <ul className="flex-1 grid gap-2">
      <ListItem title="Introduction">Getting started.</ListItem>
    </ul>
  </div>
</NavigationMenuContent>

Accessibility

  • Built on Radix NavigationMenu — handles keyboard and focus automatically
  • Keyboard: Arrow keys to navigate triggers, Enter/Space to activate, Escape to close submenus
  • Screen reader: announces navigation role
  • Uses aria-current="page" for active links

Gotchas

ProblemSolution
Link not styled as triggerApply navigationMenuTriggerStyle()
Chevron not rotatingAdd group to trigger, use group-data-[state=open]:rotate-180
Content animating wrongUse NavigationMenuViewport for proper positioning
Hover state on descriptionOnly title gets text-accent-foreground, description stays muted

See Also


Last updated: February 9, 2026