Reveal
Animate content in as it loads or scrolls into view, and out as it leaves, with fades, slides, blur and more.
Use it for content that should arrive rather than appear: a hero's headline, a section as it scrolls in, a panel that opens. One Reveal moves one element. For a group, use Stagger, and for a headline that assembles word by word, TextSplit. Don't reveal what people came to read or act on, like form fields, table rows or navigation. It should be there when they look.
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
Wrap anything. By default it rises into place the first time 30% of it is on screen.
import { Reveal } from '@youj/motion'
export function Feature() {
return (
<Reveal>
<FeatureCard />
</Reveal>
)
}Use inView={false} for content that should arrive as soon as it renders, and as to reveal a heading or list item without an extra wrapper.
<Reveal as="h2" inView={false} className="text-4xl font-semibold">
Motion that lands where you meant it.
</Reveal>Examples
Variants
Eight arrivals share one timing model. rise, fade and scale take the slow duration. The four wipe-* variants and blur-in travel further, so they take deliberate. Everything that travels decelerates hard into place on the enter curve. fade doesn't travel, so it uses standard.
Choreography
delay is a count of stagger steps, not milliseconds. delay={2} waits two delayStep steps. The whole sequence stays on the token grid, so it scales with the Speed control and never drifts out of rhythm.
<Reveal inView={false} delay={0} delayStep="loose">Eyebrow</Reveal>
<Reveal inView={false} delay={1} delayStep="loose">Headline</Reveal>
<Reveal inView={false} delay={2} delayStep="loose">Body</Reveal>For lists, reach for Stagger: it hands each child its cue, and a Reveal inside it takes its timing and delay from the parent.
Exits
Inside AnimatePresence, a Reveal leaves with the fast duration on the exit curve. Exits are shorter than arrivals, because nobody waits for something to leave. Toggle quickly: a re-entry mid-exit retargets from wherever the element is, without a jump.
Reference
Propsas, variant, inView… (12)
Props
| Prop | Type | |
|---|---|---|
as | RevealTag'div''span''section''article''header''footer''li''p''h1''h2''h3''figure'default 'div' | 'div' |
variant | RevealVariant'fade''rise''blur-in''scale''wipe-up''wipe-down''wipe-left''wipe-right'default 'rise' | 'rise' |
inView | InViewWhen it plays: | – |
delay | numberDelay as a multiple of the default 0 | 0 |
delayStep | StaggerToken'tight''base''loose'default 'base' | 'base' |
duration | DurationToken'instant''fast''base''slow''deliberate' | – |
ease | EasingToken'standard''enter''exit''emphasized''linear' | – |
distance | number | stringTravel for default 12 | 12 |
children | ReactNode | – |
Other props pass through: extends HTMLMotionProps<'div'>. | ||
distance, how far rise travels, defaults to 12 (px).
MotionEnter, enter, long travel, fade… · reduced motion
Motion
| Moment | Tokens | ||
|---|---|---|---|
Enterrise, scale | duration.slowease.enter340ms · cubic-bezier(0.16, 1, 0.3, 1) | 340mscubic-bezier(0.16, 1, 0.3, 1) | |
Enter, long travelblur-in, wipe-* | duration.deliberateease.enter560ms · cubic-bezier(0.16, 1, 0.3, 1) | 560mscubic-bezier(0.16, 1, 0.3, 1) | |
| Fadeopacity only | duration.slowease.standard340ms · cubic-bezier(0.2, 0, 0, 1) | 340mscubic-bezier(0.2, 0, 0, 1) | |
| Exit | duration.fastease.exit140ms · cubic-bezier(0.4, 0, 1, 1) | 140mscubic-bezier(0.4, 0, 1, 1) | |
Delay stepdefault delayStep | stagger.base40ms step | 40ms step |
Reduced motion
Renders in its final state: no fade, no travel. Presence exits remove the element immediately.
It animates only opacity, transform, filter and clip-path, so a reveal never triggers layout.
Reduced motion
A Reveal renders in its final state from the first frame: no fade, no travel and no wait. Inside AnimatePresence, it's removed at once. Nothing is hidden behind a scroll position, so the page reads the same either way.
AccessibilityARIA
Accessibility
- The content is in the document from the first render, so assistive technology reads it whether or not it has animated in.
- Reveal adds no roles. Put semantics on the element itself, with
as. - Scroll reveals are one-shot by default (
inView={{ once: true }}). Content that disappears as it scrolls away makes a page feel unstable, so opt out only with a reason.