{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "nav-minimal",
  "type": "registry:block",
  "title": "Minimal sticky header",
  "description": "Wordmark, centered links, and a cart badge that springs on change. Hairline appears on scroll.",
  "author": "StoreKit <hello@storekit.dev>",
  "categories": [
    "nav",
    "any"
  ],
  "dependencies": [
    "motion",
    "lucide-react"
  ],
  "registryDependencies": [
    "https://storekit-eight.vercel.app/r/cn.json",
    "https://storekit-eight.vercel.app/r/sk-analytics.json"
  ],
  "files": [
    {
      "path": "nav/nav-minimal.tsx",
      "content": "\"use client\"\n\nimport { useEffect, useState } from \"react\"\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\"\nimport { Menu, Search, ShoppingBag, User } from \"lucide-react\"\nimport { cn } from \"@/components/storekit/lib/cn\"\nimport { useSkBlock } from \"@/components/storekit/lib/sk-analytics\"\n\n/* Minimal sticky header: wordmark left, links centered, actions right. The\n   hairline under it only appears once the page has scrolled, and the cart\n   badge springs each time the count changes. Pass `scrollRoot` when the\n   header lives inside a scrolling element instead of the window. Below md the\n   links collapse behind a menu button that calls `onMenu` (pair it with a\n   drawer). */\n\ntype NavLink = { label: string; href: string }\n\nconst ring =\n  \"outline-none focus-visible:ring-2 focus-visible:ring-sk-accent focus-visible:ring-offset-2 focus-visible:ring-offset-sk-paper\"\n\nexport function NavMinimal({\n  brandName = \"Morrow\",\n  markSrc,\n  links = [\n    { label: \"Shop\", href: \"#shop\" },\n    { label: \"Bundles\", href: \"#bundles\" },\n    { label: \"Lab results\", href: \"#lab-results\" },\n    { label: \"Journal\", href: \"#journal\" },\n  ],\n  cartCount = 0,\n  onSearch,\n  onAccount,\n  onCart,\n  onMenu,\n  scrollRoot,\n  className,\n}: {\n  brandName?: string\n  markSrc?: string\n  links?: NavLink[]\n  cartCount?: number\n  onSearch?: () => void\n  onAccount?: () => void\n  onCart?: () => void\n  onMenu?: () => void\n  scrollRoot?: HTMLElement | null\n  className?: string\n}) {\n  const reduce = useReducedMotion()\n  const sk = useSkBlock(\"nav\", \"nav-minimal\")\n  const [scrolled, setScrolled] = useState(false)\n\n  useEffect(() => {\n    const target: HTMLElement | Window = scrollRoot ?? window\n    const read = () => setScrolled((scrollRoot ? scrollRoot.scrollTop : window.scrollY) > 4)\n    read()\n    target.addEventListener(\"scroll\", read, { passive: true })\n    return () => target.removeEventListener(\"scroll\", read)\n  }, [scrollRoot])\n\n  const icon = cn(\"grid h-11 w-11 place-items-center rounded-sk-btn text-sk-ink transition-colors duration-200 hover:bg-sk-ink/[0.05] active:scale-[0.97]\", ring)\n\n  return (\n    <header\n      ref={sk.ref}\n      {...sk.attrs}\n      className={cn(\n        \"sticky top-0 z-30 border-b bg-sk-paper/95 transition-colors duration-300\",\n        scrolled ? \"border-sk-line\" : \"border-transparent\",\n        className\n      )}\n    >\n      <div className=\"mx-auto flex h-16 max-w-6xl items-center gap-2 px-2 sm:gap-4 sm:px-8\">\n        <button type=\"button\" aria-label=\"Open menu\" aria-haspopup=\"dialog\" onClick={onMenu} className={cn(icon, \"md:hidden\")}>\n          <Menu size={20} aria-hidden />\n        </button>\n        <a href=\"/\" className={cn(\"flex min-h-11 min-w-0 items-center gap-2.5 rounded-sk-btn pr-1\", ring)}>\n          {markSrc ? <img src={markSrc} alt=\"\" className=\"h-7 w-7 shrink-0 object-contain\" /> : null}\n          <span className=\"truncate font-sk-display text-[15px] font-semibold tracking-[-0.02em] text-sk-ink\">{brandName}</span>\n        </a>\n        <nav aria-label=\"Primary\" className=\"hidden flex-1 justify-center md:flex\">\n          <ul className=\"flex items-center gap-1\">\n            {links.map((l) => (\n              <li key={l.href}>\n                <a\n                  href={l.href}\n                  className={cn(\"inline-flex h-11 items-center rounded-sk-btn px-3 text-sm text-sk-muted transition-colors duration-200 hover:text-sk-ink\", ring)}\n                >\n                  {l.label}\n                </a>\n              </li>\n            ))}\n          </ul>\n        </nav>\n        <div className=\"ml-auto flex shrink-0 items-center md:ml-0\">\n          <button type=\"button\" aria-label=\"Search\" onClick={onSearch} className={icon}>\n            <Search size={18} aria-hidden />\n          </button>\n          <button type=\"button\" aria-label=\"Account\" onClick={onAccount} className={cn(icon, \"hidden sm:grid\")}>\n            <User size={18} aria-hidden />\n          </button>\n          <button\n            type=\"button\"\n            aria-label={`Cart, ${cartCount} ${cartCount === 1 ? \"item\" : \"items\"}`}\n            onClick={() => {\n              sk.emit(\"interact\", \"cart_open\")\n              onCart?.()\n            }}\n            className={cn(icon, \"relative\")}\n          >\n            <ShoppingBag size={18} aria-hidden />\n            <AnimatePresence initial={false}>\n              {cartCount > 0 ? (\n                <motion.span\n                  key={cartCount}\n                  aria-hidden\n                  initial={reduce ? { opacity: 0 } : { scale: 0.4, opacity: 0 }}\n                  animate={{ scale: 1, opacity: 1 }}\n                  exit={{ scale: reduce ? 1 : 0.4, opacity: 0, transition: { duration: 0.12 } }}\n                  transition={reduce ? { duration: 0.15 } : { type: \"spring\", stiffness: 520, damping: 16 }}\n                  className=\"absolute right-1 top-1 grid h-[18px] min-w-[18px] place-items-center rounded-full bg-sk-accent px-1 text-[10px] font-semibold tabular-nums leading-none text-sk-accent-ink\"\n                >\n                  {cartCount > 99 ? \"99+\" : cartCount}\n                </motion.span>\n              ) : null}\n            </AnimatePresence>\n          </button>\n        </div>\n      </div>\n    </header>\n  )\n}\n",
      "type": "registry:block",
      "target": "components/storekit/nav/nav-minimal.tsx"
    }
  ],
  "docs": "Pass scrollRoot when the header sits inside a scrolling element rather than the window.",
  "meta": {
    "section": "nav",
    "variant": "minimal",
    "niche": [
      "any"
    ],
    "commerce": [
      "cart-count",
      "search"
    ],
    "previewHeight": 440
  }
}