Weighted Easing
A staggered reveal with its timeline exposed as a scrubbable value, so stagger stops being a feeling and becomes a number.
Achilles: Supreme. Highest learning value in the canon. Used sparingly.
Exhibit case
Why it matters
Motion is normally reviewed by watching it, which is the worst possible way to review it. A reveal runs in 900ms, everyone nods, and nobody can say whether the stagger is 60ms or 140ms — so the next component gets a different number and the interface accumulates a dozen unrelated timing systems.
This study makes the timeline a value in state rather than a transition hidden inside CSS. Because t is data, the reveal can be replayed, paused, and dragged by hand, and the numbers underneath it are printed next to it. That single architectural choice converts motion from something you have an opinion about into something you can measure and defend.
Key techniques
requestAnimationFramedriving a normalisedtfrom 0 to 1, with progress derived from elapsed time rather than accumulated per-frame deltas — so the timeline is correct on a dropped frame instead of drifting.- The house curve,
1 − (1−t)^3.4: strongly front-loaded, almost no overshoot. Fast departure, long settle, no bounce. - Per-line offset computed from a single
STAGGERconstant, with each line's local progress remapped so the last line still finishes exactly att = 1. - Masked lines (
overflow: hidden) withtranslate3don the inner text, so the reveal is a physical uncovering rather than a fade. - Opacity floored at
0.15instead of0, which keeps the text's presence — the eye reads the composition before the animation finishes. - Reduced motion checked at play time, not at mount: the timeline jumps straight to its end state and the layout is never mid-animation.
What to steal
The transferable part.Exposing `t`. Any reveal worth shipping is worth scrubbing. Lifting progress into state costs a useState and a rAF loop, and it makes timing reviewable by people who are not watching over your shoulder at the right moment.
Elapsed-time progress. (now − start) / duration, clamped. Never t += 0.016. The first is frame-rate independent; the second silently runs slow on a loaded main thread and fast on a 120Hz display.
One stagger constant. Publish it, reuse it, and remap local progress so the total duration stays fixed as the item count changes. A stagger that extends the total duration per item is how a six-item list becomes a four-second wait.
What not to copy
Context-specific or costly.Do not use this exponent for anything that repeats. 3.4 spends most of its time settling, which is beautiful once and exhausting on a hover state that fires forty times a minute. Short, symmetric curves belong on repeated interactions.
Do not ship the visible scrub control in production. It exists here because this is a study surface — the point is that motion should be inspectable during development, not that users want a slider under your hero.
Inclusion rationale
This is the timing authority for the entire canon. Every other motion entry cites its curve and its stagger from here, and the weighted press in Athena uses the same exponent. It earns Achilles not for visual ambition but for the methodological point underneath it — motion that publishes its own numbers is the difference between a design system and a collection of preferences.
Technical register
Performance
transform and opacity only; no layout property is read or written inside the loop. One rAF loop drives all five lines, so frame cost is constant with respect to item count. The loop cancels itself on completion and in the effect cleanup, so no orphaned frame requests survive a navigation. Total cost is one state update per frame for 1100ms — measurable, bounded, and zero after settle.
Mobile
Line size is clamp(1.5rem, 4vw, 2.75rem), so the reveal reads at 375px without the masks clipping descenders. Translate distance is expressed as 105% of the line's own height rather than a fixed pixel value, which means the reveal scales with the type instead of undershooting on small screens. The scrub input is a native range, which is fully draggable by thumb.
Accessibility
prefersReducedMotion() is consulted at the moment play() is called, and under it the timeline is set to 1 immediately — the content is complete and readable, never suppressed. The scrub input carries aria-label="Timeline position" and is a real <input type="range">, so keyboard arrow control comes for free. Text is never below 0.15 opacity even mid-flight, and no information is conveyed by the motion itself: the reveal orders attention, it does not carry meaning.
Study next