Refined UI

An opinionated, customizable superset of shadcn/ui

AgentsPrimitivesSkillsSkins
Guides
  • Overview
  • Get started
  • Shadcn compatibility
  • Architecture
  • Agent surface
Primitives
  • Accordion
  • Alert
  • Alert dialog
  • Aspect ratio
  • Autocomplete
  • Avatar
  • Badge
  • Button
  • Button group
  • Calendar
  • Card
  • Checkbox
  • Checkbox Group
  • Code
  • Code Diff
  • Collapsible
  • Color Picker
  • Combobox
  • Command
  • Date Picker
  • Dialog
  • Drawer
  • Empty
  • Field
  • Form
  • Gradient Editor
  • Hover card
  • Input
  • Input group
  • Input OTP
  • Item
  • Kbd
  • Markdown
  • Menu
  • Menubar
  • Meter
  • Native select
  • Navigation menu
  • Number Field
  • Pagination
  • Popover
  • Progress
  • Radio group
  • Resizable
  • Scroll area
  • Select
  • Sidebar
  • Skeleton
  • Slider
  • Spinner
  • Switch
  • Table
  • Table of contents
  • Tabs
  • Textarea
  • Toast
  • Toggle
  • Toolbar
  • Tooltip
  • Tree
  • Trigger Menu
  • Typography
Hooks
  • Use Chat Message
  • Use Chat Input
  • Use User Ask
  • Use Audio Recorder
  • Use Environment Variables
  • Use Copy To Clipboard
  • Use Tool Call
  • Use Sidebar Resize
Utils
  • cn
  • Skin
  • Contracts
  • Serialize
Extensions
  • Control Effects
  • View Transition
  • Send Aurora
  1. Primitives
  2. Navigation menu
Primitives

Navigation menu

Site navigation menu with a shared animated viewport.

Composition

The preferred shape for composing the installed primitive from its exported parts.

Parts

Exported compound parts from the installed source.

NavigationMenu
├── NavigationMenuList
├── NavigationMenuItem
├── NavigationMenuTrigger
├── NavigationMenuContent
├── NavigationMenuLink
└── NavigationMenuViewport

The refined source installs this primitive from src/registry/sources/refined/ui/navigation-menu.tsx. Install it on its own with the command above, or inspect the source below.

Registry command
npx shadcn@latest add http://127.0.0.1:3000/r/refined/navigation-menu.json
See registry manifest

Installed dependencies

Installed with this primitive because the slot depends on them; they are support files, not standalone sidebar items.

Surface variantssrc/registry/sources/refined/surface-variants.tsSupport

Raw code

Primary installed primitive source

Base UI Navigation Menu slot
src/registry/sources/refined/ui/navigation-menu.tsx
"use client";
​
import { NavigationMenu as NavigationMenuPrimitive } from "@base-ui/react/navigation-menu";
import type { ComponentProps } from "react";
import type {
NavigationMenuContentProps,
NavigationMenuItemProps,
NavigationMenuLinkProps,
NavigationMenuListProps,
NavigationMenuProps,
NavigationMenuTriggerProps,
NavigationMenuViewportProps,
} from "@/components/refined-ui/contracts";
import { controlSize } from "@/components/refined-ui/control-variants";
import { cn } from "@/components/refined-ui/lib/cn";
import { skinEffects, skinId, skinSlot } from "@/components/refined-ui/skin";
import { floatingSurfaceClasses } from "@/components/refined-ui/surface-variants";
​
// Refined skin slot, 100% Base UI: SHARED floating surface — every item has Trigger+Content, but one Portal→Positioner→Popup→Viewport MORPHS between active item's content (width/height + activation-direction).
// Triggers share quiet Button look + --radius-control; Viewport is popover surface (--radius-popover, ring-border, bg-popover, shadow-pop) matching Menu/Select.
// Motion token-driven (--duration-base/--ease-emphasized) — Reduce-motion kill-switch flattens morph too.
​
type RefinedLinkProps = NavigationMenuLinkProps & Pick<ComponentProps<typeof NavigationMenuPrimitive.Link>, "render">;
​
export function NavigationMenu({ className, children, ...props }: NavigationMenuProps) {
return (
<NavigationMenuPrimitive.Root
data-ui="agent"
data-slot="navigation-menu"
className={cn("relative flex max-w-max flex-1 items-center justify-center", skinSlot("navigation-menu", {}), className)}
{...props}
>
{children}
</NavigationMenuPrimitive.Root>
);
}
​
export function NavigationMenuList({ className, ...props }: NavigationMenuListProps) {
return (
<NavigationMenuPrimitive.List
data-ui="agent"
data-slot="navigation-menu-list"
className={cn("flex flex-1 list-none items-center justify-center gap-0.5", skinSlot("navigation-menu-list", {}), className)}
{...props}
/>
);
}
​
export function NavigationMenuItem({ className, ...props }: NavigationMenuItemProps) {
return (
<NavigationMenuPrimitive.Item
data-ui="agent"
data-slot="navigation-menu-item"
className={cn("relative", skinSlot("navigation-menu-item", {}), className)}
{...props}
/>
);
}
​
export function NavigationMenuTrigger({ className, children, ...props }: NavigationMenuTriggerProps) {
return (
<NavigationMenuPrimitive.Trigger
data-ui="agent"
data-slot="navigation-menu-trigger"
data-control="true"
data-size="sm"
className={cn(
"inline-flex cursor-default select-none items-center rounded-[var(--radius-control)] font-medium text-muted-foreground outline-none transition hover:bg-foreground/6 hover:text-foreground focus-visible:ring-2 focus-visible:ring-foreground/20 data-[popup-open]:bg-foreground/7 data-[popup-open]:text-foreground",
controlSize({ size: "sm" }),
skinSlot("navigation-menu-trigger", {}),
className,
)}
{...props}
>
{children}
<NavigationMenuPrimitive.Icon className="text-muted-foreground transition-transform duration-[var(--duration-base)] ease-[var(--ease-emphasized)] data-[popup-open]:rotate-180">
<svg viewBox="0 0 12 12" className="size-3" aria-hidden="true" fill="none">
<path d="M3 4.5 6 7.5 9 4.5" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</NavigationMenuPrimitive.Icon>
</NavigationMenuPrimitive.Trigger>
);
}
​
export function NavigationMenuContent({ className, children, ...props }: NavigationMenuContentProps) {
return (
<NavigationMenuPrimitive.Content
data-ui="agent"
data-slot="navigation-menu-content"
className={cn(
// Panel moves into shared Viewport when active; fades/slides per travel direction (data-activation-direction).
"h-full w-full p-[var(--popover-padding)] transition-[opacity,translate] duration-[var(--duration-base)] ease-[var(--ease-emphasized)] data-[starting-style]:opacity-0 data-[ending-style]:opacity-0 data-[starting-style]:data-[activation-direction=left]:-translate-x-3 data-[starting-style]:data-[activation-direction=right]:translate-x-3 data-[ending-style]:data-[activation-direction=left]:translate-x-3 data-[ending-style]:data-[activation-direction=right]:-translate-x-3",
skinSlot("navigation-menu-content", {}),
className,
)}
{...props}
>
{children}
</NavigationMenuPrimitive.Content>
);
}
​
export function NavigationMenuLink({ className, active, children, ...props }: RefinedLinkProps) {
return (
<NavigationMenuPrimitive.Link
data-ui="agent"
data-slot="navigation-menu-link"
active={active}
className={cn(
"block select-none rounded-[var(--radius-md)] px-3 py-2 text-body text-foreground no-underline outline-none transition hover:bg-foreground/6 focus-visible:ring-2 focus-visible:ring-foreground/20 data-[active]:bg-foreground/6 data-[active]:text-foreground",
skinSlot("navigation-menu-link", { active: Boolean(active) }),
className,
)}
{...props}
>
{children}
</NavigationMenuPrimitive.Link>
);
}
​
export function NavigationMenuViewport({ className, ...props }: NavigationMenuViewportProps) {
return (
<NavigationMenuPrimitive.Portal>
{/* Portal escapes token-scoped ancestor — positioner re-asserts ACTIVE skin's scope.
before:* strip bridges sideOffset gap so hover survives trigger→popup. */}
<NavigationMenuPrimitive.Positioner
data-skin={skinId()}
data-effects={skinEffects()}
sideOffset={8}
collisionPadding={{ top: 5, bottom: 5, left: 16, right: 16 }}
collisionAvoidance={{ side: "none" }}
className="z-[80] h-[var(--positioner-height)] w-[var(--positioner-width)] max-w-[var(--available-width)] outline-none transition-[top,left,right,bottom] duration-[var(--duration-base)] ease-[var(--ease-emphasized)] before:absolute before:inset-x-0 before:top-[-8px] before:h-2 before:content-['']"
>
<NavigationMenuPrimitive.Popup className="relative h-[var(--popup-height)] w-[var(--popup-width)] origin-[var(--transform-origin)] outline-none transition-[opacity,transform,width,height,scale] duration-[var(--duration-base)] ease-[var(--ease-emphasized)] data-[starting-style]:scale-95 data-[starting-style]:opacity-0 data-[ending-style]:scale-95 data-[ending-style]:opacity-0">
<NavigationMenuPrimitive.Viewport
data-ui="agent"
data-slot="navigation-menu-viewport"
className={cn(
"relative h-full w-full overflow-hidden",
floatingSurfaceClasses,
skinSlot("navigation-menu-viewport", {}),
className,
)}
{...props}
/>
</NavigationMenuPrimitive.Popup>
</NavigationMenuPrimitive.Positioner>
</NavigationMenuPrimitive.Portal>
);
}
​

On this page

  • Preview
  • Composition
  • Installation
  • Dependencies
  • Source
  • Changelog