# Header 3

> Centered navigation with shared layout underline rail on hover.

A shadcn/ui block from the [@nusaiba](https://nusaiba.dev) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/nusaiba/header-3).

- Page, with a live preview: https://shadcn.exchange/r/nusaiba/header-3
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/nusaiba/header-3-light-7695b7d4f46a0978.png
- Registry item: https://nusaiba.dev/r/base-nova/header-3.json
- Categories: header
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://nusaiba.dev/r/base-nova/header-3.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `motion`
- Registry items: `@nusaiba/foundation`, `button`

## Files

### `registry/blocks/header/header-nav-data.ts`

```ts
export const headerBrand = {
	name: "Nusaiba",
	href: "/",
	logoSrc: "/images/logo-nusaiba-wordmark.png",
	logoSrcDark: "/images/logo-nusaiba-dark.png",
	logoWidth: 150,
	logoHeight: 32,
} as const;

export const headerNavLinks = [
	{ label: "Blocks", href: "/blocks" },
	{ label: "Motion", href: "/motion" },
	{ label: "Docs", href: "/docs" },
	{ label: "Pricing", href: "/pricing" },
] as const;

export const headerRegistryItems = [
	{
		title: "Hero sections",
		description: "Editorial splits, type reveals, motion entrances",
		href: "/blocks/hero",
	},
	{
		title: "Pricing blocks",
		description: "Toggle matrices and witness panels",
		href: "/blocks/pricing",
	},
	{
		title: "Footer family",
		description: "Ship logs, strips, and license folds",
		href: "/blocks/footer",
	},
	{
		title: "All categories",
		description: "Browse the open registry catalog",
		href: "/blocks",
	},
] as const;

export const headerResourceItems = [
	{
		title: "Motion patterns",
		description: "Triggers, tokens, and linked blocks",
		href: "/motion",
	},
	{
		title: "Install guide",
		description: "components.json and CLI workflow",
		href: "/docs",
	},
	{
		title: "Account",
		description: "Registry token and Pro access",
		href: "/account",
	},
] as const;

export const headerSearchPlaceholder = "Search blocks, motion, docs…";

export const headerCommandItems = [
	{ label: "Blocks catalog", group: "Navigate", href: "/blocks", hint: "Browse all categories" },
	{ label: "Motion patterns", group: "Navigate", href: "/motion", hint: "Triggers and spring tokens" },
	{ label: "Install guide", group: "Docs", href: "/docs", hint: "components.json workflow" },
	{ label: "Pricing", group: "Navigate", href: "/pricing", hint: "Plans and Pro access" },
	{ label: "Hero sections", group: "Registry", href: "/blocks/hero", hint: "Editorial motion entrances" },
	{ label: "Header blocks", group: "Registry", href: "/blocks/header", hint: "Navigation patterns" },
	{ label: "Account", group: "Account", href: "/account", hint: "Registry token and profile" },
] as const;

export const header11RegistryColumns = [
	{
		label: "Categories",
		items: [
			{
				title: "Hero",
				description: "Editorial splits and motion entrances",
				href: "/blocks/hero",
			},
			{
				title: "Pricing",
				description: "Toggle matrices and witness panels",
				href: "/blocks/pricing",
			},
			{
				title: "Footer",
				description: "Ship logs, strips, and license folds",
				href: "/blocks/footer",
			},
			{
				title: "Contact",
				description: "Forms, channels, and enterprise intake",
				href: "/blocks/contact",
			},
			{
				title: "Auth",
				description: "Sign-in shells and framed panels",
				href: "/blocks/auth",
			},
		],
	},
	{
		label: "Motion",
		items: [
			{
				title: "Scroll reveal",
				description: "whileInView stagger for marketing sections",
				href: "/motion",
			},
			{
				title: "CTA family",
				description: "Bands, marquees, and prompt handoffs",
				href: "/blocks/cta",
			},
			{
				title: "Features",
				description: "Bento grids, rails, and witness stacks",
				href: "/blocks/features",
			},
			{
				title: "FAQs",
				description: "Accordions, rails, and command pickers",
				href: "/blocks/faqs",
			},
			{
				title: "All blocks",
				description: "Browse the open registry catalog",
				href: "/blocks",
			},
		],
	},
] as const;

export const header11RegistryFeature = {
	badge: "Install flow",
	title: "How teams ship motion sections with @nusaiba",
	description: "Copy prompt, CLI add, files land in your repo — no subscription lock-in.",
	href: "/docs",
	footer: {
		title: "Browse blocks",
		description: "Explore every category live",
		href: "/blocks",
	},
} as const;

export const header11ResourceItems = [
	{
		title: "Documentation",
		description: "Install guide and registry setup",
		href: "/docs",
	},
	{
		title: "Motion index",
		description: "Triggers, tokens, and linked blocks",
		href: "/motion",
	},
	{
		title: "Copy prompt",
		description: "AI-ready handoff on every block",
		href: "/blocks",
	},
	{
		title: "Pricing",
		description: "Pro license and team plans",
		href: "/pricing",
	},
	{
		title: "Account",
		description: "Registry token and profile",
		href: "/account",
	},
	{
		title: "Admin",
		description: "Plans and site settings",
		href: "/admin",
	},
] as const;

export const header11OpenSource = {
	title: "Open registry",
	description: "Nusaiba is open source. Star the repo to follow releases.",
	cta: "See GitHub",
	ctaHint: "View the repository",
	href: "https://github.com",
} as const;
```

### `registry/blocks/header/header-primitives.tsx`

```tsx
"use client";

import type { ReactNode } from "react";
import { useState, useRef, useEffect } from "react";
import Link from "next/link";
import { ArrowRightIcon, MenuIcon, XIcon } from "lucide-react";
import { AnimatePresence, motion } from "motion/react";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { headerBrand, headerNavLinks } from "./header-nav-data";

/** Semantic hooks for globals.css @container preview rules (see src/app/globals.css). */
export const headerPreviewDesktopNavClass = "header-preview-desktop-nav hidden lg:flex";

export const headerPreviewMobileOnlyClass = "header-preview-mobile-only lg:hidden";

export const headerPreviewMobileDrawerClass =
	"header-preview-mobile-drawer overflow-hidden border-border border-t lg:hidden";

/** Search + desktop CTAs hidden in very narrow preview frames. */
export const headerPreviewCompactHideClass = "header-preview-compact-hide";

const logoIconPaths = [
	"M44.4287 7.16278C47.943 1.06871 56.7287 1.0687 60.243 7.16277L97.954 72.5568C101.468 78.6508 97.0755 86.2684 90.0469 86.2684H14.6248C7.59624 86.2684 3.20336 78.6509 6.71765 72.5568L44.4287 7.16278Z",
	"M63.711 7.86367C69.3114 3.61171 77.4091 7.0239 78.2869 14.0056L87.7066 88.9249C88.5844 95.9066 81.584 101.222 75.1058 98.4918L5.58994 69.1994C-0.888245 66.4697 -1.98553 57.7425 3.61483 53.4905L63.711 7.86367Z",
	"M81.2118 15.9985C88.0211 14.2546 94.1626 20.5446 92.2664 27.3205L71.9195 100.031C70.0233 106.807 61.5117 108.987 56.5985 103.955L3.87631 49.9581C-1.03687 44.9261 1.3333 36.4562 8.14261 34.7123L81.2118 15.9985Z",
	"M94.1901 30.2933C101.142 31.3305 104.365 39.5132 99.9921 45.0221L53.0652 104.137C48.6921 109.646 40.0023 108.349 37.4236 101.803L9.75232 31.558C7.17363 25.0119 12.64 18.1258 19.5918 19.163L94.1901 30.2933Z",
	"M100.613 48.5092C106.619 52.1651 106.419 60.9589 100.254 64.3379L34.0967 100.598C27.9315 103.977 20.4246 99.407 20.5842 92.372L22.2977 16.881C22.4573 9.84596 30.1638 5.62213 36.1693 9.27807L100.613 48.5092Z",
] as const;

const logoWordmarkPaths = [
	"M124 80.2811V30.1054H135.832V36.2938C137.678 33.785 139.944 31.8894 142.629 30.6072C145.314 29.2692 148.223 28.6002 151.356 28.6002C154.936 28.6002 158.265 29.4643 161.342 31.1926C164.474 32.9208 166.992 35.4296 168.894 38.7189C170.796 41.9525 171.747 45.9108 171.747 50.5938V80.2811H158.572V52.35C158.572 49.2279 157.649 46.4683 155.803 44.071C153.957 41.6737 151.328 40.4751 147.915 40.4751C145.118 40.4751 142.629 41.4507 140.447 43.402C138.265 45.2975 137.174 48.1966 137.174 52.0991V80.2811H124Z",
	"M209.754 81.7864C206.229 81.7864 202.901 80.9223 199.768 79.194C196.635 77.4657 194.118 74.9848 192.216 71.7513C190.314 68.462 189.363 64.4758 189.363 59.7927V30.1054H202.537V58.0366C202.537 61.1586 203.46 63.9183 205.306 66.3156C207.208 68.7128 209.838 69.9115 213.194 69.9115C215.991 69.9115 218.481 68.908 220.663 66.9009C222.844 64.8939 223.935 62.0227 223.935 58.2874V30.1054H237.11V80.2811H225.11V74.26C223.32 76.7131 221.082 78.5807 218.397 79.863C215.712 81.1453 212.831 81.7864 209.754 81.7864Z",
	"M272.348 81.7864C269.159 81.7864 265.886 81.2568 262.53 80.1975C259.173 79.0825 256.488 77.5215 254.474 75.5144L257.831 62.8033C259.453 65.1448 261.635 67.1518 264.376 68.8243C267.173 70.4411 270.054 71.2495 273.019 71.2495C275.257 71.2495 276.879 70.8035 277.886 69.9115C278.949 68.9637 279.48 67.8208 279.48 66.4828C279.48 64.9775 278.949 63.8904 277.886 63.2214C276.823 62.4966 275.76 61.9391 274.697 61.5489L267.732 58.9565C266.278 58.3432 264.572 57.5348 262.614 56.5313C260.656 55.472 258.949 53.9946 257.495 52.0991C256.096 50.1478 255.397 47.6112 255.397 44.4891C255.397 41.6458 256.096 39.0256 257.495 36.6283C258.893 34.231 260.907 32.3076 263.537 30.8581C266.222 29.3528 269.439 28.6002 273.187 28.6002C276.543 28.6002 279.732 29.1855 282.753 30.3563C285.83 31.4713 288.151 32.8651 289.718 34.5376L286.613 46.9143C284.823 44.4613 282.669 42.5657 280.152 41.2277C277.634 39.8339 275.201 39.1371 272.851 39.1371C270.893 39.1371 269.439 39.5831 268.488 40.4751C267.537 41.3113 267.061 42.3148 267.061 43.4856C267.061 44.3776 267.397 45.2139 268.068 45.9944C268.739 46.7749 269.83 47.4439 271.341 48.0014L277.886 50.5102C279.62 51.1235 281.522 51.9876 283.592 53.1026C285.662 54.1619 287.424 55.6672 288.879 57.6184C290.389 59.5697 291.144 62.2458 291.144 65.6466C291.144 70.4411 289.382 74.3437 285.858 77.3542C282.389 80.309 277.886 81.7864 272.348 81.7864Z",
	"M330.326 81.4519C325.739 81.4519 321.543 80.2811 317.739 77.9396C313.935 75.5423 310.886 72.3645 308.592 68.4062C306.355 64.3922 305.236 59.9878 305.236 55.1933C305.236 50.3987 306.355 46.0223 308.592 42.064C310.886 38.0499 313.935 34.8721 317.739 32.5306C321.543 30.1333 325.739 28.9347 330.326 28.9347C333.627 28.9347 336.676 29.52 339.473 30.6908C342.27 31.8616 344.647 33.5062 346.605 35.6248V30.1054H358.101V80.2811H346.605V74.7618C344.647 76.8246 342.27 78.4692 339.473 79.6957C336.676 80.8665 333.627 81.4519 330.326 81.4519ZM331.92 69.8279C334.606 69.8279 337.011 69.1589 339.137 67.8208C341.319 66.4828 343.053 64.7267 344.34 62.5524C345.626 60.3223 346.27 57.8693 346.27 55.1933C346.27 52.5172 345.626 50.0921 344.34 47.9178C343.053 45.6878 341.319 43.9037 339.137 42.5657C337.011 41.2277 334.634 40.5587 332.004 40.5587C329.319 40.5587 326.886 41.2277 324.704 42.5657C322.578 43.9037 320.872 45.6878 319.585 47.9178C318.298 50.0921 317.655 52.5172 317.655 55.1933C317.655 57.8693 318.298 60.3223 319.585 62.5524C320.872 64.7267 322.578 66.4828 324.704 67.8208C326.886 69.1589 329.291 69.8279 331.92 69.8279Z",
	"M377.881 80.2811V30.1054H391.055V80.2811H377.881ZM384.51 22.4118C382.44 22.4118 380.678 21.7149 379.224 20.3212C377.825 18.8716 377.126 17.1155 377.126 15.0527C377.126 12.9342 377.825 11.178 379.224 9.78426C380.678 8.3905 382.44 7.69361 384.51 7.69361C386.636 7.69361 388.398 8.3905 389.797 9.78426C391.195 11.178 391.894 12.9342 391.894 15.0527C391.894 17.1155 391.195 18.8716 389.797 20.3212C388.398 21.7149 386.636 22.4118 384.51 22.4118Z",
	"M438.463 81.4519C435.219 81.4519 432.198 80.8665 429.401 79.6957C426.66 78.525 424.282 76.9361 422.268 74.9291V80.2811H410.772V0H423.946V33.9522C425.904 32.3912 428.086 31.1647 430.492 30.2727C432.953 29.3807 435.61 28.9347 438.463 28.9347C443.163 28.9347 447.414 30.1333 451.218 32.5306C455.022 34.8721 458.043 38.022 460.281 41.9803C462.574 45.9386 463.721 50.343 463.721 55.1933C463.721 59.9878 462.574 64.3922 460.281 68.4062C458.043 72.3645 455.022 75.5423 451.218 77.9396C447.414 80.2811 443.163 81.4519 438.463 81.4519ZM436.869 69.8279C439.554 69.8279 441.96 69.1589 444.086 67.8208C446.267 66.4828 448.001 64.7267 449.288 62.5524C450.575 60.3223 451.218 57.8693 451.218 55.1933C451.218 52.5172 450.575 50.0921 449.288 47.9178C448.001 45.6878 446.267 43.9037 444.086 42.5657C441.96 41.2277 439.582 40.5587 436.953 40.5587C434.268 40.5587 431.834 41.2277 429.652 42.5657C427.527 43.9037 425.82 45.6878 424.534 47.9178C423.247 50.0921 422.604 52.5172 422.604 55.1933C422.604 57.8693 423.247 60.3223 424.534 62.5524C425.82 64.7267 427.527 66.4828 429.652 67.8208C431.834 69.1589 434.24 69.8279 436.869 69.8279Z",
	"M502.885 81.4519C498.298 81.4519 494.102 80.2811 490.298 77.9396C486.494 75.5423 483.445 72.3645 481.151 68.4062C478.914 64.3922 477.795 59.9878 477.795 55.1933C477.795 50.3987 478.914 46.0223 481.151 42.064C483.445 38.0499 486.494 34.8721 490.298 32.5306C494.102 30.1333 498.298 28.9347 502.885 28.9347C506.185 28.9347 509.234 29.52 512.031 30.6908C514.828 31.8616 517.206 33.5062 519.164 35.6248V30.1054H530.66V80.2811H519.164V74.7618C517.206 76.8246 514.828 78.4692 512.031 79.6957C509.234 80.8665 506.185 81.4519 502.885 81.4519ZM504.479 69.8279C507.164 69.8279 509.57 69.1589 511.696 67.8208C513.877 66.4828 515.612 64.7267 516.898 62.5524C518.185 60.3223 518.828 57.8693 518.828 55.1933C518.828 52.5172 518.185 50.0921 516.898 47.9178C515.612 45.6878 513.877 43.9037 511.696 42.5657C509.57 41.2277 507.192 40.5587 504.563 40.5587C501.878 40.5587 499.444 41.2277 497.263 42.5657C495.137 43.9037 493.431 45.6878 492.144 47.9178C490.857 50.0921 490.214 52.5172 490.214 55.1933C490.214 57.8693 490.857 60.3223 492.144 62.5524C493.431 64.7267 495.137 66.4828 497.263 67.8208C499.444 69.1589 501.85 69.8279 504.479 69.8279Z",
] as const;

export function HeaderLogo({
	className,
}: {
	className?: string;
}) {
	return (
		<svg
			aria-label={headerBrand.name}
			className={cn("h-7 w-auto md:h-8", className)}
			fill="none"
			viewBox="0 0 531 108"
			xmlns="http://www.w3.org/2000/svg"
		>
			{logoIconPaths.map((path) => (
				<path
					className="fill-black/[0.42] dark:fill-white/[0.42]"
					d={path}
					key={path}
				/>
			))}
			{logoWordmarkPaths.map((path) => (
				<path className="fill-[#020202] dark:fill-white" d={path} key={path} />
			))}
		</svg>
	);
}

export function HeaderBrand({
	className,
	logoClassName,
	onClick,
}: {
	className?: string;
	logoClassName?: string;
	onClick?: () => void;
}) {
	return (
		<Link
			className={cn(
				"relative inline-flex min-h-10 shrink-0 items-center before:absolute before:-inset-x-2 before:-inset-y-1 before:content-['']",
				className,
			)}
			href={headerBrand.href}
			onClick={onClick}
		>
			<HeaderLogo className={logoClassName} />
		</Link>
	);
}

export function HeaderSignInCta({
	className,
	primaryLabel = "Get started",
}: {
	className?: string;
	primaryLabel?: string;
}) {
	return (
		<div className={cn("flex items-center gap-1.5 sm:gap-2", className)}>
			<Button className="hidden sm:inline-flex" variant="ghost">
				Sign in
			</Button>
			<Button className="hidden sm:inline-flex">
				{primaryLabel}
				<ArrowRightIcon data-icon="inline-end" />
			</Button>
		</div>
	);
}

export function HeaderMobileToggle({
	open,
	onToggle,
}: {
	open: boolean;
	onToggle: () => void;
}) {
	return (
		<Button
			aria-expanded={open}
			aria-label={open ? "Close menu" : "Open menu"}
			className={cn(headerPreviewMobileOnlyClass)}
			onClick={onToggle}
			size="icon-sm"
			variant="ghost"
		>
			{open ? <XIcon /> : <MenuIcon />}
		</Button>
	);
}

export function HeaderMobileDrawer({
	open,
	onClose,
	children,
}: {
	open: boolean;
	onClose: () => void;
	children: ReactNode;
}) {
	return (
		<AnimatePresence>
			{open ? (
				<motion.div
					animate={{ opacity: 1, height: "auto" }}
					className={headerPreviewMobileDrawerClass}
					exit={{ opacity: 0, height: 0 }}
					initial={{ opacity: 0, height: 0 }}
					transition={{ duration: 0.22, ease: [0.22, 1, 0.36, 1] }}
				>
					<nav aria-label="Mobile" className="px-4 py-3" onClick={onClose}>
						{children}
					</nav>
				</motion.div>
			) : null}
		</AnimatePresence>
	);
}

export function HeaderMobileLinks({ onNavigate }: { onNavigate?: () => void }) {
	return (
		<div className="flex flex-col gap-1">
			{headerNavLinks.map((link) => (
				<Link
					className="rounded-lg px-3 py-2.5 font-medium text-base text-foreground/85 transition-colors hover:bg-muted/50"
					href={link.href}
					key={link.href}
					onClick={onNavigate}
				>
					{link.label}
				</Link>
			))}
		</div>
	);
}

export function HeaderUnderlineNav({
	active,
	className,
	layoutId = "header-nav-underline",
	onActiveChange,
}: {
	active: string;
	className?: string;
	layoutId?: string;
	onActiveChange: (href: string) => void;
}) {
	return (
		<nav aria-label="Main" className={cn(headerPreviewDesktopNavClass, "items-center gap-6", className)}>
			{headerNavLinks.map((link) => {
				const isActive = active === link.href;

				return (
					<Link
						className="relative py-1 font-medium text-muted-foreground text-sm transition-colors hover:text-foreground"
						href={link.href}
						key={link.href}
						onMouseEnter={() => onActiveChange(link.href)}
					>
						{link.label}
						{isActive ? (
							<motion.span
								className="absolute inset-x-0 -bottom-1 h-px bg-foreground"
								layoutId={layoutId}
								transition={{ type: "spring", duration: 0.35, bounce: 0 }}
							/>
						) : null}
					</Link>
				);
			})}
		</nav>
	);
}

export function useHeaderNavActive(initial: string = headerNavLinks[0].href) {
	return useState(initial);
}

export function useHeaderScrollMorph(threshold = 24) {
	const headerRef = useRef<HTMLElement>(null);
	const [scrolled, setScrolled] = useState(false);

	useEffect(() => {
		const header = headerRef.current;
		if (!header) return;

		let scrollRoot: HTMLElement | Window = window;
		let parent = header.parentElement;

		while (parent) {
			const { overflowY } = getComputedStyle(parent);
			if (overflowY === "auto" || overflowY === "scroll" || overflowY === "overlay") {
				scrollRoot = parent;
				break;
			}
			parent = parent.parentElement;
		}

		const read = () => {
			const offset =
				scrollRoot === window
					? window.scrollY
					: (scrollRoot as HTMLElement).scrollTop;
			setScrolled(offset > threshold);
		};

		scrollRoot.addEventListener("scroll", read, { passive: true });
		read();

		return () => scrollRoot.removeEventListener("scroll", read);
	}, [threshold]);

	return { headerRef, scrolled };
}
```

### `registry/blocks/header/3/header-section.tsx`

```tsx
"use client";

import { useState } from "react";
import { ArrowRightIcon } from "lucide-react";
import { ContentRail } from "@/components/layout-contract";
import { Button } from "@/components/ui/button";
import {
	HeaderBrand,
	HeaderMobileDrawer,
	HeaderMobileLinks,
	HeaderMobileToggle,
	HeaderUnderlineNav,
	useHeaderNavActive,
} from "../header-primitives";

export function HeaderUnderlineSection() {
	const [mobileOpen, setMobileOpen] = useState(false);
	const [active, setActive] = useHeaderNavActive("/blocks");

	return (
		<header className="sticky top-0 z-50 border-border border-b bg-background/90 backdrop-blur-md">
			<ContentRail className="flex h-14 items-center gap-4 px-4 sm:px-6">
				<HeaderBrand />

				<div className="flex flex-1 justify-center">
					<HeaderUnderlineNav
						active={active}
						layoutId="header-3-underline"
						onActiveChange={setActive}
					/>
				</div>

				<div className="flex items-center gap-1">
					<Button className="hidden sm:inline-flex" variant="ghost">
						Sign in
					</Button>
					<Button className="hidden sm:inline-flex">
						Get started
						<ArrowRightIcon data-icon="inline-end" />
					</Button>
					<HeaderMobileToggle
						onToggle={() => setMobileOpen((open) => !open)}
						open={mobileOpen}
					/>
				</div>
			</ContentRail>

			<HeaderMobileDrawer onClose={() => setMobileOpen(false)} open={mobileOpen}>
				<HeaderMobileLinks onNavigate={() => setMobileOpen(false)} />
				<div className="mt-3 flex flex-col gap-2 border-border border-t pt-3">
					<Button className="w-full" variant="ghost">
						Sign in
					</Button>
					<Button className="w-full">
						Get started
						<ArrowRightIcon data-icon="inline-end" />
					</Button>
				</div>
			</HeaderMobileDrawer>
		</header>
	);
}
```
