Components

Navigation Menu

A horizontal list of links and dropdowns for primary site navigation, built on Radix with full keyboard and pointer support.

When to use it

This is the marketing-site and public-page navigation. Inside a signed-in Comitor app the equivalent job belongs to Sidebar and AppLauncher from @comitor/ui/shell, which know about entitlements and the current workspace. Unlike most overlay components, this module ships without a 'use client' directive — it only declares layout, and the state lives inside the Radix package — so the parts and the navigationMenuTriggerStyle() helper can both be used directly from a Server Component.

Basic usage

Pair a NavigationMenuTrigger with NavigationMenuContent to reveal a dropdown panel of links. Hover or focus the trigger to open it.

Simple links

For top-level links without a dropdown, render a NavigationMenuLink styled with navigationMenuTriggerStyle() so it matches the trigger height and padding.

Inline content

Pass viewport={false} on the root to drop the shared floating viewport and render each panel inline, directly beneath its own item.

Indicator

Drop a NavigationMenuIndicator as the last child of the list and a small arrow slides along the bar to whichever trigger is open. It is state information, not decoration, so the package draws it with the control-edge token instead of the decorative border grey: the arrow then follows the reader's palette and darkens under data-contrast="high", where a hard-coded scale class would stay pinned to the default one.

Props

The parts exported from @comitor/ui. Every part forwards the props of the Radix primitive it wraps, plus the native props of the element it renders.

PropTypeDefaultDescription
NavigationMenuNavigationMenuPrimitive.RootThe root bar. Renders NavigationMenuViewport for you unless viewport is false.
NavigationMenuListNavigationMenuPrimitive.ListThe <ul> holding the items — and the indicator, when you use one.
NavigationMenuItemNavigationMenuPrimitive.ItemOne <li>: either a trigger plus content, or a bare link.
NavigationMenuTriggerNavigationMenuPrimitive.TriggerOpens its content on hover or keyboard. Appends a chevron that rotates while open.
NavigationMenuContentNavigationMenuPrimitive.ContentThe panel of links. Styles itself as a popover only when viewport is false.
NavigationMenuLinkNavigationMenuPrimitive.LinkA link inside a panel, or a top-level link when styled with navigationMenuTriggerStyle().
NavigationMenuIndicatorNavigationMenuPrimitive.IndicatorThe small arrow that points at the open trigger. Place it last inside the list.
NavigationMenuViewportNavigationMenuPrimitive.ViewportThe shared panel the content animates into. Rendered automatically by the root; only mount it yourself if you need it somewhere else in the tree.
navigationMenuTriggerStyle() => stringThe cva helper behind the trigger. Callable from a Server Component — the module carries no "use client".

NavigationMenu (root)

PropTypeDefaultDescription
viewportbooleantruePackage addition. When true, panels render into one shared viewport below the bar that resizes between items. Set it to false to let each item pop its own panel — the matching styles hang off group-data-[viewport=false].
valuestringControlled value of the currently open item.
defaultValuestringValue of the item open on first render, for uncontrolled usage.
onValueChange(value: string) => voidCalled when the open item changes.
delayDurationnumber200Milliseconds a pointer must rest on a trigger before its content opens.
skipDelayDurationnumber300Window after closing during which moving to another trigger opens it instantly.
orientation'horizontal' | 'vertical''horizontal'Layout axis, which also decides which arrow keys move between triggers.
dir'ltr' | 'rtl''ltr'Reading direction, affecting the arrow keys and the open animation.

NavigationMenuLink

PropTypeDefaultDescription
activebooleanfalseMarks the link as the current page: Radix stamps aria-current="page" and an empty data-active attribute on the anchor. Style the current link yourself — data-active carries no value, so the packaged data-[active=true] rules do not match it.
hrefstringDestination URL, forwarded to the underlying <a>.
onSelect(event: Event) => voidCalled when the link is selected; the menu closes afterwards.
asChildbooleanfalseMerges the link props onto its single child — the way to wrap a framework link such as next/link.
classNamestringAdditional Tailwind classes. Pass navigationMenuTriggerStyle() here for a top-level link.

Accessibility

  • Built on Radix Navigation Menu, which exposes the menu with the correct ARIA roles and manages aria-expanded on each trigger automatically.
  • Full keyboard support: Tab moves focus into and out of the bar, / move between triggers, opens a panel and moves into it, and Esc closes the active dropdown.
  • Triggers and links render a focus-visible:ring-2 ring-ring/50 outline with a background-coloured offset — the ring token is the brand gold, so the focus outline stays recognisable in both themes and both contrast palettes.
  • The indicator arrow is drawn with a role token rather than a scale class, so it tracks the reader's chosen palette; it is redundant with the trigger's own open state, which assistive tech reads from aria-expanded.
  • Set active on the NavigationMenuLink for the current page: Radix renders aria-current="page", which is what a screen reader announces. Add your own visible treatment alongside it — location must not be signalled to sighted users by nothing at all.