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└── NavigationMenuViewportThe 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.
npx shadcn@latest add http://127.0.0.1:3000/r/refined/navigation-menu.jsonInstalled dependencies
Installed with this primitive because the slot depends on them; they are support files, not standalone sidebar items.
Surface variants
src/registry/sources/refined/surface-variants.tsSupportRaw 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> );}