import * as React from 'react' import { motion, useScroll, useTransform, type MotionValue } from 'framer-motion' import { useRef } from 'react' export interface MagicTextProps { text: string /** dir of the text — affects word flow */ dir?: 'rtl' | 'ltr' /** inline style applied to the paragraph (font-size, color, line-height…) */ style?: React.CSSProperties className?: string } interface WordProps { children: string progress: MotionValue range: [number, number] } const Word: React.FC = ({ children, progress, range }) => { const opacity = useTransform(progress, range, [0, 1]) return ( {children} {children} ) } /** * Scroll-driven per-word reveal. Each word fades from faint → solid as the * paragraph scrolls through the viewport. Adapted from the HextaUI MagicText * recipe to framer-motion + RTL body text. */ export const MagicText: React.FC = ({ text, dir = 'rtl', style, className }) => { const container = useRef(null) const { scrollYProgress } = useScroll({ target: container, offset: ['start 0.9', 'start 0.35'], }) const words = text.split(' ') return (

{words.map((word, i) => { const start = i / words.length const end = start + 1 / words.length return ( {word} ) })}

) } export interface MagicTextGroupProps { /** ordered list of paragraphs — revealed sequentially, one finishing before the next starts */ paragraphs: string[] dir?: 'rtl' | 'ltr' style?: React.CSSProperties className?: string } /** * Reveals several paragraphs with a SINGLE shared scroll progress. Every word * across all paragraphs gets a global range, so paragraph 1 fully reveals * before paragraph 2 begins (instead of all paragraphs animating at once). */ export const MagicTextGroup: React.FC = ({ paragraphs, dir = 'rtl', style, className }) => { const container = useRef(null) const { scrollYProgress } = useScroll({ target: container, offset: ['start 0.9', 'end 0.4'], }) // tokenize once and assign a continuous global index to every word const wordsPerPara = paragraphs.map((p) => p.split(' ')) const totalWords = wordsPerPara.reduce((n, w) => n + w.length, 0) let globalIndex = 0 return (
{wordsPerPara.map((words, pi) => (

{words.map((word, wi) => { const start = globalIndex / totalWords const end = (globalIndex + 1) / totalWords globalIndex += 1 return ( {word} ) })}

))}
) }