{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "macbook-scroll",
  "title": "Macbook Scroll",
  "description": "A CSS-drawn laptop pins in place and opens its lid as the screenshot scales up.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "src/components/velora/macbook-scroll.tsx",
      "content": "\"use client\";\n\nimport { useRef } from \"react\";\nimport { motion, useScroll, useSpring, useTransform } from \"motion/react\";\n\nimport { cn } from \"@/lib/utils\";\n\ninterface MacbookScrollProps\n  extends Omit<React.HTMLAttributes<HTMLDivElement>, \"title\"> {\n  /** Screenshot shown on the screen */\n  src?: string;\n  /** Alt text for `src`; leave empty if the image is decorative */\n  alt?: string;\n  /** Custom screen content, used instead of `src`; `cqw` units scale with the screen */\n  children?: React.ReactNode;\n  /** Heading above the laptop, rendered in an h2 */\n  title?: React.ReactNode;\n  /** Small element on the lower-left of the deck, e.g. a logo */\n  badge?: React.ReactNode;\n  /** Scrollable element to track instead of the window */\n  container?: React.RefObject<HTMLElement | null>;\n}\n\n// Bottom keyboard row: key widths in grid columns (14 per row).\nconst BOTTOM_ROW = [1, 1, 1, 2, 5, 2, 1, 1];\n\n/**\n * A laptop drawn in CSS that pins in place, opens its lid and scales the\n * screenshot to fill the screen as you scroll. Heights use `cqh`, so inside a\n * `@container-size` scroll box they measure that box instead of the\n * viewport. Under `prefers-reduced-motion` the laptop renders open and still.\n */\nexport function MacbookScroll({\n  src,\n  alt = \"\",\n  children,\n  title,\n  badge,\n  container,\n  className,\n  ...props\n}: MacbookScrollProps) {\n  const ref = useRef<HTMLDivElement>(null);\n  const { scrollYProgress } = useScroll({\n    container,\n    target: ref,\n    offset: [\"start start\", \"end end\"],\n  });\n  const progress = useSpring(scrollYProgress, { stiffness: 200, damping: 34 });\n\n  const rotateX = useTransform(progress, [0, 0.6], [-122, 0]);\n  const scale = useTransform(progress, [0.25, 0.85], [0.7, 1]);\n  const titleY = useTransform(progress, [0, 0.6], [0, -16]);\n\n  return (\n    <div\n      ref={ref}\n      data-slot=\"macbook-scroll\"\n      className={cn(\"relative h-[200cqh] motion-reduce:h-auto\", className)}\n      {...props}\n    >\n      <div className=\"sticky top-0 flex h-[100cqh] flex-col items-center justify-center gap-[5cqh] overflow-x-clip px-4\">\n        {title && (\n          <motion.h2\n            style={{ y: titleY }}\n            className=\"max-w-xl text-center text-2xl font-semibold tracking-tight text-balance sm:text-3xl motion-reduce:transform-none!\"\n          >\n            {title}\n          </motion.h2>\n        )}\n\n        <div className=\"flex w-[min(100%,44rem,78cqh)] flex-col items-center perspective-[1600px] @container\">\n          {/* Lid: the screen on the front face, the shell on the back. */}\n          <motion.div\n            style={{ rotateX }}\n            className=\"relative z-10 w-[88%] origin-bottom transform-3d will-change-transform motion-reduce:transform-none!\"\n          >\n            <div className=\"rounded-t-[2.4cqw] border border-b-0 border-zinc-700/60 bg-zinc-950 p-[1.6cqw] pb-[2.6cqw] shadow-2xl shadow-black/20 backface-hidden\">\n              <div className=\"relative aspect-[16/10] overflow-hidden rounded-[0.8cqw] bg-black @container\">\n                <motion.div\n                  style={{ scale }}\n                  className=\"size-full overflow-hidden rounded-[0.8cqw] will-change-transform motion-reduce:transform-none!\"\n                >\n                  {children ??\n                    (src && (\n                      // eslint-disable-next-line @next/next/no-img-element\n                      <img loading=\"lazy\" decoding=\"async\" src={src} alt={alt} className=\"size-full object-cover object-top\" />\n                    ))}\n                </motion.div>\n              </div>\n            </div>\n            <div\n              aria-hidden\n              className=\"absolute inset-0 flex rotate-x-180 items-center justify-center rounded-t-[2.4cqw] bg-gradient-to-b from-zinc-300 to-zinc-400 backface-hidden dark:from-zinc-700 dark:to-zinc-800\"\n            >\n              <span className=\"size-[7cqw] rounded-full bg-gradient-to-br from-brand-from to-brand-to opacity-70\" />\n            </div>\n          </motion.div>\n\n          {/* Deck, laid back in perspective like a laptop seen from the front. */}\n          <div className=\"relative -mb-[22cqw] w-[88%] origin-top rotate-x-[52deg] rounded-b-[2.4cqw] bg-gradient-to-b from-zinc-200 to-zinc-300 p-[2.5cqw] pt-[1.5cqw] shadow-[inset_0_1px_0_rgb(255_255_255/0.6)] dark:from-zinc-700 dark:to-zinc-800 dark:shadow-[inset_0_1px_0_rgb(255_255_255/0.08)]\"\n          >\n            <div aria-hidden className=\"mx-auto mb-[2cqw] h-[1.2cqw] w-1/4 rounded-b-[0.8cqw] bg-zinc-400/70 dark:bg-zinc-900/70\" />\n            <div aria-hidden className=\"grid grid-cols-14 gap-[0.5cqw] rounded-[1cqw] bg-zinc-900 p-[0.8cqw]\">\n              {Array.from({ length: 70 }, (_, i) => (\n                <span key={i} className=\"aspect-square rounded-[0.4cqw] bg-zinc-800 shadow-[inset_0_-1px_0_rgb(0_0_0/0.5)]\" />\n              ))}\n              {BOTTOM_ROW.map((span, i) => (\n                <span\n                  key={`b${i}`}\n                  style={{ gridColumn: `span ${span}` }}\n                  className=\"h-[4.2cqw] rounded-[0.4cqw] bg-zinc-800 shadow-[inset_0_-1px_0_rgb(0_0_0/0.5)]\"\n                />\n              ))}\n            </div>\n            <div aria-hidden className=\"mx-auto mt-[2.5cqw] aspect-[16/9] w-2/5 rounded-[1.2cqw] bg-zinc-300/80 shadow-[inset_0_0_0_1px_rgb(0_0_0/0.06)] dark:bg-zinc-600/60 dark:shadow-[inset_0_0_0_1px_rgb(255_255_255/0.06)]\" />\n            {badge && (\n              <div className=\"absolute bottom-[2.5cqw] left-[3cqw]\">{badge}</div>\n            )}\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:ui",
      "target": "components/velora/macbook-scroll.tsx"
    }
  ],
  "type": "registry:ui"
}