{
  "$schema": "https://abackui.com/schema/registry-item.json",
  "name": "site-header",
  "type": "registry:block",
  "title": "Site header",
  "description": "A sticky header with brand, primary navigation, optional search, theme toggle, actions, and a mobile sheet.",
  "dependencies": [
    "@tabler/icons-react"
  ],
  "registryDependencies": [
    "https://abackui.com/r/utils.json",
    "https://abackui.com/r/button.json",
    "https://abackui.com/r/kbd.json",
    "https://abackui.com/r/sheet.json",
    "https://abackui.com/r/theme-toggle.json"
  ],
  "categories": [
    "Marketing"
  ],
  "docs": "https://abackui.com/docs/blocks/site-header",
  "meta": {
    "status": "beta",
    "usedIn": [
      "aback-tools",
      "aback-launch",
      "aback-group"
    ]
  },
  "files": [
    {
      "path": "components/blocks/site-header.tsx",
      "content": "\"use client\";\n\nimport { useState, type ReactNode } from \"react\";\nimport { IconMenu2, IconSearch } from \"@tabler/icons-react\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { Kbd } from \"@/components/ui/kbd\";\nimport {\n  Sheet,\n  SheetContent,\n  SheetDescription,\n  SheetHeader,\n  SheetTitle,\n  SheetTrigger,\n} from \"@/components/ui/sheet\";\nimport { ThemeToggle } from \"@/components/ui/theme-toggle\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface SiteHeaderLink {\n  title: string;\n  href: string;\n}\n\nexport interface SiteHeaderBlockProps {\n  /** Your logo and name, usually a link home. */\n  brand: ReactNode;\n  links: SiteHeaderLink[];\n  /** The href of the current section. A link is active when the path starts with it. */\n  activePath?: string;\n  /** Primary actions on the right, such as a sign-in link or a CTA. */\n  actions?: ReactNode;\n  /** Renders a search trigger. Omit it and no search control appears. */\n  onSearch?: () => void;\n  /** Shortcut shown on the search trigger. Only show one you actually bind. */\n  searchShortcut?: string;\n  showThemeToggle?: boolean;\n}\n\nfunction isActive(activePath: string | undefined, href: string) {\n  if (!activePath) return false;\n  if (href === \"/\") return activePath === \"/\";\n  return activePath === href || activePath.startsWith(`${href.replace(/\\/$/, \"\")}/`);\n}\n\nfunction linkClass(active: boolean) {\n  return cn(\n    \"inline-flex h-9 items-center px-3 text-sm font-medium outline-none transition-colors duration-200\",\n    \"focus-visible:ring-2 focus-visible:ring-focus-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n    active\n      ? \"bg-brand-wash text-link\"\n      : \"text-muted-foreground hover:bg-muted hover:text-foreground\",\n  );\n}\n\n/**\n * A sticky site header: brand, primary navigation, optional search, theme\n * toggle, and actions. Below md the links move into a sheet. Links are plain\n * anchors; swap them for your router's link component.\n */\nexport function SiteHeaderBlock({\n  brand,\n  links,\n  activePath,\n  actions,\n  onSearch,\n  searchShortcut,\n  showThemeToggle = true,\n}: SiteHeaderBlockProps) {\n  const [open, setOpen] = useState(false);\n\n  return (\n    <header className=\"sticky top-0 z-40 w-full border-b border-separator bg-background/95 backdrop-blur-sm\">\n      <div className=\"mx-auto flex h-16 w-full max-w-6xl items-center gap-4 px-4 md:px-6\">\n        <div className=\"shrink-0\">{brand}</div>\n\n        <nav aria-label=\"Primary\" className=\"ml-2 hidden items-center gap-1 md:flex\">\n          {links.map((link) => {\n            const active = isActive(activePath, link.href);\n            return (\n              <a\n                key={link.href}\n                href={link.href}\n                aria-current={active ? \"page\" : undefined}\n                className={linkClass(active)}\n              >\n                {link.title}\n              </a>\n            );\n          })}\n        </nav>\n\n        <div className=\"ml-auto flex items-center gap-1\">\n          {onSearch ? (\n            <>\n              <button\n                type=\"button\"\n                onClick={onSearch}\n                className=\"hidden h-9 w-52 items-center gap-2 border border-border bg-muted/30 px-3 text-sm text-muted-foreground outline-none transition-colors duration-200 hover:border-primary-300 hover:bg-background hover:text-foreground focus-visible:ring-2 focus-visible:ring-focus-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background lg:flex\"\n              >\n                <IconSearch aria-hidden=\"true\" className=\"size-4\" />\n                <span className=\"flex-1 text-left\">Search</span>\n                {searchShortcut ? <Kbd>{searchShortcut}</Kbd> : null}\n              </button>\n              <Button\n                variant=\"ghost\"\n                size=\"icon\"\n                aria-label=\"Search\"\n                onClick={onSearch}\n                className=\"lg:hidden\"\n              >\n                <IconSearch aria-hidden=\"true\" />\n              </Button>\n            </>\n          ) : null}\n\n          {showThemeToggle ? <ThemeToggle /> : null}\n\n          {actions ? (\n            <>\n              <span aria-hidden=\"true\" className=\"mx-2 hidden h-5 w-px bg-border md:block\" />\n              <div className=\"hidden items-center gap-2 md:flex\">{actions}</div>\n            </>\n          ) : null}\n\n          <Sheet open={open} onOpenChange={setOpen}>\n            <SheetTrigger asChild>\n              <Button variant=\"ghost\" size=\"icon\" aria-label=\"Open menu\" className=\"md:hidden\">\n                <IconMenu2 aria-hidden=\"true\" />\n              </Button>\n            </SheetTrigger>\n            <SheetContent side=\"right\" className=\"w-72 gap-0 p-0\">\n              <SheetHeader className=\"h-16 justify-center border-b border-separator px-4\">\n                <SheetTitle>Menu</SheetTitle>\n                <SheetDescription className=\"sr-only\">Site navigation</SheetDescription>\n              </SheetHeader>\n              <nav aria-label=\"Mobile\" className=\"flex flex-col gap-1 p-3\">\n                {links.map((link) => {\n                  const active = isActive(activePath, link.href);\n                  return (\n                    <a\n                      key={link.href}\n                      href={link.href}\n                      onClick={() => setOpen(false)}\n                      aria-current={active ? \"page\" : undefined}\n                      className={cn(linkClass(active), \"h-10 w-full\")}\n                    >\n                      {link.title}\n                    </a>\n                  );\n                })}\n              </nav>\n              {actions ? (\n                <div className=\"mt-auto flex flex-col gap-2 border-t border-separator p-4 [&>*]:w-full\">\n                  {actions}\n                </div>\n              ) : null}\n            </SheetContent>\n          </Sheet>\n        </div>\n      </div>\n    </header>\n  );\n}\n",
      "type": "registry:block",
      "target": "components/blocks/site-header.tsx"
    }
  ]
}
