Motion UIComponentsComponent

Scramble reveal

Text that churns through random glyphs on scroll-in, then settles character by character.

npx shadcn@latest add @motion/scramble-reveal

API reference

Components

<ScrambleReveal />

The scramble-decode headline mechanic. Renders the accessibility split (a hidden in-flow element owning the name + box, an aria-hidden scrambling overlay), owns the once-per-view activation gate and the three-tier reduced-motion policy, and exposes replay() via ref. Drop it inside your own heading column; constrain its width from the parent to pin the wrapping.

children

string

The copy to scramble into place. A single string; the in-flow accessible element carries it verbatim as the accessible name.

as?

ElementType

The element the accessible (in-flow, opacity-0) copy renders as. Defaults to "span"; pass "h1" (or any heading) when the reveal is a headline so the settled copy carries the right semantics. The visible overlay is always an aria-hidden span.

duration?

number = 1

How long each character stays scrambled before it settles, in seconds - the main "speed" of the reveal. Defaults to 1.

interval?

number = 0.05

Seconds between random glyph switches while a character is scrambling - the flicker rate. Defaults to 0.05.

stagger?

number = 0.02

Per-character start delay, in seconds, applied left to right as a cascade. 0 reveals every character at once. Defaults to 0.02.

charSet?

ScrambleCharSet = "symbols"

Which glyph palette the scramble churns through. Defaults to "symbols".

className?

string

Merged onto BOTH the in-flow accessible element and the aria-hidden scrambling overlay, so their wrapping matches exactly. Set the display treatment here - use a FIXED-WIDTH (font-mono) face so a swapped-in glyph never reflows the line it lands on, and omit text-wrap: balance (which would relayout on every scramble tick).

ref?

Ref<ScrambleRevealHandle>

Imperative handle exposing replay().

Functions

scheduleReplayReset

Schedule the replay reset on Motion's shared render step and return cleanup for unmounts or a newer replay request.

scheduleReplayReset(callback: ReplayFrameCallback, schedule: (callback: ReplayFrameCallback) => void = (next) =>
    frame.render(next), cancel: (callback: ReplayFrameCallback) => void = (next) =>
    cancelFrame(next)): () => void