{
  "name": "scroll-text",
  "type": "registry:component",
  "dependencies": [
    "motion"
  ],
  "files": [
    {
      "type": "registry:component",
      "content": "\"use client\";\n\n/**\n * @author: @dorianbaffier\n * @description: Scroll Text\n * @version: 1.0.0\n * @date: 2025-06-26\n * @license: MIT\n * @website: https://kokonutui.com\n * @github: https://github.com/kokonut-labs/kokonutui\n */\n\nimport { motion, type Variants } from \"motion/react\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\n\ninterface ScrollTextProps {\n  texts?: string[];\n  className?: string;\n}\n\nexport default function ScrollText({\n  texts = [\n    \"TailwindCSS\",\n    \"Kokonut UI\",\n    \"shadcn/ui\",\n    \"Next.js\",\n    \"Vercel\",\n    \"Motion\",\n    \"React\",\n    \"Resend\",\n    \"TypeScript\",\n    \"Fumadocs\",\n    \"Supabase\",\n    \"Vercel\",\n  ],\n  className,\n}: ScrollTextProps) {\n  const [activeIndex, setActiveIndex] = useState(0);\n  const observerRef = useRef<IntersectionObserver | null>(null);\n  const itemsRef = useRef<(HTMLDivElement | null)[]>([]);\n  const containerRef = useRef<HTMLDivElement>(null);\n\n  // Scroll to top on mount\n  useEffect(() => {\n    if (containerRef.current) {\n      containerRef.current.scrollTop = 0;\n    }\n  }, []);\n\n  const handleIntersection = (entries: IntersectionObserverEntry[]) => {\n    entries.forEach((entry) => {\n      if (entry.isIntersecting) {\n        const index = itemsRef.current.findIndex(\n          (item) => item === entry.target\n        );\n        setActiveIndex(index);\n      }\n    });\n  };\n\n  // Setup intersection observer\n  const setupObserver = (element: HTMLDivElement | null, index: number) => {\n    if (element && !itemsRef.current[index]) {\n      itemsRef.current[index] = element;\n\n      if (!observerRef.current) {\n        observerRef.current = new IntersectionObserver(handleIntersection, {\n          threshold: 0.7,\n          root: containerRef.current,\n          rootMargin: \"-45% 0px -45% 0px\",\n        });\n      }\n\n      observerRef.current.observe(element);\n    }\n  };\n\n  // Animation variants for the reveal effect\n  const containerVariants: Variants = {\n    hidden: { opacity: 0 },\n    visible: {\n      opacity: 1,\n      transition: {\n        staggerChildren: 0.1,\n      },\n    },\n  };\n\n  const itemVariants: Variants = {\n    hidden: (index: number) => ({\n      opacity: 0,\n      x: index % 2 === 0 ? -100 : 100,\n      rotate: index % 2 === 0 ? -10 : 10,\n    }),\n    visible: {\n      opacity: 1,\n      x: 0,\n      rotate: 0,\n      transition: {\n        type: \"spring\",\n        stiffness: 100,\n        damping: 15,\n        duration: 0.5,\n      },\n    },\n  };\n\n  return (\n    <div className={cn(\"mx-auto w-full max-w-3xl\", className)}>\n      <div\n        className={cn(\n          \"scrollbar-none h-[300px] overflow-y-auto\",\n          \"relative flex flex-col items-center\",\n          \"[-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\"\n        )}\n        ref={containerRef}\n      >\n        <div className=\"h-[150px]\" />\n        <motion.div\n          animate=\"visible\"\n          className=\"flex w-full flex-col items-center\"\n          initial=\"hidden\"\n          variants={containerVariants}\n        >\n          {texts.map((text, index) => (\n            <motion.div\n              className={cn(\n                \"whitespace-nowrap px-4 py-8 font-bold text-5xl\",\n                \"transition-colors duration-300\",\n                activeIndex === index\n                  ? \"text-black dark:text-white\"\n                  : \"text-neutral-500/50 dark:text-neutral-600\"\n              )}\n              custom={index}\n              initial=\"hidden\"\n              key={text}\n              ref={(el) => setupObserver(el, index)}\n              variants={itemVariants}\n              viewport={{\n                once: false,\n                margin: \"-20% 0px -20% 0px\",\n              }}\n              whileInView=\"visible\"\n            >\n              {text}\n            </motion.div>\n          ))}\n        </motion.div>\n        <div className=\"h-[150px]\" />\n      </div>\n    </div>\n  );\n}\n",
      "path": "/components/kokonutui/scroll-text.tsx",
      "target": "components/kokonutui/scroll-text.tsx"
    }
  ]
}