{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "text-reveal",
  "title": "Text Reveal",
  "description": "Scroll-driven word-by-word or character-by-character text reveal using Motion's useScroll and useTransform.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/components/unlumen/text-reveal/index.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { motion, useInView } from \"motion/react\";\n\nimport { cn } from \"@/lib/utils\";\n\nexport interface TextRevealProps {\n  /** The text to animate. */\n  text: string;\n  /**\n   * HTML tag for the container element.\n   * @default \"p\"\n   */\n  as?: keyof React.JSX.IntrinsicElements;\n  /**\n   * Split mode: word-by-word or character-by-character.\n   * @default \"words\"\n   */\n  splitBy?: \"words\" | \"characters\";\n  /**\n   * Delay between each word/character animation in seconds.\n   * @default 0.05\n   */\n  staggerDelay?: number;\n  /**\n   * Duration of each unit's animation in seconds.\n   * @default 0.5\n   */\n  duration?: number;\n  /**\n   * Trigger the animation only once.\n   * @default true\n   */\n  once?: boolean;\n  className?: string;\n}\n\nexport function TextReveal({\n  text,\n  as: Tag = \"p\",\n  splitBy = \"words\",\n  staggerDelay = 0.05,\n  duration = 0.5,\n  once = true,\n  className,\n}: TextRevealProps) {\n  const ref = React.useRef<HTMLElement>(null);\n  const isInView = useInView(ref, { once, margin: \"0px 0px -10% 0px\" });\n\n  const units =\n    splitBy === \"words\"\n      ? text\n          .split(/\\s+/)\n          .map((w, i, arr) => (i < arr.length - 1 ? w + \"\\u00A0\" : w))\n      : text.split(\"\");\n\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  const AnyTag = Tag as any;\n\n  return (\n    <AnyTag\n      ref={ref}\n      className={cn(\"leading-relaxed\", className)}\n      aria-label={text}\n    >\n      {units.map((unit, i) => (\n        <motion.span\n          key={i}\n          initial={{ opacity: 0.1, filter: \"blur(8px)\" }}\n          animate={\n            isInView\n              ? { opacity: 1, filter: \"blur(0px)\" }\n              : { opacity: 0.1, filter: \"blur(8px)\" }\n          }\n          transition={{\n            duration,\n            delay: i * staggerDelay,\n            ease: \"easeOut\",\n          }}\n          style={{ display: \"inline-block\" }}\n          className=\"will-change-[opacity,filter]\"\n        >\n          {unit}\n        </motion.span>\n      ))}\n    </AnyTag>\n  );\n}\n",
      "type": "registry:ui",
      "target": "components/unlumen-ui/text-reveal.tsx"
    }
  ],
  "type": "registry:ui"
}