Gradient Wave Text
spell-ui/gradient-wave-textFreeComponent
Apple-style animated gradient text with wave effect.
Live preview
live · rendered by the registry
Rendered by spell-ui on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://spell.sh/r/gradient-wave-text.jsonSource
1"use client";23import { useRef, useEffect, useMemo, useCallback, useState } from "react";4import { cn } from "@/lib/utils";56type Align = "left" | "center" | "right";78const defaultColors = ["#8d6869", "#5a8ea6", "#b9c96e", "#c7c571", "#cb706f", "#7e5e5f"];910interface GradientWaveTextProps {11 children?: React.ReactNode;12 align?: Align;13 className?: string;1415 speed?: number;16 paused?: boolean;17 delay?: number;18 repeat?: boolean;19 inView?: boolean;20 once?: boolean;2122 radial?: boolean;23 bottomOffset?: number;24 bandGap?: number;25 bandCount?: number;26 customColors?: string[];2728 onClick?: (e: React.MouseEvent) => void;29 onMouseEnter?: (e: React.MouseEvent) => void;30 onMouseLeave?: (e: React.MouseEvent) => void;3132 ariaLabel?: string;33}343536export function GradientWaveText({37 children,38 align = "center",39 className,4041 speed = 1,42 paused = false,43 delay = 0,44 repeat = false,45 inView = false,46 once = true,4748 radial = true,49 bottomOffset = 20,50 bandGap = 4,51 bandCount = 8,52 customColors,5354 onClick,55 onMouseEnter,56 onMouseLeave,5758 ariaLabel,59}: GradientWaveTextProps) {60 const elRef = useRef<HTMLDivElement | null>(null);61 const rafRef = useRef(0);62 const tRef = useRef(0);63 const cyclesDoneRef = useRef(0);64 const finishedRef = useRef(false);65 const startedRef = useRef(false);66 const startAtRef = useRef(0);67 const hasPlayedRef = useRef(false);6869 const [isInView, setIsInView] = useState(!inView);7071 const cycles = repeat ? 0 : 1;7273 useEffect(() => {74 if (!inView) {75 setIsInView(true);76 return;77 }7879 const node = elRef.current;80 if (!node) return;8182 const observer = new IntersectionObserver(83 (entries) => {84 entries.forEach((entry) => {85 if (entry.isIntersecting) {86 if (once && hasPlayedRef.current) return;87 setIsInView(true);88 hasPlayedRef.current = true;89 } else if (!once) {90 setIsInView(false);91 }92 });93 },94 { threshold: 0.1 }95 );9697 observer.observe(node);98 return () => observer.disconnect();99 }, [inView, once]);100101 const resolvedColors = useMemo(() => {102 return customColors?.length ? customColors : defaultColors;103 }, [customColors]);104105 const stops = useMemo(() => {106 const arr: string[] = [];107 const baseColor = "var(--gradient-wave-base, rgb(29,29,31))";108 arr.push(`${baseColor} calc((var(--gi) + 0) * 1%)`);109 for (let i = 0; i < bandCount && i < resolvedColors.length * 2; i++) {110// … truncated
Files it writes
More from spell-ui
All 33 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Animated Checkboxanimated-checkbox | spell-ui | Components | Free | 1 dep | |
| Animated Gradientanimated-gradient | spell-ui | Components | Free | no deps | |
| Badgebadge | spell-ui | Components | Free | 2 deps | |
| Bars Spinnerbars-spinner | spell-ui | Components | Free | no deps | |
| Blur Revealblur-reveal | spell-ui | Components | Free | 1 dep | |
| Chartchart | spell-ui | Components | Free | no deps | |
| Color Selectorcolor-selector | spell-ui | Components | Free | no deps | |
| Copy Buttoncopy-button | spell-ui | Components | Free | 1 dep |
