{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"split-text","type":"registry:ui","title":"Split Text","description":"Letters that spring into place one by one when the text scrolls into view.","dependencies":["motion"],"registryDependencies":["https://ui.mekaigames.com/r/theme.json"],"files":[{"path":"components/ui/split-text.tsx","type":"registry:ui","content":"\"use client\";\n\nimport { motion, useInView, useReducedMotion } from \"motion/react\";\nimport { Fragment, useRef } from \"react\";\n\ntype SplitTextProps = {\n  text: string;\n  /** Seconds between one letter and the next. */\n  stagger?: number;\n  /** Where the letters come from. */\n  from?: \"bottom\" | \"top\";\n  /** Play only the first time the text enters the viewport. */\n  once?: boolean;\n  className?: string;\n};\n\n/** Letters that spring into place one by one when the text scrolls into view. */\nexport function SplitText({ text, stagger = 0.035, from = \"bottom\", once = true, className = \"\" }: SplitTextProps) {\n  const ref = useRef<HTMLSpanElement>(null);\n  const inView = useInView(ref, { once, amount: 0.4 });\n  const reduce = useReducedMotion();\n\n  const words = text\n    .split(/\\s+/)\n    .filter(Boolean)\n    .map((word) => Array.from(word));\n  // Index of each word's first letter, so the stagger runs across the whole line.\n  const starts: number[] = [];\n  let total = 0;\n  for (const letters of words) {\n    starts.push(total);\n    total += letters.length;\n  }\n\n  const sign = from === \"bottom\" ? 1 : -1;\n  const hidden = { opacity: 0, y: `${0.75 * sign}em`, rotate: 14 * sign };\n  const shown = { opacity: 1, y: \"0em\", rotate: 0 };\n\n  return (\n    <span ref={ref} className={className}>\n      <span className=\"sr-only\">{text}</span>\n      <span aria-hidden=\"true\">\n        {words.map((letters, w) => (\n          <Fragment key={w}>\n            {w > 0 ? \" \" : null}\n            <span className=\"inline-block whitespace-nowrap\">\n              {letters.map((char, i) => {\n                const d = (starts[w] + i) * stagger;\n                return (\n                  <motion.span\n                    key={i}\n                    className=\"inline-block\"\n                    style={{ transformOrigin: from === \"bottom\" ? \"0% 100%\" : \"0% 0%\" }}\n                    initial={reduce ? false : hidden}\n                    animate={reduce || inView ? shown : hidden}\n                    transition={\n                      reduce\n                        ? { duration: 0 }\n                        : inView\n                          ? {\n                              type: \"spring\",\n                              stiffness: 420,\n                              damping: 19,\n                              mass: 0.8,\n                              delay: d,\n                              opacity: { duration: 0.25, delay: d },\n                            }\n                          : { duration: 0.2 }\n                    }\n                  >\n                    {char}\n                  </motion.span>\n                );\n              })}\n            </span>\n          </Fragment>\n        ))}\n      </span>\n    </span>\n  );\n}\n"}]}