{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "resizable-navbar",
  "title": "Resizable Navbar",
  "description": "Full-width navbar that shrinks into a floating, blurred pill once you scroll.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "src/components/velora/resizable-navbar.tsx",
      "content": "\"use client\";\n\nimport { useEffect, useId, useRef, useState } from \"react\";\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\n\nimport { cn } from \"@/lib/utils\";\n\ninterface NavItem {\n  label: string;\n  href: string;\n}\n\ninterface ResizableNavbarProps extends React.HTMLAttributes<HTMLElement> {\n  /** Links shown in the bar (and in the mobile menu) */\n  items: NavItem[];\n  /** Brand mark or logo link on the left */\n  logo?: React.ReactNode;\n  /** Call to action on the right (e.g. a sign-up link) */\n  cta?: React.ReactNode;\n  /** Pixels scrolled before the bar shrinks into a floating pill */\n  threshold?: number;\n  /** Maximum width of the floating pill, in px */\n  compactWidth?: number;\n  /** Maximum width of the full bar, in px */\n  maxWidth?: number;\n  /** href of the current page; that link gets aria-current=\"page\" */\n  activeHref?: string;\n  /** Accessible name of the navigation landmark */\n  label?: string;\n  /** Scrollable element to watch instead of the window */\n  container?: React.RefObject<HTMLElement | null>;\n}\n\n/**\n * Sticky navbar that shrinks into a centred, blurred pill once the page is\n * scrolled. A highlight glides between links on hover and focus; below a\n * 48rem container width the links move into a disclosure menu.\n */\nexport function ResizableNavbar({\n  items,\n  logo,\n  cta,\n  threshold = 80,\n  compactWidth = 760,\n  maxWidth = 1152,\n  activeHref,\n  label = \"Main\",\n  container,\n  className,\n  ...props\n}: ResizableNavbarProps) {\n  const [compact, setCompact] = useState(false);\n  const [hover, setHover] = useState({ left: 0, width: 0, on: false, moved: false });\n  const [menuOpen, setMenuOpen] = useState(false);\n  const reduce = useReducedMotion();\n  const id = useId();\n  const rootRef = useRef<HTMLElement>(null);\n  const buttonRef = useRef<HTMLButtonElement>(null);\n  const spring = reduce\n    ? { duration: 0 }\n    : { type: \"spring\" as const, stiffness: 300, damping: 30 };\n\n  useEffect(() => {\n    const el = container?.current;\n    const target = el ?? window;\n    // Same-value state updates bail out, so no throttling is needed.\n    const update = () => setCompact((el ? el.scrollTop : window.scrollY) > threshold);\n    update();\n    target.addEventListener(\"scroll\", update, { passive: true });\n    return () => target.removeEventListener(\"scroll\", update);\n  }, [container, threshold]);\n\n  useEffect(() => {\n    if (!menuOpen) return;\n    const onPointerDown = (event: PointerEvent) => {\n      if (!rootRef.current?.contains(event.target as Node)) setMenuOpen(false);\n    };\n    document.addEventListener(\"pointerdown\", onPointerDown);\n    return () => document.removeEventListener(\"pointerdown\", onPointerDown);\n  }, [menuOpen]);\n\n  // The highlight glides between links; appearing from nothing it fades in place.\n  const show = ({ currentTarget: t }: React.SyntheticEvent<HTMLElement>) =>\n    setHover((h) => ({ left: t.offsetLeft, width: t.offsetWidth, on: true, moved: h.on }));\n  const hide = () => setHover((h) => ({ ...h, on: false }));\n  const glide = reduce || !hover.moved ? { duration: 0 } : spring;\n\n  const link = (item: NavItem, inMenu: boolean) => (\n    <a\n      href={item.href}\n      aria-current={item.href === activeHref ? \"page\" : undefined}\n      onMouseEnter={inMenu ? undefined : show}\n      onFocus={inMenu ? undefined : show}\n      onClick={() => setMenuOpen(false)}\n      className={cn(\n        \"relative block rounded-full px-3.5 py-1.5 text-sm text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring aria-[current=page]:text-foreground\",\n        inMenu && \"rounded-xl px-4 py-2.5 text-base hover:bg-muted\"\n      )}\n    >\n      {item.label}\n    </a>\n  );\n\n  return (\n    <header\n      {...props}\n      ref={rootRef}\n      data-slot=\"resizable-navbar\"\n      onKeyDown={(event) => {\n        if (event.key === \"Escape\" && menuOpen) {\n          setMenuOpen(false);\n          buttonRef.current?.focus();\n        }\n        props.onKeyDown?.(event);\n      }}\n      className={cn(\"@container/navbar sticky top-0 z-50 w-full px-3\", className)}\n    >\n      <motion.nav\n        aria-label={label}\n        initial={false}\n        animate={{ maxWidth: compact ? compactWidth : maxWidth, y: compact ? 12 : 0 }}\n        transition={spring}\n        className={cn(\n          \"relative mx-auto flex h-14 items-center gap-4 rounded-full border px-4 transition-[background-color,border-color,box-shadow] duration-300 motion-reduce:transition-none\",\n          compact || menuOpen\n            ? \"border-border bg-background/75 shadow-lg shadow-foreground/5 backdrop-blur-md\"\n            : \"border-transparent\"\n        )}\n      >\n        <div className=\"flex shrink-0 items-center\">{logo}</div>\n        <div className=\"hidden flex-1 justify-center @3xl/navbar:flex\">\n          <div\n            onMouseLeave={hide}\n            onBlur={(event) => {\n              if (!event.currentTarget.contains(event.relatedTarget)) hide();\n            }}\n            className=\"relative\"\n          >\n            <motion.span\n              aria-hidden\n              initial={false}\n              animate={{ x: hover.left, width: hover.width, opacity: hover.on ? 1 : 0 }}\n              transition={{ x: glide, width: glide, opacity: { duration: reduce ? 0 : 0.15 } }}\n              className=\"absolute inset-y-0 left-0 rounded-full bg-muted\"\n            />\n            <ul className=\"flex items-center gap-1\">\n              {items.map((item, i) => (\n                <li key={`${i}-${item.label}`}>{link(item, false)}</li>\n              ))}\n            </ul>\n          </div>\n        </div>\n        {cta && <div className=\"hidden shrink-0 @3xl/navbar:block\">{cta}</div>}\n        <button\n          ref={buttonRef}\n          type=\"button\"\n          aria-label=\"Menu\"\n          aria-expanded={menuOpen}\n          aria-controls={menuOpen ? `${id}-menu` : undefined}\n          onClick={() => setMenuOpen((open) => !open)}\n          className=\"group/menu ml-auto grid size-9 cursor-pointer place-items-center rounded-full transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring @3xl/navbar:hidden\"\n        >\n          <span aria-hidden className=\"relative block h-3 w-4\">\n            <span className=\"absolute top-0 left-0 h-0.5 w-4 rounded-full bg-foreground transition-transform duration-200 group-aria-expanded/menu:translate-y-[5px] group-aria-expanded/menu:rotate-45 motion-reduce:transition-none\" />\n            <span className=\"absolute bottom-0 left-0 h-0.5 w-4 rounded-full bg-foreground transition-transform duration-200 group-aria-expanded/menu:-translate-y-[5px] group-aria-expanded/menu:-rotate-45 motion-reduce:transition-none\" />\n          </span>\n        </button>\n\n        <AnimatePresence>\n          {menuOpen && (\n            <motion.div\n              id={`${id}-menu`}\n              initial={{ opacity: 0, y: -8 }}\n              animate={{ opacity: 1, y: 0 }}\n              exit={{ opacity: 0, y: -8 }}\n              transition={reduce ? { duration: 0 } : { duration: 0.2 }}\n              className=\"absolute inset-x-0 top-full mt-2 rounded-2xl border bg-popover p-2 text-popover-foreground shadow-xl @3xl/navbar:hidden\"\n            >\n              <ul>\n                {items.map((item, i) => (\n                  <li key={`${i}-${item.label}`}>{link(item, true)}</li>\n                ))}\n              </ul>\n              {cta && <div className=\"mt-2 border-t p-2 pt-3\">{cta}</div>}\n            </motion.div>\n          )}\n        </AnimatePresence>\n      </motion.nav>\n    </header>\n  );\n}\n",
      "type": "registry:ui",
      "target": "components/velora/resizable-navbar.tsx"
    }
  ],
  "type": "registry:ui"
}