{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "split-text-effect",
  "type": "registry:ui",
  "title": "Split Text Effect",
  "description": "A split text effect component for Next.js apps with next-themes and Tailwind CSS, supporting system, light, and dark modes.",
  "dependencies": [
    "motion"
  ],
  "files": [
    {
      "path": "registry/bucharitesh/split-text-effect.tsx",
      "content": "'use client';\n\nimport { cn } from '@/lib/utils';\nimport { motion, useSpring, useTransform } from 'motion/react';\nimport * as React from 'react';\n\ninterface CrossProps extends React.HTMLAttributes<HTMLDivElement> {\n  position: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';\n  color?: string;\n}\n\nconst Cross = React.forwardRef<HTMLDivElement, CrossProps>(\n  ({ position, className, color, ...props }, ref) => {\n    const positionClasses = {\n      'top-left': '-top-px -left-px rotate-0',\n      'top-right': '-top-px -right-px rotate-90',\n      'bottom-left': 'bottom-[-2px] -left-px -rotate-90',\n      'bottom-right': 'bottom-[-2px] -right-px -rotate-180',\n    };\n\n    return (\n      <div\n        ref={ref}\n        aria-hidden=\"true\"\n        className={cn(\n          'absolute h-[15px] w-[15px] cursor-pointer',\n          positionClasses[position],\n          className\n        )}\n        data-position={position}\n        {...props}\n      >\n        <div\n          className=\"absolute top-0 left-0 h-px w-[15px]\"\n          style={{ backgroundColor: color }}\n        />\n        <div\n          className=\"absolute bottom-0 left-0 h-[15px] w-px\"\n          style={{ backgroundColor: color }}\n        />\n      </div>\n    );\n  }\n);\nCross.displayName = 'Cross';\n\ninterface SplitTextEffectProps extends React.HTMLAttributes<HTMLDivElement> {\n  text: string | React.ReactNode;\n  fill?: number;\n  accent?: string;\n}\n\nconst SplitTextEffect = React.forwardRef<HTMLDivElement, SplitTextEffectProps>(\n  ({ text, fill = 0.5, accent = '#006efe', className, ...props }, ref) => {\n    const containerRef = React.useRef<HTMLDivElement>(null);\n    const lineRef = React.useRef<HTMLDivElement>(null);\n    const [hasMounted, setHasMounted] = React.useState(false);\n\n    React.useEffect(() => {\n      setHasMounted(true);\n    }, []);\n\n    const smoothY = useSpring(0, {\n      stiffness: 100,\n      damping: 20,\n    });\n\n    React.useEffect(() => {\n      if (!hasMounted || !containerRef.current) return;\n\n      const container = containerRef.current;\n      const height = container.offsetHeight;\n      const initialY = Math.min(\n        Math.max(height * (1 - fill), height * 0.1),\n        height * 0.9\n      );\n\n      smoothY.set(initialY);\n    }, [hasMounted, fill]);\n\n    const handleMouseMove = (e: React.MouseEvent) => {\n      if (!containerRef.current) return;\n      const rect = containerRef.current.getBoundingClientRect();\n      const height = rect.height;\n\n      // Calculate y position and clamp between 20% and 80% of height\n      const rawY = e.clientY - rect.top;\n      const clampedY = Math.min(Math.max(rawY, height * 0.1), height * 0.9);\n      smoothY.set(clampedY);\n    };\n\n    const handleMouseLeave = () => {\n      if (!containerRef.current) return;\n      const height = containerRef.current.offsetHeight;\n      // Reset to initial fill position, but respect the 20%-80% bounds\n      const resetY = Math.min(\n        Math.max(height * (1 - fill), height * 0.1),\n        height * 0.9\n      );\n      smoothY.set(resetY);\n    };\n\n    return (\n      <div\n        ref={containerRef}\n        className={cn(\n          'relative flex h-full w-full items-center justify-center bg-white p-20 text-5xl dark:bg-black',\n          className\n        )}\n        onMouseMove={handleMouseMove}\n        onMouseLeave={handleMouseLeave}\n        {...props}\n      >\n        <Cross position=\"top-left\" color={accent} />\n        <Cross position=\"top-right\" color={accent} />\n        <Cross position=\"bottom-left\" color={accent} />\n        <Cross position=\"bottom-right\" color={accent} />\n\n        <div className=\"z-0 flex h-full w-full items-center justify-center text-black dark:text-white\">\n          {text}\n        </div>\n\n        <motion.div\n          ref={lineRef}\n          aria-hidden=\"true\"\n          className=\"absolute inset-0 z-20 h-1 select-none border-t-white dark:border-t-black\"\n          style={{\n            opacity: 1,\n            y: smoothY,\n            borderTopWidth: '2px',\n            borderBottomWidth: '2px',\n            borderBottomColor: accent,\n          }}\n        />\n\n        <motion.div\n          aria-hidden=\"true\"\n          className=\"pointer-events-none absolute inset-0 bottom-0 left-0 z-2 flex select-none items-center justify-center\"\n          style={{\n            opacity: 1,\n            clipPath: useTransform(\n              smoothY,\n              (value) => `inset(${value}px 0 0 0)`\n            ),\n          }}\n        >\n          <div\n            className=\"absolute inset-0\"\n            style={{\n              background: `linear-gradient(180deg, ${accent} 0, transparent 100%)`,\n            }}\n          />\n          <div\n            className=\"text-white dark:text-black\"\n            style={{\n              textShadow: `-1px -1px 0 ${accent}, 1px -1px 0 ${accent}, -1px 1px 0 ${accent}, 1px 1px 0 ${accent}`,\n            }}\n          >\n            {text}\n          </div>\n        </motion.div>\n      </div>\n    );\n  }\n);\nSplitTextEffect.displayName = 'SplitTextEffect';\n\nexport { SplitTextEffect };\n",
      "type": "registry:ui"
    }
  ]
}