{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "scroll-blur",
  "title": "Scroll Blur",
  "description": "A scroll utility with top, bottom, left, and right edge blurs plus optional snap-item helpers.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/corr/components/scroll-blur.tsx",
      "content": "import * as React from \"react\"\nimport { motion, useReducedMotion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\ntype ScrollBlurAxis = \"vertical\" | \"horizontal\" | \"both\"\ntype ScrollSnap = \"none\" | \"x\" | \"y\" | \"both\"\ntype ScrollSnapAlign = \"start\" | \"center\" | \"end\"\n\nexport interface ScrollBlurProps extends React.HTMLAttributes<HTMLDivElement> {\n  axis?: ScrollBlurAxis\n  edgeSize?: number\n  snap?: ScrollSnap\n  viewportClassName?: string\n  contentClassName?: string\n  children: React.ReactNode\n}\n\nexport function ScrollBlur({\n  axis = \"vertical\",\n  edgeSize = 40,\n  snap = \"none\",\n  className,\n  viewportClassName,\n  contentClassName,\n  children,\n  ...props\n}: ScrollBlurProps) {\n  const viewportRef = React.useRef<HTMLDivElement | null>(null)\n  const reduceMotion = useReducedMotion()\n  const [edges, setEdges] = React.useState({\n    top: false,\n    bottom: false,\n    left: false,\n    right: false,\n  })\n  const isVertical = axis === \"vertical\" || axis === \"both\"\n  const isHorizontal = axis === \"horizontal\" || axis === \"both\"\n\n  const updateEdges = React.useCallback(() => {\n    const viewport = viewportRef.current\n    if (!viewport) return\n\n    const maxTop = viewport.scrollHeight - viewport.clientHeight\n    const maxLeft = viewport.scrollWidth - viewport.clientWidth\n\n    setEdges({\n      top: isVertical && viewport.scrollTop > 2,\n      bottom: isVertical && viewport.scrollTop < maxTop - 2,\n      left: isHorizontal && viewport.scrollLeft > 2,\n      right: isHorizontal && viewport.scrollLeft < maxLeft - 2,\n    })\n  }, [isHorizontal, isVertical])\n\n  React.useEffect(() => {\n    const viewport = viewportRef.current\n    if (!viewport) return\n\n    updateEdges()\n\n    const resizeObserver = new ResizeObserver(updateEdges)\n    resizeObserver.observe(viewport)\n    if (viewport.firstElementChild) {\n      resizeObserver.observe(viewport.firstElementChild)\n    }\n\n    viewport.addEventListener(\"scroll\", updateEdges, { passive: true })\n    window.addEventListener(\"resize\", updateEdges)\n\n    return () => {\n      resizeObserver.disconnect()\n      viewport.removeEventListener(\"scroll\", updateEdges)\n      window.removeEventListener(\"resize\", updateEdges)\n    }\n  }, [updateEdges])\n\n  return (\n    <div\n      data-slot=\"scroll-blur\"\n      className={cn(\"relative overflow-hidden\", className)}\n      {...props}\n    >\n      <div\n        ref={viewportRef}\n        data-slot=\"scroll-blur-viewport\"\n        className={cn(\n          \"scrollbar-none h-full w-full\",\n          isVertical && \"overflow-y-auto\",\n          isHorizontal && \"overflow-x-auto\",\n          snap === \"x\" && \"snap-x snap-mandatory\",\n          snap === \"y\" && \"snap-y snap-mandatory\",\n          snap === \"both\" && \"snap-both snap-mandatory\",\n          viewportClassName\n        )}\n      >\n        <div data-slot=\"scroll-blur-content\" className={contentClassName}>\n          {children}\n        </div>\n      </div>\n\n      {isVertical ? (\n        <>\n          <ScrollBlurEdge\n            visible={edges.top}\n            side=\"top\"\n            size={edgeSize}\n            reduceMotion={reduceMotion}\n          />\n          <ScrollBlurEdge\n            visible={edges.bottom}\n            side=\"bottom\"\n            size={edgeSize}\n            reduceMotion={reduceMotion}\n          />\n        </>\n      ) : null}\n      {isHorizontal ? (\n        <>\n          <ScrollBlurEdge\n            visible={edges.left}\n            side=\"left\"\n            size={edgeSize}\n            reduceMotion={reduceMotion}\n          />\n          <ScrollBlurEdge\n            visible={edges.right}\n            side=\"right\"\n            size={edgeSize}\n            reduceMotion={reduceMotion}\n          />\n        </>\n      ) : null}\n    </div>\n  )\n}\n\nexport function ScrollSnapItem({\n  align = \"start\",\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLDivElement> & {\n  align?: ScrollSnapAlign\n}) {\n  return (\n    <div\n      data-slot=\"scroll-snap-item\"\n      className={cn(\n        align === \"start\" && \"snap-start\",\n        align === \"center\" && \"snap-center\",\n        align === \"end\" && \"snap-end\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction ScrollBlurEdge({\n  visible,\n  side,\n  size,\n  reduceMotion,\n}: {\n  visible: boolean\n  side: \"top\" | \"bottom\" | \"left\" | \"right\"\n  size: number\n  reduceMotion: boolean | null\n}) {\n  const isVertical = side === \"top\" || side === \"bottom\"\n  const gradient =\n    side === \"top\"\n      ? \"bg-linear-to-b\"\n      : side === \"bottom\"\n        ? \"bg-linear-to-t\"\n        : side === \"left\"\n          ? \"bg-linear-to-r\"\n          : \"bg-linear-to-l\"\n  const mask =\n    side === \"top\"\n      ? \"[mask-image:linear-gradient(to_bottom,black_0%,black_45%,transparent_100%)]\"\n      : side === \"bottom\"\n        ? \"[mask-image:linear-gradient(to_top,black_0%,black_45%,transparent_100%)]\"\n        : side === \"left\"\n          ? \"[mask-image:linear-gradient(to_right,black_0%,black_45%,transparent_100%)]\"\n          : \"[mask-image:linear-gradient(to_left,black_0%,black_45%,transparent_100%)]\"\n\n  return (\n    <motion.div\n      data-slot=\"scroll-blur-edge\"\n      aria-hidden=\"true\"\n      className={cn(\n        \"pointer-events-none absolute z-10\",\n        side === \"top\" && \"inset-x-0 top-0\",\n        side === \"bottom\" && \"inset-x-0 bottom-0\",\n        side === \"left\" && \"inset-y-0 left-0\",\n        side === \"right\" && \"inset-y-0 right-0\",\n        isVertical ? \"w-full\" : \"h-full\"\n      )}\n      style={isVertical ? { height: size } : { width: size }}\n      initial={false}\n      animate={{ opacity: visible ? 1 : 0 }}\n      transition={{ duration: reduceMotion ? 0 : 0.16 }}\n    >\n      <div\n        className={cn(\n          \"absolute inset-0 from-background via-background/75 to-transparent\",\n          gradient\n        )}\n      />\n      <div className={cn(\"absolute inset-0 backdrop-blur-[4px]\", mask)} />\n    </motion.div>\n  )\n}\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component"
}