{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "flip-words",
  "title": "Flip Words",
  "description": "Cycles words with a springy blur-flip transition.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "src/components/velora/flip-words.tsx",
      "content": "\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\n\nimport { cn } from \"@/lib/utils\";\n\ninterface FlipWordsProps {\n  words: string[];\n  /** ms each word stays on screen */\n  duration?: number;\n  className?: string;\n}\n\n/**\n * Cycles through words with a blur-flip transition.\n */\nexport function FlipWords({ words, duration = 2600, className }: FlipWordsProps) {\n  const [index, setIndex] = useState(0);\n  const reducedMotion = useReducedMotion();\n  // Paused while hovered or focused (WCAG 2.2.2)\n  const [paused, setPaused] = useState(false);\n\n  // Reduced motion: no cycling, words[0] stays on screen\n  useEffect(() => {\n    if (reducedMotion || paused) return;\n    const interval = setInterval(\n      () => setIndex((i) => (i + 1) % words.length),\n      duration\n    );\n    return () => clearInterval(interval);\n  }, [words.length, duration, reducedMotion, paused]);\n\n  const longest = words.reduce((a, b) => (b.length > a.length ? b : a), \"\");\n\n  // The invisible longest word reserves the cell size so flipping\n  // between words never shifts surrounding layout.\n  return (\n    <span\n      data-slot=\"flip-words\"\n      className={cn(\"inline-grid text-left align-bottom\", className)}\n      onMouseEnter={() => setPaused(true)}\n      onMouseLeave={() => setPaused(false)}\n      onFocus={() => setPaused(true)}\n      onBlur={() => setPaused(false)}\n    >\n      <span className=\"sr-only\">{words.join(\", \")}</span>\n      <span aria-hidden className=\"invisible col-start-1 row-start-1\">\n        {longest}\n      </span>\n      <AnimatePresence mode=\"popLayout\" initial={false}>\n        <motion.span\n          key={words[index]}\n          initial={{ opacity: 0, y: 14, filter: \"blur(6px)\" }}\n          animate={{ opacity: 1, y: 0, filter: \"blur(0px)\" }}\n          exit={{ opacity: 0, y: -14, filter: \"blur(6px)\" }}\n          transition={{ type: \"spring\", stiffness: 240, damping: 26 }}\n          aria-hidden\n          className=\"col-start-1 row-start-1 inline-block\"\n        >\n          {words[index]}\n        </motion.span>\n      </AnimatePresence>\n    </span>\n  );\n}\n",
      "type": "registry:ui",
      "target": "components/velora/flip-words.tsx"
    }
  ],
  "type": "registry:ui"
}