{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "text-reveal", "title": "Text Reveal", "description": "Text that reveals character-by-character on scroll.", "dependencies": [ "framer-motion", "clsx", "tailwind-merge" ], "files": [ { "path": "registry/components/text-reveal.tsx", "content": "\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport { motion, useInView } from \"framer-motion\";\nimport { useEffect, useRef, useState } from \"react\";\n\ninterface TextRevealProps {\n text: string;\n className?: string;\n delay?: number;\n}\n\nexport function TextReveal({ text, className, delay = 0 }: TextRevealProps) {\n const ref = useRef(null);\n const isInView = useInView(ref, { once: true, margin: \"-100px\" });\n const [hasAnimated, setHasAnimated] = useState(false);\n\n useEffect(() => {\n if (isInView && !hasAnimated) {\n setHasAnimated(true);\n }\n }, [isInView, hasAnimated]);\n\n const words = text.split(\" \");\n\n return (\n \n {words.map((word, wordIndex) => (\n \n {word.split(\"\").map((char, charIndex) => {\n const globalIndex =\n words.slice(0, wordIndex).reduce((acc, w) => acc + w.length, 0) +\n charIndex;\n\n return (\n \n {char}\n \n );\n })}\n \n ))}\n \n );\n}\n", "type": "registry:component" } ], "type": "registry:block" }