Nav
Independent building blocks for navigation menus. Compose them inside any <nav> element.
Solid
default<NavMenu>
<NavMenuLink href="/" variant="solid">Inactive</NavMenuLink>
<NavMenuLink href="/docs" variant="solid" isActive>Active</NavMenuLink>
</NavMenu>Ghost
<NavMenu>
<NavMenuLink href="/" variant="ghost">Inactive</NavMenuLink>
<NavMenuLink href="/docs" variant="ghost" isActive>Active</NavMenuLink>
</NavMenu>import { NavMenu, NavMenuLink } from "@/ui";
<NavMenu>
<NavMenuLink href="/about">About</NavMenuLink>
<NavMenuLink href="/contact">Contact</NavMenuLink>
</NavMenu>src/ui/Nav.tsx
import { type ReactNode } from "react";
import { Link } from "./Link";
import { cn } from "@/utils";
// NavMenu
export interface NavMenuProps {
children: ReactNode;
className?: string;
elevated?: boolean;
spacing?: "sm" | "md" | "lg";
}
export function NavMenu({
children,
className,
elevated = false,
spacing = "md",
}: NavMenuProps) {
const gap =
spacing === "sm" ? "gap-1" : spacing === "lg" ? "gap-8" : "gap-4";
return (
<div
className={cn(
"hidden items-center md:flex",
gap,
elevated && "rounded-md bg-root-100 p-1",
className,
)}
>
{children}
</div>
);
}
// NavMenuLink
export interface NavMenuLinkProps {
href: string;
onClick?: () => void;
isActive?: boolean;
variant?: "solid" | "ghost" | "underline";
className?: string;
children: ReactNode;
}
export function NavMenuLink({
href,
onClick,
isActive = false,
variant = "solid",
className,
children,
}: NavMenuLinkProps) {
const isUnderline = variant === "underline";
const padding = isUnderline ? "py-1" : "px-3.5 py-1.5";
const activeStyles = (() => {
switch (variant) {
case "underline":
return "border-b border-root-contrast text-root-contrast";
case "ghost":
return "bg-root-ghost text-root-contrast";
default:
return "bg-root-200 text-root-contrast shadow-sm";
}
})();
const inactiveStyles = cn(
"text-root-contrast-subtle hover:text-root-contrast",
isUnderline && "border-b border-transparent",
);
return (
<Link
href={href}
onClick={onClick}
className={cn(
"text-sm font-medium transition-colors",
!isUnderline && "rounded-md",
padding,
isActive ? activeStyles : inactiveStyles,
className,
)}
>
{children}
</Link>
);
}