Getting Started
Scroll Animations
Text Animations
Hover & Cursor
Sliders & Carousels
Galleries & Grids
Menus & Navigation
Preloaders & Intros
Page Transitions
3D & WebGL
import { BlockTextReveal } from "@/registry/tween/block-text-reveal"
const INK = "#0b0b0c"
const CREAM = "#f1ece1"
const WINE = "#b8121b"
const BONE = "#e8e2d3"
const NIGHT = "#111418"
const sectionBase =
"@container/block relative flex w-full flex-col overflow-hidden"
const minPreview = { minHeight: "var(--preview-h, 100vh)" }
export function BlockTextRevealDemo() {
return (
<div className="w-full" style={{ backgroundColor: CREAM, color: INK }}>
<section
className={`${sectionBase} items-center justify-center px-6 py-24 text-center`}
style={{ ...minPreview, backgroundColor: CREAM }}
>
<BlockTextReveal blockColor={INK}>
<p className="mb-8 text-[clamp(0.7rem,1.4cqi,0.95rem)] font-medium tracking-[0.35em] uppercase opacity-80">
Ferrous — Issue 07, Autumn
</p>
</BlockTextReveal>
<BlockTextReveal blockColor={WINE} stagger={0.1}>
<h1
className="w-[min(92%,18ch)] text-center text-[clamp(2.5rem,12cqi,9rem)] leading-[0.95] font-black [letter-spacing:-0.04em] uppercase"
style={{ color: WINE }}
>
Heavy type, set loose
</h1>
</BlockTextReveal>
<BlockTextReveal blockColor={INK}>
<p className="mt-10 w-[min(80%,42ch)] text-center text-[clamp(0.9rem,1.6cqi,1.05rem)] font-medium tracking-[0.2em] uppercase opacity-70">
Notes from the composing room floor
</p>
</BlockTextReveal>
</section>
<section
className={`${sectionBase} items-start justify-center px-8 py-24 md:px-16`}
style={{ ...minPreview, backgroundColor: BONE }}
>
<div className="grid w-full grid-cols-1 gap-12 md:grid-cols-[1fr_3fr]">
<BlockTextReveal blockColor={WINE}>
<p
className="text-[clamp(1rem,1.8cqi,1.25rem)] font-semibold tracking-[0.25em] uppercase"
style={{ color: WINE }}
>
§ 02 / On Weight
</p>
</BlockTextReveal>
<BlockTextReveal blockColor={INK} stagger={0.13}>
<p className="w-[min(100%,40ch)] text-left text-[clamp(1.5rem,4.2cqi,3rem)] leading-[1.15] font-medium [letter-spacing:-0.015em]">
We set every headline twice: once to be read, once to be felt. The
second pass is slower and heavier, and it is the one our readers
remember.
</p>
</BlockTextReveal>
</div>
</section>
<section
className={`${sectionBase} items-center justify-center px-6 py-24 text-center`}
style={{ ...minPreview, backgroundColor: NIGHT, color: CREAM }}
>
<BlockTextReveal blockColor={WINE}>
<p
className="mb-10 text-[clamp(0.7rem,1.4cqi,0.95rem)] font-medium tracking-[0.4em] uppercase"
style={{ color: WINE }}
>
— Field Note 14
</p>
</BlockTextReveal>
<BlockTextReveal blockColor={CREAM} alternate stagger={0.14}>
<h2
className="w-[min(92%,22ch)] text-center text-[clamp(1.75rem,6.2cqi,4.5rem)] leading-[1.05] font-medium [letter-spacing:-0.025em]"
style={{ color: CREAM }}
>
A page should open like a shutter, one line at a time, never all at
once.
</h2>
</BlockTextReveal>
<div className="mt-12 flex items-center gap-4 opacity-70">
<span className="h-px w-12" style={{ backgroundColor: CREAM }} />
<BlockTextReveal blockColor={CREAM}>
<p
className="text-[clamp(0.7rem,1.3cqi,0.85rem)] font-medium tracking-[0.3em] uppercase"
style={{ color: CREAM }}
>
Set in Lead · Printed in Ghent
</p>
</BlockTextReveal>
<span className="h-px w-12" style={{ backgroundColor: CREAM }} />
</div>
</section>
<section
className={`${sectionBase} items-center justify-center px-6 py-24 text-center`}
style={{ ...minPreview, backgroundColor: CREAM }}
>
<BlockTextReveal blockColor={WINE} stagger={0.11}>
<h1
className="w-[min(92%,20ch)] text-center text-[clamp(2.25rem,11cqi,8rem)] leading-[0.92] font-black [letter-spacing:-0.045em] uppercase"
style={{ color: WINE }}
>
Print it
<br />
like it matters
</h1>
</BlockTextReveal>
<div className="mt-12 flex w-[min(90%,60ch)] flex-col items-center gap-2">
<BlockTextReveal blockColor={INK}>
<p className="text-[clamp(0.8rem,1.4cqi,0.95rem)] font-medium tracking-[0.3em] uppercase opacity-70">
Type · Press · Archive
</p>
</BlockTextReveal>
<BlockTextReveal blockColor={INK}>
<p className="text-[clamp(0.75rem,1.2cqi,0.85rem)] font-medium tracking-[0.5em] uppercase opacity-50">
Ferrous
</p>
</BlockTextReveal>
</div>
</section>
</div>
)
}
import { BlockTextReveal } from "@/components/ui/block-text-reveal"<BlockTextReveal blockColor="#b8121b">
<h1>Motion is part of the message.</h1>
</BlockTextReveal>
<BlockTextReveal blockColor="#000" alternate>
<p>Every line is masked, wiped, and lifted into place.</p>
</BlockTextReveal>
<BlockTextReveal blockColor="#000" animateOnScroll={false}>
<p>Play the reveal immediately on mount.</p>
</BlockTextReveal>The child is split into lines, and each line gets its own block overlay. The block first wipes in to fully cover the line, then wipes back out to expose the text, which simultaneously lifts up by lineLift pixels. Lines fire in sequence with stagger, so the paragraph assembles top to bottom. Set alternate to flip every other line's wipe direction for a woven effect, or disable animateOnScroll to run the whole sequence as soon as the component mounts.
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | Single text element (or wrapper) to animate. |
animateOnScroll | boolean | true | Trigger the reveal on scroll-into-view. false plays immediately. |
delay | number | 0 | Delay (seconds) before the first line starts. |
blockColor | string | "var(--foreground)" | CSS color for the wiping block. |
stagger | number | 0.07 | Per-line stagger (seconds). |
duration | number | 0.42 | Base tween budget (seconds). Cover, reveal, and lift durations derive from this. |
ease | string | "power4.in" | Easing for the wipe-in (cover phase). |
easeOut | string | "expo.out" | Easing for the wipe-out (reveal phase) and the line lift. |
lineLift | number | 10 | How far each line lifts (px) as it becomes visible. Set 0 to disable. |
alternate | boolean | false | If true, alternate lines wipe in from the right instead of the left. |
start | string | "top 90%" | ScrollTrigger start value when animateOnScroll is true. |
scroller | HTMLElement | string | null | — | Optional ScrollTrigger scroller; defaults to the ambient preview scroller. |
className | string | — | Merged onto the cloned child element. |