import type { ComponentPropsWithoutRef, CSSProperties, ElementType, ReactNode } from 'react' import { cn } from '../../lib/cn' export interface GradientTextOwnProps { /** The text. It stays selectable and searchable. */ children: ReactNode /** Two or more colours. They are repeated to make the loop seamless. */ colors?: string[] /** Seconds for one pass. Ignored when `animate` is false. */ duration?: number animate?: boolean /** Angle of the sweep in degrees. */ angle?: number /** A soft coloured halo under the text, for a dark or busy background. */ glow?: boolean /** Merged last, so it wins. */ className?: string } type GradientTextProps = GradientTextOwnProps & { as?: E } & Omit, keyof GradientTextOwnProps | 'as'> /** * Text painted with a gradient that slides across it. * * The gradient is duplicated end to end and the background sized to 200%, so * animating `background-position` from 0 to 200% lands exactly where it * started — the loop has no seam and needs no reverse leg. * * `background-clip: text` makes the glyphs a window onto the gradient, which * means the text is still text: selectable, searchable, and read normally by a * screen reader. The `glow` layer is a blurred, aria-hidden duplicate rather * than a text-shadow, because a shadow on clipped text paints over the fill. */ export function GradientText({ as, children, colors = ['#7fd4ff', 'var(--color-accent-strong)', '#b6f09c', '#7fd4ff'], duration = 5, animate = true, angle = 90, glow = false, className, ...rest }: GradientTextProps) { const Component = (as ?? 'span') as ElementType const stops = colors.join(', ') const paint: CSSProperties = { backgroundImage: `linear-gradient(${angle}deg, ${stops}, ${stops})`, backgroundSize: '200% auto', backgroundPosition: '0% center', animation: animate ? `gradient-pan ${duration}s linear infinite` : undefined, } return ( {glow && ( )} {children} ) }