{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "blur-reveal",
  "title": "Blur Reveal",
  "description": "Reveals content with a blur-to-sharp transition when it scrolls into view.",
  "dependencies": [
    "@vllnt/ui@^0.2.1"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/blur-reveal/blur-reveal.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"@vllnt/ui\";\n\n/** Props for {@link BlurReveal}. */\nexport type BlurRevealProps = React.ComponentPropsWithoutRef<\"div\"> & {\n  /** Delay in milliseconds before the reveal begins. Defaults to `0`. */\n  delay?: number;\n};\n\nfunction usePrefersReducedMotion(): boolean {\n  const [reduced, setReduced] = React.useState(false);\n\n  React.useEffect(() => {\n    if (\n      typeof window === \"undefined\" ||\n      typeof window.matchMedia !== \"function\"\n    ) {\n      return;\n    }\n\n    const query = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n    const onChange = (): void => {\n      setReduced(query.matches);\n    };\n\n    onChange();\n    query.addEventListener(\"change\", onChange);\n\n    return () => {\n      query.removeEventListener(\"change\", onChange);\n    };\n  }, []);\n\n  return reduced;\n}\n\nfunction useInView(): [React.RefObject<HTMLDivElement | null>, boolean] {\n  const elementRef = React.useRef<HTMLDivElement>(null);\n  const [inView, setInView] = React.useState(false);\n\n  React.useEffect(() => {\n    const element = elementRef.current;\n    if (!element || typeof IntersectionObserver !== \"function\") {\n      setInView(true);\n      return;\n    }\n\n    const observer = new IntersectionObserver((entries) => {\n      const entry = entries[0];\n      if (entry?.isIntersecting) {\n        setInView(true);\n        observer.disconnect();\n      }\n    });\n    observer.observe(element);\n\n    return () => {\n      observer.disconnect();\n    };\n  }, []);\n\n  return [elementRef, inView];\n}\n\n/**\n * Reveals content from a blurred, transparent state when it scrolls into view.\n *\n * Respects `prefers-reduced-motion`: content is visible from the start.\n *\n * @example\n * ```tsx\n * <BlurReveal delay={150}>Content</BlurReveal>\n * ```\n */\nexport const BlurReveal = ({\n  children,\n  className,\n  delay = 0,\n  ref,\n  style,\n  ...props\n}: BlurRevealProps & { ref?: React.Ref<HTMLDivElement> }) => {\n  const reduced = usePrefersReducedMotion();\n  const [elementRef, inView] = useInView();\n  const revealed = reduced || inView;\n  const setReferences = (node: HTMLDivElement | null): void => {\n    elementRef.current = node;\n    if (typeof ref === \"function\") {\n      ref(node);\n    } else if (ref) {\n      ref.current = node;\n    }\n  };\n\n  return (\n    <div\n      className={cn(\n        \"transition-all duration-700\",\n        revealed\n          ? \"opacity-100 [filter:blur(0)]\"\n          : \"opacity-0 [filter:blur(12px)]\",\n        className,\n      )}\n      ref={setReferences}\n      style={{ transitionDelay: `${delay}ms`, ...style }}\n      {...props}\n    >\n      {children}\n    </div>\n  );\n};\nBlurReveal.displayName = \"BlurReveal\";\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component",
  "version": "0.2.1",
  "stability": "stable"
}
