Text Effects
BlurCascade
Words drift up out of a blur one after another, like a lens finding focus. Starts when scrolled into view.
Download
- React
- Tailwind CSS v4
- motion
components/ui/blur-cascade.tsx
"use client";
import { motion, useReducedMotion } from "motion/react";
type BlurCascadeProps = {
text: string;
className?: string;
/** Seconds between one word and the next. */
stagger?: number;
/** Starting blur, in pixels. */
blur?: number;
/** Play only the first time it scrolls into view. */
once?: boolean;
};
/* BlurCascade: words drift up out of a blur one after another, like a lens finding focus. */
export function BlurCascade({ text, className = "", stagger = 0.08, blur = 12, once = true }: BlurCascadeProps) {
const reduce = useReducedMotion();
if (reduce) return <span className={className}>{text}</span>;
const words = text.split(" ");
return (
<motion.span className={className} initial="hidden" whileInView="show" viewport={{ once, amount: 0.6 }}>
<span className="sr-only">{text}</span>
<span aria-hidden>
{words.map((word, i) => (
<span key={i}>
<motion.span
className="inline-block"
custom={i}
variants={{
hidden: { opacity: 0, y: "0.35em", filter: `blur(${blur}px)` },
show: (n: number) => ({
opacity: 1,
y: 0,
filter: "blur(0px)",
transition: { duration: 0.8, delay: n * stagger, ease: [0.22, 1, 0.36, 1] },
}),
}}
>
{word}
</motion.span>
{i < words.length - 1 && " "}
</span>
))}
</span>
</motion.span>
);
}
1. Install the dependency.
npm i motion2. Paste the Code tab into
components/ui/blur-cascade.tsx. The styles are Tailwind CSS v4 classes.3. Use it. This is the preview above:
app/blur-cascade-demo.tsx
"use client"; import { useState } from "react"; import { BlurCascade } from "@/components/ui/blur-cascade"; export default function BlurCascadeExample() { const [run, setRun] = useState(0); return ( <div className="grid h-full place-items-center bg-[#f4f1ea] p-6"> <div className="max-w-xl text-center"> <BlurCascade key={run} text="Good design brings the important things into focus." className="text-[clamp(24px,4vw,42px)] font-semibold leading-tight tracking-tight text-[#141414]" /> <button type="button" onClick={() => setRun((r) => r + 1)} className="mt-6 block w-full text-xs text-[#141414]/50 hover:text-[#141414]"> Replay </button> </div> </div> ); }
Props
| Name | Type | Default | What it does |
|---|---|---|---|
| text | string | — | Text to show. |
| stagger | number | 0.08 | Seconds between words. |
| blur | number | 12 | Starting blur, in pixels. |
| once | boolean | true | Play only the first time it scrolls into view. |
| className | string | "" | Type styles for the text. |