{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "header",
  "type": "registry:ui",
  "title": "Glass header",
  "description": "A sticky glass bar with a wordmark, a nav and your actions. The page blurs under it as it scrolls, and on a narrow bar the links fold behind a menu button instead of squeezing.",
  "registryDependencies": [
    "https://littmd.vercel.app/r/litt.json",
    "https://littmd.vercel.app/r/button.json",
    "https://littmd.vercel.app/r/icons.json",
    "https://littmd.vercel.app/r/menu.json"
  ],
  "files": [
    {
      "path": "registry/litt/ui/header.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n// @litt/header 2026.11.06. What changed: https://littmd.vercel.app/changelog.md\n\n/**\n * Header: the site header. Sticky glass, a wordmark, a nav and right-aligned actions.\n *\n * - Surface: litt-glass on a sticky bar of --litt-header-h, full-bleed so it sets rounded-none\n *   (.litt-glass carries the surface radius). It sticks at the safe-area inset, like the\n *   gallery shell. Glass is only honest with content passing under it, so the page that\n *   uses it must scroll beneath.\n * - Content sits in a max-w-litt-wide wrap with the page gutter. Order: wordmark, nav, actions.\n * - Nav links are small type in fg-subtle. Hover is fg on an fg-6% fill (instant tier,\n *   color and background-color only). The current page is raised (R3): the press material's\n *   block (--litt-block, --litt-block-lift) in fg, as a switch's block and a selected segment are, and\n *   aria-current=\"page\". It keeps that look under the pointer. There is no dot. In the menu the current page\n *   carries a check in the icon cell, like a picked option.\n * - Responsive by container, not viewport. The bar asks how wide it is (a CSS container\n *   query), so it behaves the same as a full-bleed header on a page and inside a frame,\n *   and it needs no JavaScript or hydration to pick a layout. Below navFrom the links are\n *   not squeezed or wrapped: they leave the bar and live behind a menu button that opens\n *   litt's Menu (responsive tier, owned by Menu). Actions stay in the bar.\n * - No router. Links take an href and a current flag. renderLink swaps the anchor for your\n *   router's link. The menu's rows are real links too (MenuItem href), unless you pass\n *   onNavigate: then choosing a row calls it, for client-side routing.\n * - Only one nav is ever visible, so there is always exactly one landmark.\n *\n * On a phone (foundations.md section 6)\n * - The nav stays behind the menu button, and the button opens the anchored dropdown (Menu's own\n *   phone behavior, which is only a list here). The bar is container-driven, so a bar narrower than\n *   navFrom shows the button on any screen, and a phone is always narrower than 640.\n * - The bar stays sticky at the safe-area inset and never hides on scroll: nothing here reads the\n *   scroll position, and nothing moves it.\n * - On coarse screens the links, the wordmark link and the menu button are --litt-control-md tall (44px),\n *   and a pressed link fills like a hovered one (pointer-coarse:active:, instant tier, color only),\n *   because a finger has no hover.\n * - Flat: no hairline under the bar; the current link is a grey bubble, the others --litt-fg-muted words.\n */\nimport type { ReactNode } from \"react\";\nimport { Button } from \"./button\";\nimport { CheckIcon, MenuIcon } from \"./icons\";\nimport { Menu, MenuItem } from \"./menu\";\n\n/** One nav link. */\nexport type HeaderLink = {\n  /** The link text. Keep it to one or two short words. */\n  label: string;\n  /** Where the link goes. Also the key, so give each link its own. */\n  href: string;\n  /** Marks the page you are on: sets aria-current=\"page\" and lights the link. Set it on at most one link. */\n  current?: boolean;\n};\n\n/** What renderLink receives to put on your link, so a router link looks exactly like the default. */\nexport type HeaderLinkProps = {\n  /** The header's link classes. Pass them to your link. */\n  className: string;\n  /** \"page\" on the current link and undefined on the rest. Pass it to your link. */\n  \"aria-current\"?: \"page\";\n  /** The label. Pass it as the link's children. */\n  children: ReactNode;\n};\n\nexport type HeaderProps = {\n  /** The brand: usually a link home that wraps the studio name or a logo. A logo needs an aria-label on the link. */\n  wordmark: ReactNode;\n  /** The nav links, in order, each `{ label, href, current? }`. Four to six short ones fit the bar. For more, group them behind a page. Leave it empty for a bar with only a wordmark and actions. */\n  links: HeaderLink[];\n  /** Right-aligned actions, for example one primary Button. They stay in the bar on a phone, so keep it to one or two. */\n  actions?: ReactNode;\n  /** The nav landmark's name. Give each header on a page its own. */\n  navLabel?: string;\n  /** Where the links appear in the bar, measured on the header's own width: sm is 640px, lg is 1024px. Below it they move behind the menu button. Use sm for up to four short links and one action. */\n  navFrom?: \"sm\" | \"lg\";\n  /** Draws each link. Receives the link and the props to pass on. Use it for a router link, for example `(link, props) => <Link to={link.href} {...props} />`. */\n  renderLink?: (link: HeaderLink, props: HeaderLinkProps) => ReactNode;\n  /** Pass your router's navigate to keep client-side routing in the menu. Without it, the menu's rows are plain links. */\n  onNavigate?: (link: HeaderLink) => void;\n  /** The menu button's accessible name. */\n  menuLabel?: string;\n  /** Extra classes for the header element, such as a z-index. */\n  className?: string;\n};\n\n/* Container queries must be written out in full for Tailwind to find them. */\nconst FROM = {\n  sm: { nav: \"hidden @min-[40rem]:block\", menu: \"@min-[40rem]:hidden\" },\n  lg: { nav: \"hidden @min-[64rem]:block\", menu: \"@min-[64rem]:hidden\" },\n} as const;\n\nconst link =\n  \"relative flex h-litt-control-sm items-center rounded-litt-control px-litt-control-pad-sm text-litt-small in-[[data-material=flat]]:text-litt-fg-muted whitespace-nowrap select-none \" +\n  \"[-webkit-tap-highlight-color:transparent] transition-[background-color,color] duration-(--litt-dur-instant) ease-litt \" +\n  \"pointer-coarse:h-litt-control-md focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-litt-focus\";\n/* The current page is raised (R3); the others fill on hover, and on a press on touch. */\nconst current = \"bg-litt-block text-litt-fg shadow-(--litt-block-lift) forced-colors:border\";\nconst other =\n  \"text-litt-fg-subtle hover:bg-[color-mix(in_oklab,var(--litt-fg)_6%,transparent)] in-[[data-material=flat]]:hover:bg-litt-surface-3 hover:text-litt-fg \" +\n  \"pointer-coarse:active:bg-[color-mix(in_oklab,var(--litt-fg)_6%,transparent)] pointer-coarse:active:text-litt-fg\";\n\n/** The sticky glass header. It sits at the top of the page, with content that scrolls under it. */\nexport function Header({\n  wordmark,\n  links,\n  actions,\n  navLabel = \"Main\",\n  navFrom = \"lg\",\n  renderLink,\n  onNavigate,\n  menuLabel = \"Menu\",\n  className = \"\",\n}: HeaderProps) {\n  const from = FROM[navFrom];\n  const draw = renderLink ?? ((l: HeaderLink, props: HeaderLinkProps) => <a href={l.href} {...props} />);\n  return (\n    /* Full-bleed, so no corners: .litt-glass carries the surface radius. */\n    <header className={\"litt-glass sticky top-[env(safe-area-inset-top,0px)] z-30 rounded-none in-[[data-material=flat]]:before:shadow-none \" + className}>\n      {/* The bar is the container, so the layout follows the width it is given. */}\n      <div className=\"@container\">\n        <div className=\"mx-auto flex h-(--litt-header-h) max-w-litt-wide items-center gap-4 px-litt-gutter @min-[40rem]:gap-6\">\n          <div className=\"flex shrink-0 items-center text-litt-body font-litt-title text-litt-fg [&_a]:rounded-litt-control [&_a:focus-visible]:outline-2 [&_a:focus-visible]:outline-offset-4 [&_a:focus-visible]:outline-litt-focus pointer-coarse:[&_a]:inline-flex pointer-coarse:[&_a]:h-litt-control-md pointer-coarse:[&_a]:items-center\">\n            {wordmark}\n          </div>\n\n          {links.length > 0 && (\n            <nav aria-label={navLabel} className={\"ml-4 \" + from.nav}>\n              <ul className=\"flex items-center gap-1\">\n                {links.map((l) => (\n                  <li key={l.href}>\n                    {draw(l, {\n                      className: link + \" \" + (l.current ? current : other),\n                      \"aria-current\": l.current ? \"page\" : undefined,\n                      children: <span className=\"litt-trim\">{l.label}</span>,\n                    })}\n                  </li>\n                ))}\n              </ul>\n            </nav>\n          )}\n\n          <div className=\"ml-auto flex items-center gap-3\">\n            {actions}\n            {links.length > 0 && (\n              <nav aria-label={navLabel} className={from.menu}>\n                <Menu\n                  label={navLabel}\n                  align=\"end\"\n                  /* The 44px button sits 10px above the bar's bottom edge. 8 more clears the bar, so the menu\n                     floats over the page and never over its own glass. */\n                  sideOffset={18}\n                  trigger={\n                    <Button iconOnly variant=\"ghost\" aria-label={menuLabel}>\n                      <MenuIcon />\n                    </Button>\n                  }\n                >\n                  {links.map((l) => (\n                    <MenuItem\n                      key={l.href}\n                      icon={l.current ? <CheckIcon /> : <span />}\n                      /* A real link by default; with onNavigate, the router does the navigating. */\n                      {...(onNavigate ? { onSelect: () => onNavigate(l) } : { href: l.href })}\n                    >\n                      {l.label}\n                      {l.current && <span className=\"sr-only\">, current page</span>}\n                    </MenuItem>\n                  ))}\n                </Menu>\n              </nav>\n            )}\n          </div>\n        </div>\n      </div>\n    </header>\n  );\n}\n",
      "target": "components/litt/header.tsx"
    }
  ],
  "docs": "Import Header from components/litt/header. Pass a wordmark, links with a current flag and your actions. It needs no router and no provider; pass renderLink and onNavigate to use yours.",
  "meta": {
    "version": "2026.11.06",
    "released": "2026-11-06",
    "changelog": "https://littmd.vercel.app/changelog.md"
  }
}
