TextEffect
Render any text with a reveal: a split-flap board, a terminal, a decode, a blur-in or rolling reels.
Use it wherever a short piece of text arrives or changes and the change is worth seeing: a hero headline, a stat, a status line, a departures row. Pick the effect by what the text is. A board or a code reads well as split-flap or roll, a command or log line as terminal, a technical label as decode, and a headline or sentence as blur-in. Keep it to a few words or one sentence, and don't animate text people need to read at once, like an error message.
Installation
npm i @youj/motionFor an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.
Usage
import { TextEffect } from '@youj/motion'
export function Hero() {
return <TextEffect as="h1" text="Interfaces that move with intent" />
}It plays when half of it is on screen (inView; inView={false} plays on mount) and again whenever text changes, from the text it showed to the new one. The final text holds its box from the first frame, so nothing around it moves.
Examples
Effects
effect picks one of five:
blur-in, the default: word by word, each sharpening out of a blur as it rises a little.split-flap: each character on a tile that flips through its alphabet to land, top half then bottom, across a hinge.terminal: typed out a keystroke at a time behind an optionalprompt, with a block cursor that blinks while it waits.decode: random glyphs resolving into place. This is ScrambleText, with TextEffect's box and timing.roll: each character on a reel that rolls up to it, like a slot or an odometer.
split-flap and roll pass through the target's own digits or letters (A–Z, a–z or 0–9), or through glyphs when you set one. A space or punctuation mark changes in one step, and a character that stays the same doesn't move.
Departures board
Each cell of a board is its own split-flap. Pad each field to a fixed number of characters (padEnd) so the board never changes size, and a new page flips only the characters that changed.
The tiles are bg-well with your text color; style them through [&_[data-yj-flap]]:bg-… on a parent.
Status line
With erase, a terminal line deletes back to what the old and new text share before it types the rest, so deploy: building becomes deploy: ready by deleting building only. Style the prompt through [&_[data-yj-prompt]].
Reference
Propstext, effect, as, inView… (11)
Props
| Prop | Type | |
|---|---|---|
textRequired | stringThe text. Changing it plays the effect from the old text to the new one. | – |
effect | TextEffectName'split-flap''terminal''decode''blur-in''roll'How the text arrives. default 'blur-in' | 'blur-in' |
as | ElementTypedefault 'span' | 'span' |
inView | InViewWhen the first play starts: | – |
duration | DurationToken'instant''fast''base''slow''deliberate'One unit's motion: a flap's flip (split-flap, default 'fast'), a reel's roll (roll, 'deliberate'), a word's blur-in ('slow') or the whole decode ('deliberate'). Terminal doesn't use it. | – |
stagger | StaggerToken'tight''base''loose'The gap between units: characters (split-flap 'base', roll 'tight'),
words (blur-in 'loose') or keystrokes (terminal 'loose', varied per key).
A long text is compressed to land within the | – |
glyphs | stringThe alphabet decode draws noise from, and the one split-flap and roll pass through. Default: per character, its own digits, upper- or lower-case letters. | – |
prompt | stringTerminal: a prefix shown before the text and never typed, such as | – |
cursor | booleanTerminal: the block cursor. default true | true |
erase | booleanTerminal: on a text change, delete back to the shared start before typing the new text. default false | false |
onComplete | () => voidCalled when a play lands on the text (at once under reduced motion). | – |
Other props pass through: extends HTMLAttributes<HTMLElement>. | ||
MotionPer effect · reduced motion
Motion
duration is one unit's motion and stagger the gap between units. Each effect has its own defaults:
| Moment | Tokens | ||
|---|---|---|---|
| Split-flap flipeach flip; up to six per character, characters staggered | duration.faststagger.base140ms · 40ms step | 140ms40ms step | |
| Rolleach reel, characters staggered | duration.deliberateease.enterstagger.tight560ms · cubic-bezier(0.16, 1, 0.3, 1) · 24ms step | 560mscubic-bezier(0.16, 1, 0.3, 1)24ms step | |
| Blur-ineach word: blur, rise and fade, words staggered | duration.slowease.enterstagger.loose340ms · cubic-bezier(0.16, 1, 0.3, 1) · 72ms step | 340mscubic-bezier(0.16, 1, 0.3, 1)72ms step | |
| Terminal keystrokeuneven per key, longer after punctuation; erase deletes at twice the speed | stagger.loose72ms step | 72ms step | |
| Decodethe whole decode | duration.deliberate560ms | 560ms |
Reduced motion
The final text shows at once, and new text swaps in place. The cursor holds still.
A long text keeps its stagger within the deliberate token, so twenty tiles land about as soon as five. Every effect runs on one animation frame loop per instance and moves only transforms, opacity and filter; the cursor blinks only while it's on screen.
Reduced motion
The text renders as it is, with no flips, typing, glyphs, blur or reels, and onComplete is called at once. A board shows its tiles and a terminal its prompt and a still cursor, so the look stays.
AccessibilityARIA, selection, direction
Accessibility
- The text is rendered once in a visually hidden span, and the animated layer is
aria-hidden. A screen reader reads the text, never the tiles, keystrokes or noise. - To announce a change, put TextEffect inside a live region such as
role="status", as the status line above does. - At rest the visible text is plain text you can select and copy; the hidden copy is left out of the selection.
blur-inandterminalfollow the reading direction, so a right-to-left line types and sharpens from the right.split-flapandrollanimate each character on its own tile or reel, which breaks the joining of connected scripts such as Arabic: useblur-inorterminalthere.data-stateon the root isidle,playingordone, anddata-yj-effectnames the effect.