{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "navbar",
  "title": "Navbar",
  "description": "Sticky top navbar with brand mark, center nav using NavigationMenu (supports flat links + rich icon/description dropdowns), CTA buttons, and a scroll-aware backdrop blur. Mobile menu uses Accordion. Position prop ('fixed' | 'absolute' | 'sticky' | 'static') so you can drop it on a real page or showcase inside a card.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "accordion",
    "button",
    "navigation-menu"
  ],
  "files": [
    {
      "path": "registry/new-york/blocks/navbar/navbar.tsx",
      "content": "\"use client\";\n\nimport { Menu, X } from \"lucide-react\";\nimport Link from \"next/link\";\nimport * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport {\n  Accordion,\n  AccordionContent,\n  AccordionItem,\n  AccordionTrigger,\n} from \"@/registry/new-york/ui/accordion\";\nimport { Button } from \"@/registry/new-york/ui/button\";\nimport {\n  NavigationMenu,\n  NavigationMenuContent,\n  NavigationMenuItem,\n  NavigationMenuLink,\n  NavigationMenuList,\n  NavigationMenuTrigger,\n  navigationMenuTriggerStyle,\n} from \"@/registry/new-york/ui/navigation-menu\";\n\n/**\n * A single nav link. If `items` is provided, the link renders as a dropdown\n * trigger; otherwise it's a flat top-level link.\n */\nexport type NavbarLink = {\n  label: string;\n  href: string;\n  items?: NavbarDropdownItem[];\n};\n\n/**\n * A row inside a dropdown panel. `icon` is optional — when present, the\n * item renders as a rich card (icon + title + description).\n */\nexport type NavbarDropdownItem = {\n  label: string;\n  href: string;\n  description?: string;\n  icon?: React.ReactNode;\n};\n\nexport type NavbarCta = {\n  label: string;\n  href: string;\n  variant?: \"default\" | \"outline\" | \"secondary\" | \"ghost\";\n};\n\nexport type NavbarProps = {\n  /** Brand mark — pass any node (logo SVG, wordmark, etc.) */\n  brand: React.ReactNode;\n  /** Brand link target (typically \"/\") */\n  brandHref?: string;\n  /** Accessible name for the brand link */\n  brandLabel?: string;\n  /** Center nav links */\n  links: NavbarLink[];\n  /** Right-side CTA buttons (rendered in order) */\n  ctas?: NavbarCta[];\n  /** Optional element rendered after the CTAs (e.g. a theme toggle) */\n  trailing?: React.ReactNode;\n  /** Tailwind classes for the outer header */\n  className?: string;\n  /** Pixel offset from top — useful when a banner sits above the navbar */\n  topOffset?: number;\n  /** Max width of the centered container */\n  maxWidthClassName?: string;\n  /**\n   * Positioning strategy:\n   * - \"fixed\" (default) — locks to the viewport top (standard page navbar).\n   * - \"absolute\" — locks to the nearest positioned ancestor. Useful for\n   *   showcasing the navbar inside a card on the page.\n   * - \"sticky\" — stays in-flow until it hits the top.\n   * - \"static\" — fully in-flow.\n   */\n  position?: \"fixed\" | \"absolute\" | \"sticky\" | \"static\";\n};\n\n/**\n * Sticky top navbar with scroll-aware backdrop blur, centered nav with\n * NavigationMenu dropdowns, CTA buttons, and a mobile menu using Accordion.\n *\n * Visual lineage: the long-running navbar shipped on epigraphmedia.com and\n * its sister sites, ported to a self-contained block with a clean prop API.\n */\nexport function Navbar({\n  brand,\n  brandHref = \"/\",\n  brandLabel = \"Home\",\n  links,\n  ctas,\n  trailing,\n  className,\n  topOffset = 0,\n  maxWidthClassName = \"max-w-6xl\",\n  position = \"fixed\",\n}: NavbarProps) {\n  const [isMobileMenuOpen, setIsMobileMenuOpen] = React.useState(false);\n  const isScrolled = useIsScrolled();\n  useBodyScrollLock(isMobileMenuOpen && position === \"fixed\");\n\n  const positionClass = {\n    fixed: \"fixed inset-x-0 z-50\",\n    absolute: \"absolute inset-x-0 z-50\",\n    sticky: \"sticky top-0 z-50\",\n    static: \"static\",\n  }[position];\n\n  return (\n    <header\n      className={cn(\n        positionClass,\n        \"border-foreground/5 border-b bg-background/80 backdrop-blur transition-all duration-300\",\n        \"data-[scrolled=true]:bg-background/85 data-[scrolled=true]:shadow-black/5 data-[scrolled=true]:shadow-sm data-[scrolled=true]:backdrop-blur-md\",\n        isMobileMenuOpen && \"max-lg:h-screen max-lg:overflow-hidden\",\n        isMobileMenuOpen && \"bg-background/90\",\n        className\n      )}\n      data-state={isMobileMenuOpen ? \"active\" : \"inactive\"}\n      style={position === \"fixed\" ? { top: topOffset } : undefined}\n      {...(isScrolled && { \"data-scrolled\": true })}\n    >\n      <div\n        className={cn(\n          \"relative mx-auto px-6\",\n          \"before:absolute before:inset-y-0 before:-left-px before:w-px before:bg-foreground/8 before:content-['']\",\n          \"after:absolute after:inset-y-0 after:-right-px after:w-px after:bg-foreground/8 after:content-['']\",\n          maxWidthClassName\n        )}\n      >\n        <div className=\"relative flex h-14 flex-wrap items-center justify-between lg:h-auto lg:py-5\">\n          {/* Brand + mobile menu trigger */}\n          <div className=\"flex w-full items-center justify-between gap-8 lg:w-auto\">\n            <Link\n              aria-label={brandLabel}\n              className=\"flex items-center\"\n              href={brandHref}\n            >\n              {brand}\n            </Link>\n\n            <button\n              aria-label={isMobileMenuOpen ? \"Close menu\" : \"Open menu\"}\n              className=\"relative z-20 -m-2.5 -mr-3 block cursor-pointer p-2.5 lg:hidden\"\n              onClick={() => setIsMobileMenuOpen((v) => !v)}\n              type=\"button\"\n            >\n              <Menu className=\"m-auto size-5 in-data-[state=active]:rotate-180 in-data-[state=active]:scale-0 in-data-[state=active]:opacity-0 duration-200\" />\n              <X className=\"absolute inset-0 m-auto size-5 -rotate-180 in-data-[state=active]:rotate-0 in-data-[state=active]:scale-100 scale-0 in-data-[state=active]:opacity-100 opacity-0 duration-200\" />\n            </button>\n          </div>\n\n          {/* Desktop center nav */}\n          <div className=\"absolute inset-0 m-auto hidden size-fit lg:block\">\n            <DesktopMenu links={links} />\n          </div>\n\n          {/* Mobile menu */}\n          {isMobileMenuOpen && (\n            <div className=\"w-full pb-6 lg:hidden\">\n              <MobileMenu\n                closeMenu={() => setIsMobileMenuOpen(false)}\n                links={links}\n              />\n            </div>\n          )}\n\n          {/* Right rail */}\n          <div className=\"in-data-[state=active]:flex hidden w-full flex-wrap items-center justify-end gap-3 lg:flex lg:w-fit\">\n            {ctas?.map((cta) => (\n              <Button\n                asChild\n                key={`${cta.label}-${cta.href}`}\n                size=\"sm\"\n                variant={cta.variant ?? \"default\"}\n              >\n                <Link href={cta.href}>{cta.label}</Link>\n              </Button>\n            ))}\n            {trailing}\n          </div>\n        </div>\n      </div>\n    </header>\n  );\n}\n\n// ───────── Desktop menu ─────────\n\nfunction DesktopMenu({ links }: { links: NavbarLink[] }) {\n  return (\n    <NavigationMenu>\n      <NavigationMenuList className=\"gap-1\">\n        {links.map((link) => {\n          if (link.items && link.items.length > 0) {\n            const hasRichItems = link.items.some(\n              (i) => i.icon || i.description\n            );\n            return (\n              <NavigationMenuItem key={`${link.label}-${link.href}`}>\n                <NavigationMenuTrigger>{link.label}</NavigationMenuTrigger>\n                <NavigationMenuContent>\n                  <ul\n                    className={cn(\n                      \"grid gap-1 p-2\",\n                      hasRichItems\n                        ? \"w-[420px] sm:w-[620px] sm:grid-cols-2\"\n                        : \"w-[260px]\"\n                    )}\n                  >\n                    {link.items.map((item) => (\n                      <DropdownRow\n                        item={item}\n                        key={`${item.label}-${item.href}`}\n                      />\n                    ))}\n                  </ul>\n                </NavigationMenuContent>\n              </NavigationMenuItem>\n            );\n          }\n\n          return (\n            <NavigationMenuItem key={`${link.label}-${link.href}`}>\n              <NavigationMenuLink\n                asChild\n                className={navigationMenuTriggerStyle()}\n              >\n                <Link href={link.href}>{link.label}</Link>\n              </NavigationMenuLink>\n            </NavigationMenuItem>\n          );\n        })}\n      </NavigationMenuList>\n    </NavigationMenu>\n  );\n}\n\nfunction DropdownRow({ item }: { item: NavbarDropdownItem }) {\n  const isRich = Boolean(item.icon || item.description);\n\n  if (!isRich) {\n    return (\n      <li>\n        <NavigationMenuLink asChild>\n          <Link href={item.href}>\n            <span className=\"font-medium text-foreground text-sm\">\n              {item.label}\n            </span>\n          </Link>\n        </NavigationMenuLink>\n      </li>\n    );\n  }\n\n  return (\n    <li>\n      <NavigationMenuLink asChild>\n        <Link\n          className=\"grid grid-cols-[auto_1fr] gap-3 rounded-md p-2\"\n          href={item.href}\n        >\n          {item.icon && (\n            <div className=\"flex size-10 items-center justify-center rounded border bg-card shadow-sm ring-1 ring-foreground/10\">\n              {item.icon}\n            </div>\n          )}\n          <div className=\"min-w-0\">\n            <div className=\"truncate font-medium text-foreground text-sm\">\n              {item.label}\n            </div>\n            {item.description && (\n              <p className=\"line-clamp-1 text-muted-foreground text-xs\">\n                {item.description}\n              </p>\n            )}\n          </div>\n        </Link>\n      </NavigationMenuLink>\n    </li>\n  );\n}\n\n// ───────── Mobile menu ─────────\n\nfunction MobileMenu({\n  links,\n  closeMenu,\n}: {\n  links: NavbarLink[];\n  closeMenu: () => void;\n}) {\n  return (\n    <nav className=\"w-full\">\n      <Accordion\n        className=\"-mx-4 mt-0.5 space-y-0.5 **:hover:no-underline\"\n        collapsible\n        type=\"single\"\n      >\n        {links.map((link) => {\n          if (link.items && link.items.length > 0) {\n            return (\n              <AccordionItem\n                className=\"group relative border-b-0\"\n                key={`${link.label}-${link.href}`}\n                value={link.label}\n              >\n                <AccordionTrigger className=\"flex items-center justify-between px-4 py-3 text-lg **:font-normal! data-[state=open]:bg-muted\">\n                  {link.label}\n                </AccordionTrigger>\n                <AccordionContent className=\"pb-5\">\n                  <ul>\n                    {link.items.map((item) => (\n                      <li key={`${item.label}-${item.href}`}>\n                        <Link\n                          className=\"grid grid-cols-[auto_1fr] items-center gap-2.5 px-4 py-2\"\n                          href={item.href}\n                          onClick={closeMenu}\n                        >\n                          {item.icon && (\n                            <div\n                              aria-hidden=\"true\"\n                              className=\"flex items-center justify-center *:size-4\"\n                            >\n                              {item.icon}\n                            </div>\n                          )}\n                          <div className=\"text-base\">{item.label}</div>\n                        </Link>\n                      </li>\n                    ))}\n                  </ul>\n                </AccordionContent>\n              </AccordionItem>\n            );\n          }\n\n          return (\n            <Link\n              className=\"group relative block py-4 text-lg\"\n              href={link.href}\n              key={`${link.label}-${link.href}`}\n              onClick={closeMenu}\n            >\n              {link.label}\n            </Link>\n          );\n        })}\n      </Accordion>\n    </nav>\n  );\n}\n\n// ───────── Hooks ─────────\n\nfunction useIsScrolled(): boolean {\n  const [isScrolled, setIsScrolled] = React.useState(false);\n\n  React.useEffect(() => {\n    let rafId: number | null = null;\n    let lastValue = false;\n\n    const update = () => {\n      rafId = null;\n      const nextValue = window.scrollY > 5;\n      if (nextValue !== lastValue) {\n        lastValue = nextValue;\n        setIsScrolled(nextValue);\n      }\n    };\n\n    const onScroll = () => {\n      if (rafId != null) {\n        return;\n      }\n      rafId = window.requestAnimationFrame(update);\n    };\n\n    update();\n\n    const scrollOptions = { passive: true } as EventListenerOptions;\n    window.addEventListener(\"scroll\", onScroll, scrollOptions);\n\n    return () => {\n      if (rafId != null) {\n        window.cancelAnimationFrame(rafId);\n      }\n      window.removeEventListener(\"scroll\", onScroll, scrollOptions);\n    };\n  }, []);\n\n  return isScrolled;\n}\n\nfunction useBodyScrollLock(locked: boolean) {\n  React.useEffect(() => {\n    const originalOverflow = document.body.style.overflow;\n    if (locked) {\n      document.body.style.overflow = \"hidden\";\n    }\n    return () => {\n      document.body.style.overflow = originalOverflow;\n    };\n  }, [locked]);\n}\n",
      "type": "registry:component"
    }
  ],
  "categories": [
    "navigation"
  ],
  "type": "registry:block"
}