For the complete documentation index, see llms.txt. Markdown variants are available by appending .md to documentation URLs. Homepage markdown is available at /llms.md/content.md. An agent skill is available at /.well-known/agent-skills/site-skill.md.

Navigation Menu

The Navigation Menu component from this design system, built on Base UI.

Installation

$ pnpm dlx shadcn@latest add @cobalt/navigation-menu

Source

import { NavigationMenu as NavigationMenuPrimitive } from "@base-ui/react/navigation-menu";
import { RiArrowDownSLine } from "@remixicon/react";
import { cva } from "class-variance-authority";
import { cn } from "cn";

function NavigationMenu({
  align = "start",
  className,
  children,
  popupClassName,
  ...props
}: NavigationMenuPrimitive.Root.Props &
  Pick<NavigationMenuPrimitive.Positioner.Props, "align"> & {
    popupClassName?: string;
  }) {
  return (
    <NavigationMenuPrimitive.Root
      data-slot="navigation-menu"
      className={cn(
        "group/navigation-menu relative flex max-w-full min-w-0 flex-1 items-center justify-center",
        className
      )}
      {...props}
    >
      {children}
      <NavigationMenuPositioner align={align} className={popupClassName} />
    </NavigationMenuPrimitive.Root>
  );
}

function NavigationMenuList({
  className,
  ...props
}: React.ComponentPropsWithRef<typeof NavigationMenuPrimitive.List>) {
  return (
    <NavigationMenuPrimitive.List
      data-slot="navigation-menu-list"
      className={cn(
        "group flex flex-1 list-none flex-wrap items-center justify-center gap-0",
        className
      )}
      {...props}
    />
  );
}

function NavigationMenuItem({
  className,
  ...props
}: React.ComponentPropsWithRef<typeof NavigationMenuPrimitive.Item>) {
  return (
    <NavigationMenuPrimitive.Item
      data-slot="navigation-menu-item"
      className={cn("relative", className)}
      {...props}
    />
  );
}

const navigationMenuTriggerStyle = cva(
  "group/navigation-menu-trigger inline-flex cursor-pointer items-center justify-center gap-2.5 rounded-[8px] px-2.5 py-2 text-sm leading-5 font-normal whitespace-nowrap text-foreground outline-none transition-colors duration-150 ease-out hover:bg-[var(--surface-hover,var(--muted))] focus-visible:ring-2 focus-visible:ring-ring data-popup-open:bg-[var(--surface-hover,var(--muted))] data-disabled:cursor-not-allowed data-disabled:opacity-50 motion-reduce:transition-none"
);

function NavigationMenuTrigger({
  className,
  children,
  ...props
}: NavigationMenuPrimitive.Trigger.Props) {
  return (
    <NavigationMenuPrimitive.Trigger
      data-slot="navigation-menu-trigger"
      className={cn(navigationMenuTriggerStyle(), "group", className)}
      {...props}
    >
      {children}{" "}
      <RiArrowDownSLine
        className="size-3 transition-transform duration-150 ease-out group-data-popup-open/navigation-menu-trigger:rotate-180 motion-reduce:transition-none"
        aria-hidden="true"
      />
    </NavigationMenuPrimitive.Trigger>
  );
}

function NavigationMenuContent({
  className,
  ...props
}: NavigationMenuPrimitive.Content.Props) {
  return (
    <NavigationMenuPrimitive.Content
      data-slot="navigation-menu-content"
      className={cn("h-full w-auto p-2.5 outline-none", className)}
      {...props}
    />
  );
}

function NavigationMenuPositioner({
  className,
  side = "bottom",
  sideOffset = 8,
  align = "start",
  alignOffset = 0,
  ...props
}: NavigationMenuPrimitive.Positioner.Props) {
  return (
    <NavigationMenuPrimitive.Portal>
      <NavigationMenuPrimitive.Positioner
        side={side}
        sideOffset={sideOffset}
        align={align}
        alignOffset={alignOffset}
        className={cn(
          "isolate z-50 h-(--positioner-height) w-(--positioner-width) max-w-(--available-width) transition-[top,left,right,bottom] duration-150 ease-out data-instant:transition-none motion-reduce:transition-none data-[side=bottom]:before:top-[-10px] data-[side=bottom]:before:right-0 data-[side=bottom]:before:left-0",
          className
        )}
        {...props}
      >
        <NavigationMenuPrimitive.Popup className="relative h-(--popup-height) w-(--popup-width) max-w-[calc(100vw-32px)] origin-(--transform-origin) overflow-hidden rounded-[16px] border border-[var(--border-subtle,var(--border))] bg-[var(--surface-raised,var(--popover))] text-popover-foreground shadow-[shadow:var(--shadow-dropdown,0_1px_1px_0_rgb(0_0_0/0.04),0_4px_4px_0_rgb(0_0_0/0.02))] outline-none transition-[opacity,scale,filter,width,height] duration-150 ease-out data-starting-style:scale-95 data-starting-style:opacity-0 data-starting-style:blur-[2px] data-ending-style:scale-95 data-ending-style:opacity-0 data-ending-style:blur-[2px] motion-reduce:transition-none motion-reduce:data-starting-style:scale-100 motion-reduce:data-starting-style:opacity-100 motion-reduce:data-starting-style:blur-none motion-reduce:data-ending-style:scale-100 motion-reduce:data-ending-style:opacity-100 motion-reduce:data-ending-style:blur-none">
          <NavigationMenuPrimitive.Viewport className="relative size-full overflow-hidden" />
        </NavigationMenuPrimitive.Popup>
      </NavigationMenuPrimitive.Positioner>
    </NavigationMenuPrimitive.Portal>
  );
}

function NavigationMenuLink({
  className,
  ...props
}: NavigationMenuPrimitive.Link.Props) {
  return (
    <NavigationMenuPrimitive.Link
      data-slot="navigation-menu-link"
      className={cn(
        "relative isolate flex items-center gap-2 rounded-[10px] p-2 text-start text-sm leading-5 text-foreground outline-none transition-colors before:absolute before:inset-0 before:-z-10 before:rounded-[inherit] hover:bg-[var(--surface-hover,var(--muted))] focus-visible:bg-[var(--surface-hover,var(--muted))] data-active:transition-none data-active:not-hover:not-focus-visible:before:bg-[var(--surface-hover,var(--muted))] motion-reduce:transition-none [&_svg:not([class*='size-'])]:size-4",
        className
      )}
      {...props}
    />
  );
}

function NavigationMenuIndicator({
  className,
  ...props
}: React.ComponentPropsWithRef<typeof NavigationMenuPrimitive.Icon>) {
  return (
    <NavigationMenuPrimitive.Icon
      data-slot="navigation-menu-indicator"
      className={cn(
        "top-full z-1 flex h-1.5 items-end justify-center overflow-hidden ",
        className
      )}
      {...props}
    >
      <div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-[4px] bg-border shadow-md" />
    </NavigationMenuPrimitive.Icon>
  );
}

export {
  NavigationMenu,
  NavigationMenuContent,
  NavigationMenuIndicator,
  NavigationMenuItem,
  NavigationMenuLink,
  NavigationMenuList,
  NavigationMenuTrigger,
  navigationMenuTriggerStyle,
  NavigationMenuPositioner,
};