Duck Reveal
Sections arrive as the reader gets to them, and headlines assemble a word at a time.
DisplayClient componentmotion
Preview
Read
delay 0ms
Then
delay 80ms
Arrive
delay 160ms
Under reduced motion this paragraph is simply here — final state, no movement, nothing left invisible.
Installation
The CLI writes the source into your project and pulls in whatever it depends on.
$
pnpm dlx shadcn@latest add @duck/duck-revealPulls in @duck/theme. Already installed items are skipped.
Usage
import { DuckReveal, DuckSplitReveal } from "@/components/ui/duck-reveal"The file lands in components/ui/duck-reveal.tsx and belongs to you from that point on. Edit it in place rather than wrapping it.
Props
Everything not listed here is forwarded to the underlying element.
| Prop | Type | Default | Description |
|---|---|---|---|
| delay | number | 0 | Seconds before it starts, for staggering siblings. |
| duration | number | 0.65 | Seconds the move takes. |
| distance | number | 22 | Distance travelled, in px. |
| direction | "up" | "down" | "left" | "right" | "in" | "up" | Which way it comes from. in is opacity only. |
| repeat | boolean | false | Replay on every entry instead of once. |
| text | string | - | On DuckSplitReveal: the line to split. A string, because it has to be split. |
| by | "word" | "char" | "word" | On DuckSplitReveal: piece size. |
| stagger | number | 0.045 | On DuckSplitReveal: seconds between two pieces. |
Rules
What keeps this component from turning into noise.
- Reduced motion renders the final state, not no state. Skipping the animation without setting the end leaves the element invisible for good — that is the bug this component exists to avoid.
- SplitReveal is for a headline. A paragraph split into words is 200 animated elements and a line break that lands somewhere new on every reflow.
- The split pieces are aria-hidden and the whole line is the accessible name, so a screen reader reads a sentence rather than a list of words.
