caliper uidocs

Navigation menu

Free

A site header whose items open frosted panels of links. Hover waits for intent, the panel resizes from one item to the next, and every link is reachable by keyboard.

Installs components/ui/navigation-menu.tsxDepends on @radix-ui/react-navigation-menu, lucide-reactMotion shared motion tokens
pnpm dlx shadcn@latest add https://caliperui.site/r/navigation-menu.json
Halden

When to use #

Use it for

  • Marketing and docs sites with product and resource sections.
  • Headers with more destinations than fit as plain links.

Skip it when

  • Phones; fold the links into a drawer as the demo does.
  • App navigation; use the sidebar.

Installation #

terminal
pnpm dlx shadcn@latest add https://caliperui.site/r/navigation-menu.json

Adds its 3 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.

Usage #

example.tsx
import { Boxes, ChartLine, GitBranch, ShieldCheck, Workflow } from "lucide-react"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Drawer, DrawerContent, DrawerDescription, DrawerTitle, DrawerTrigger } from "@/components/ui/drawer"
import { NavigationMenu, NavigationMenuContent, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuPanel, NavigationMenuTrigger } from "@/components/ui/navigation-menu"

const product = [
  {
    title: "Platform",
    links: [
      { href: "#ledger", title: "Ledger", description: "Every entry, reconciled as it lands.", icon: <Boxes /> },
      { href: "#close", title: "Close", description: "A month-end checklist that runs itself.", icon: <Workflow /> },
      { href: "#reports", title: "Reports", description: "Board-ready numbers on the first day.", icon: <ChartLine /> },
    ],
  },
  {
    title: "Trust",
    links: [
      { href: "#security", title: "Security", description: "SOC 2 Type II, SSO and audit trails.", icon: <ShieldCheck /> },
      { href: "#changelog", title: "Changelog", description: "What shipped this week.", icon: <GitBranch /> },
    ],
  },
];

const resources = [
  {
    links: [
      { href: "#guides", title: "Guides", description: "Set up your first close in an afternoon." },
      { href: "#api", title: "API reference", description: "REST and webhooks with examples." },
      { href: "https://status.example.com", title: "System status", description: "Uptime for the last 90 days.", external: true },
    ],
  },
];

export function NavigationMenuDemo() {
  return (
    <div className="w-full">
      <header className="flex h-14 items-center justify-between gap-4 rounded-[14px] border border-border px-4">
        <span className="flex items-center gap-2 text-sm font-semibold">
          <span aria-hidden="true" className="grid size-7 place-items-center rounded-[8px] bg-foreground text-xs text-background">H</span>
          Halden
        </span>
        <NavigationMenu aria-label="Main" className="max-md:hidden">
          <NavigationMenuList>
            <NavigationMenuItem>
              <NavigationMenuTrigger>Product</NavigationMenuTrigger>
              <NavigationMenuContent>
                <NavigationMenuPanel
                  columns={product}
                  featured={
                    <>
                      <Badge variant="info">New</Badge>
                      <p className="text-sm font-medium">Two-way NetSuite sync</p>
                      <p className="text-xs leading-[18px] text-muted-foreground">Entries posted in either place appear in both within a minute.</p>
                      <a href="#netsuite" className="text-xs font-medium text-foreground underline underline-offset-2">Read the launch note</a>
                    </>
                  }
                />
              </NavigationMenuContent>
            </NavigationMenuItem>
            <NavigationMenuItem>
              <NavigationMenuTrigger>Resources</NavigationMenuTrigger>
              <NavigationMenuContent>
                <NavigationMenuPanel columns={resources} />
              </NavigationMenuContent>
            </NavigationMenuItem>
            <NavigationMenuItem>
              <NavigationMenuLink href="#customers">Customers</NavigationMenuLink>
            </NavigationMenuItem>
            <NavigationMenuItem>
              <NavigationMenuLink href="#pricing" active>Pricing</NavigationMenuLink>
            </NavigationMenuItem>
          </NavigationMenuList>
        </NavigationMenu>
        <div className="flex items-center gap-2">
          <Button size="sm" className="max-md:hidden">Start free</Button>
          <Drawer>
            <DrawerTrigger asChild>
              <Button size="sm" variant="secondary" className="md:hidden">Menu</Button>
            </DrawerTrigger>
            <DrawerContent>
              <DrawerTitle>Menu</DrawerTitle>
              <DrawerDescription className="sr-only">Site navigation</DrawerDescription>
              <nav aria-label="Main" className="grid gap-5 overflow-y-auto">
                {[...product, { title: "Resources", links: resources[0]!.links }].map((group) => (
                  <div key={group.title} className="grid gap-1">
                    <p className="text-xs font-medium uppercase tracking-wide text-muted-foreground">{group.title}</p>
                    {group.links.map((link) => (
                      <a key={link.href} href={link.href} className="rounded-[10px] px-2 py-2.5 text-sm hover:bg-muted">{link.title}</a>
                    ))}
                  </div>
                ))}
                <Button>Start free</Button>
              </nav>
            </DrawerContent>
          </Drawer>
        </div>
      </header>
      <div className="h-72" aria-hidden="true" />
    </div>
  );
}

API reference #

NavigationMenu

component

No props of its own; it accepts the native props of its root element.

NavigationMenuList

component

No props of its own; it accepts the native props of its root element.

NavigationMenuItem

component

No props of its own; it accepts the native props of its root element.

NavigationMenuTrigger

component

No props of its own; it accepts the native props of its root element.

NavigationMenuContent

component

No props of its own; it accepts the native props of its root element.

NavigationMenuCard

component
PropTypeDefault
href *string—
title *string—
descriptionstring—
iconReactNode—
externalbooleanfalse
activeboolean—
classNamestring—

NavigationMenuPanel

component
PropTypeDefault
columns *readonly { title?: string | undefined; links: readonly { href: string; title: string; description?: string | undefined; icon?: ReactNode; external?: boolean …—
featuredReactNode—
classNamestring—

Accessibility #

It is built on native elements and keeps aria-hidden and aria-current in step with state.

Keyboard and focus handling comes from Radix navigation-menu: arrow keys move through items, Enter and Space select, and Escape closes and returns focus.

Motion #

Transitions use the shared duration and easing tokens. Under prefers-reduced-motion those tokens collapse to instant and travel distances go to zero, so state changes still happen without movement.