Text Effects
TextScramble
Letters shuffle through glyphs before settling, then the next word arrives. Screen readers get the plain word.
Download
- React
- Tailwind CSS v4
- motion
components/ui/text-scramble.tsx
"use client";
import { useEffect, useState } from "react";
import { useReducedMotion } from "motion/react";
const GLYPHS = "!<>-_\\/[]{}=+*^?#ABCDEFX01";
type TextScrambleProps = {
/** Words to cycle through. */
words: string[];
className?: string;
/** Milliseconds each word stays. */
interval?: number;
/** Characters shown while a letter is scrambling. */
glyphs?: string;
};
/* TextScramble: letters shuffle through glyphs before settling, then the next word. */
export function TextScramble({ words, className = "", interval = 2600, glyphs = GLYPHS }: TextScrambleProps) {
const reduce = useReducedMotion();
const [index, setIndex] = useState(0);
const target = words[index % words.length] ?? "";
const [text, setText] = useState(target);
useEffect(() => {
if (words.length < 2) return;
const id = window.setInterval(() => {
if (!document.hidden) setIndex((i) => (i + 1) % words.length);
}, interval);
return () => window.clearInterval(id);
}, [interval, words.length]);
useEffect(() => {
if (reduce) return;
let tick = 0;
let frame = 0;
const step = () => {
tick++;
const out = target
.split("")
.map((ch, i) => (ch === " " || tick > i * 3 + 6 ? ch : glyphs[Math.floor(Math.random() * glyphs.length)]))
.join("");
setText(out);
if (out !== target) frame = requestAnimationFrame(step);
};
frame = requestAnimationFrame(step);
return () => cancelAnimationFrame(frame);
}, [target, reduce, glyphs]);
return (
<span className={className}>
<span className="sr-only">{target}</span>
<span aria-hidden>{reduce ? target : text}</span>
</span>
);
}
1. Install the dependency.
npm i motion2. Paste the Code tab into
components/ui/text-scramble.tsx. The styles are Tailwind CSS v4 classes.3. Use it. This is the preview above:
app/text-scramble-demo.tsx
import { TextScramble } from "@/components/ui/text-scramble"; export default function TextScrambleExample() { return ( <div className="grid h-full place-items-center bg-[#e8e4d8]"> <TextScramble words={["DECODE", "RENDER", "DEPLOY", "REPEAT"]} className="font-mono text-[clamp(32px,5vw,56px)] font-bold tracking-[0.12em] text-[#121212]" /> </div> ); }
Props
| Name | Type | Default | What it does |
|---|---|---|---|
| words | string[] | — | Words to cycle through. |
| interval | number | 2600 | Milliseconds each word stays. |
| glyphs | string | !<>-_\/[]{}=+*^?#ABCDEFX01 | Characters shown while a letter scrambles. |
| className | string | "" | Type styles for the text. |