# Navigation menu

Site or app navigation with panels that open under their trigger, in Candor's theme (Radix). Arrow keys move between the triggers and links, Down opens a trigger's panel and moves into it, Escape closes it and returns focus to the trigger. The current page's link is marked `aria-current`. A link a person can't follow stays, marked and explained.

- Registry name: `navigation-menu` (`@candor/navigation-menu`)
- Tier: Pro. Installing it needs a Pro, Premium or Team licence key: the person runs `npx candor-ui key <key>` themselves; never ask for the key in chat or write it to a file.
- Docs: https://candor.themethodic.com/docs/navigation-menu

## Install

```bash
npx candor-ui add navigation-menu
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/navigation-menu`.
It brings in: locked, motion, render-prop, utils.
npm packages: class-variance-authority@^0.7.1, lucide-react@^1.67.0, radix-ui@^1.8.1.

## Example

```tsx
"use client"

import {
  NavigationMenu,
  NavigationMenuContent,
  NavigationMenuItem,
  NavigationMenuLink,
  NavigationMenuList,
  NavigationMenuTrigger,
  navigationMenuTriggerStyle,
} from "@/components/ui/navigation-menu"

export default function Example() {
  return (
    <NavigationMenu aria-label="Workspace">
      <NavigationMenuList>
        <NavigationMenuItem>
          <NavigationMenuTrigger>Reports</NavigationMenuTrigger>
          <NavigationMenuContent>
            <ul className="grid w-72 gap-1">
              <li>
                <NavigationMenuLink href="#weekly" active>
                  <span className="font-medium">Weekly summary</span>
                  <span className="text-xs text-muted-foreground">Runs, failures and spend for the past 7 days.</span>
                </NavigationMenuLink>
              </li>
              <li>
                <NavigationMenuLink href="#audit">
                  <span className="font-medium">Audit trail</span>
                  <span className="text-xs text-muted-foreground">Who changed what, and when.</span>
                </NavigationMenuLink>
              </li>
              <li>
                <NavigationMenuLink href="#export" locked="Exports are off for your workspace.">
                  <span className="font-medium">Scheduled exports</span>
                </NavigationMenuLink>
              </li>
            </ul>
          </NavigationMenuContent>
        </NavigationMenuItem>
        <NavigationMenuItem>
          <NavigationMenuTrigger>Team</NavigationMenuTrigger>
          <NavigationMenuContent>
            <ul className="grid w-60 gap-1">
              <li>
                <NavigationMenuLink href="#members">Members</NavigationMenuLink>
              </li>
              <li>
                <NavigationMenuLink href="#roles">Roles</NavigationMenuLink>
              </li>
            </ul>
          </NavigationMenuContent>
        </NavigationMenuItem>
        <NavigationMenuItem>
          <NavigationMenuLink href="#docs" className={navigationMenuTriggerStyle()}>
            Docs
          </NavigationMenuLink>
        </NavigationMenuItem>
      </NavigationMenuList>
    </NavigationMenu>
  )
}
```

## <NavigationMenu>

Props:

- `viewport?: boolean | undefined` (default `true`): Shows every panel in one shared box under the list that resizes between them. Defaults to true; false anchors each panel to its own trigger.

Also takes the props of `React.ComponentProps<typeof NavigationMenuPrimitive.Root>`.

## <NavigationMenuContent>

No props.

Also takes the props of `React.ComponentProps<typeof NavigationMenuPrimitive.Content>`.

## <NavigationMenuIndicator>

No props.

Also takes the props of `React.ComponentProps<typeof NavigationMenuPrimitive.Indicator>`.

## <NavigationMenuItem>

No props.

Also takes the props of `React.ComponentProps<typeof NavigationMenuPrimitive.Item>`.

## <NavigationMenuLink>

Props:

- `locked?: string | undefined`: Why the link can't be followed right now. When set, it stays focusable and marked `aria-disabled`, following it does nothing, and the reason is shown on hover and focus and read to screen readers (see `Locked`).

Also takes the props of `React.ComponentProps<typeof NavigationMenuPrimitive.Link>`, `RenderProp`.

## <NavigationMenuList>

No props.

Also takes the props of `React.ComponentProps<typeof NavigationMenuPrimitive.List>`.

## <NavigationMenuTrigger>

No props.

Also takes the props of `React.ComponentProps<typeof NavigationMenuPrimitive.Trigger>`.

## <NavigationMenuViewport>

No props.

Also takes the props of `React.ComponentProps<typeof NavigationMenuPrimitive.Viewport>`.

## Motion

- **indicator** (overlay, fade)
- **open** (overlay, scale): With the shared viewport (the default), the viewport box fades in over the base duration and out over the quick one, scaling up from 0.95 from the top, under the triggers. A panel itself slides in from the side the person moved towards when they move between two triggers while one is open (3 rem, a fade where there is no side). With `viewport={false}` each panel is its own box and the first open is a plain fade, since there is no side to slide from; moving between triggers slides as above.
- **slide** (overlay, slide)
- **turn** (reveal, size)

Set with `--cd-motion-navigation-menu-<moment>-*` in your brand file; the families are in the design reference.

## Rules for using Candor UI

- Components take plain typed data. Never pass model output as HTML or render it as a link; messages are typed blocks.
- A control that can't be used says why: wrap it in `<Locked reason="…">`, don't just disable it.
- Don't show a value you don't have: leave it out (it renders as —) rather than guess. An estimate is labelled as one.
- Nothing is pre-selected in a decision a person makes, and the agent is named from your app's data, never from model output.
- Your app's words go in `LocaleProvider` (fixed strings) and `TermsProvider` (nouns); entity kinds and agent roles in `EntityKindsProvider` and `AgentRolesProvider`.
