{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "pdp-sticky-atc",
  "type": "registry:component",
  "title": "Sticky add to cart",
  "description": "Bottom bar that slides up once the buy button scrolls away, with thumb, variant, price and add.",
  "author": "StoreKit <hello@storekit.dev>",
  "categories": [
    "pdp",
    "any"
  ],
  "dependencies": [
    "motion",
    "lucide-react"
  ],
  "registryDependencies": [
    "https://storekit-eight.vercel.app/r/cn.json",
    "https://storekit-eight.vercel.app/r/button.json",
    "https://storekit-eight.vercel.app/r/price.json",
    "https://storekit-eight.vercel.app/r/sk-analytics.json"
  ],
  "files": [
    {
      "path": "pdp/pdp-sticky-atc.tsx",
      "content": "\"use client\"\n\nimport { useEffect, useRef, useState } from \"react\"\nimport { motion, useReducedMotion } from \"motion/react\"\nimport { Check } from \"lucide-react\"\nimport { cn } from \"@/components/storekit/lib/cn\"\nimport { Button } from \"@/components/storekit/ui/button\"\nimport { Price } from \"@/components/storekit/ui/price\"\nimport { useSkBlock } from \"@/components/storekit/lib/sk-analytics\"\n\n/* Sticky add-to-cart bar. Slides up once the main buy button scrolls away.\n   `position=\"absolute\"` pins it inside a scroll frame instead of the window.\n   The bar stays mounted and slides off screen when hidden (inert), so the\n   view event only fires once it is actually on screen. */\n\nconst EASE = [0.23, 1, 0.32, 1] as const\n\nexport function PdpStickyAtc({\n  visible = true,\n  title = \"Daily Greens\",\n  image = \"/brands/morrow/greens.webp\",\n  variant = \"30 servings\",\n  price = 4900,\n  compareAt,\n  position = \"fixed\",\n  onAdd,\n  className,\n}: {\n  visible?: boolean\n  title?: string\n  image?: string\n  variant?: string\n  price?: number\n  compareAt?: number\n  position?: \"fixed\" | \"absolute\"\n  onAdd?: () => void\n  className?: string\n}) {\n  const reduce = useReducedMotion()\n  const sk = useSkBlock(\"pdp\", \"pdp-sticky-atc\")\n  const [status, setStatus] = useState<\"idle\" | \"loading\" | \"added\">(\"idle\")\n  const timers = useRef<number[]>([])\n  useEffect(() => () => timers.current.forEach((t) => window.clearTimeout(t)), [])\n\n  const add = () => {\n    if (status !== \"idle\") return\n    setStatus(\"loading\")\n    sk.emit(\"convert\", \"add_to_cart\", price)\n    timers.current.push(\n      window.setTimeout(() => {\n        setStatus(\"added\")\n        onAdd?.()\n        timers.current.push(window.setTimeout(() => setStatus(\"idle\"), 1200))\n      }, 700)\n    )\n  }\n\n  return (\n    <motion.div\n      {...sk.attrs}\n      ref={sk.ref as React.RefObject<HTMLDivElement>}\n      role=\"region\"\n      aria-label=\"Quick add to cart\"\n      aria-hidden={!visible || undefined}\n      inert={!visible}\n      initial={false}\n      animate={{ y: visible || reduce ? 0 : \"110%\", opacity: visible ? 1 : 0 }}\n      transition={{ duration: reduce ? 0.15 : 0.45, ease: EASE }}\n      className={cn(position === \"fixed\" ? \"fixed\" : \"absolute\", \"inset-x-0 bottom-0 z-30 p-3\", !visible && \"pointer-events-none\", className)}\n    >\n      <div className=\"mx-auto flex max-w-3xl items-center gap-3 rounded-sk-card border border-sk-line bg-sk-card p-2.5 pr-3 shadow-[0_24px_60px_-24px_rgb(var(--sk-ink)/0.25)]\">\n        <span className=\"relative h-12 w-12 shrink-0 overflow-hidden rounded-sk-btn bg-sk-surface\">\n          <img src={image} alt=\"\" className=\"absolute inset-0 h-full w-full object-cover\" />\n        </span>\n        <span className=\"flex min-w-0 flex-1 flex-col\">\n          <span className=\"truncate text-sm font-medium text-sk-ink\">{title}</span>\n          <span className=\"flex flex-wrap items-baseline gap-x-2 text-[13px] text-sk-muted\">\n            <span className=\"font-sk-mono text-[11px] uppercase tracking-[0.08em]\">{variant}</span>\n            <Price amount={price} compareAt={compareAt} size=\"sm\" />\n          </span>\n        </span>\n        <Button onClick={add} loading={status === \"loading\"} className=\"min-w-[96px] shrink-0\">\n          {status === \"added\" ? (\n            <>\n              <Check size={16} strokeWidth={2.5} /> Added\n            </>\n          ) : (\n            \"Add\"\n          )}\n        </Button>\n      </div>\n    </motion.div>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/storekit/pdp/pdp-sticky-atc.tsx"
    }
  ],
  "docs": "Drive `visible` from an IntersectionObserver on your main add to cart button.",
  "meta": {
    "section": "pdp",
    "variant": "sticky",
    "niche": [
      "any"
    ],
    "commerce": [
      "atc"
    ],
    "previewHeight": 460
  }
}