Motion
Motion serves clarity, not decoration. Six durations, five easings, six named choreographies. Editorial pacing: short, settled, never bouncy. The system honours prefers-reduced-motion automatically – every transition collapses to 1ms and every distance to 0px when the reader asks for stillness.
Durations
Anchored to perceived response thresholds. 80 ms reads as instant feedback. 220 ms is the default reveal. Anything beyond 480 ms is reserved for editorial moments and route changes. Hover any tile to play.
Easings
Each curve carries an intent. Standard handles two-way state changes. Entrance decelerates as a thing arrives. Exit accelerates as it leaves. Emphasized is the editorial curve – slow start, fast finish – for hero reveals and route transitions. Linear is for indeterminate progress only. Bouncy and elastic curves are not part of the system.
Semantic motion
Components consume semantic aliases – never the primitives. Each alias composes a duration with an easing for a specific intent. Re-tune the system in one place. Hover any card to play.
Distance
Translate offsets for entrance and exit slides. Match distance to duration: a 4 px state shift travels in 150 ms; a 32 px toast travels in 220 ms. Larger distances need more time, but never invent values between these four.
Choreography
Stagger composes a list reveal so items arrive in sequence, not at once. Three offsets handle every case. Use tight for dense menus, default for editorial lists, loose for hero galleries. Hover the list to replay.
Reduced motion
Roughly one in three desktop visitors has prefers-reduced-motion: reduce set – often for vestibular reasons. The token system honours that automatically. Every duration collapses to 1 ms; every distance collapses to 0 px. The state still changes – the journey to it does not.
--es-duration-base: 220ms --es-duration-slow: 320ms --es-distance-md: 16px --es-distance-lg: 32px --es-stagger-default: 50ms
--es-duration-base: 1ms --es-duration-slow: 1ms --es-distance-md: 0px --es-distance-lg: 0px --es-stagger-default: 0ms
The contract for every component is the same: consume the tokens, never hard-code a duration or a distance, and the reduced-motion case takes care of itself.
Usage rules
/* 150ms · standard */
Hover, focus, and press use the state token. Two-way moves want a symmetric curve.
A blanket transition makes hovers sluggish and modals rushed. Each move picks its own duration.
/* 220ms · entrance curve */
Things arriving on screen ease out – fast in, slow to settle. Reads as “landing”.
Linear arrivals feel mechanical. Reserve linear for indeterminate progress.
/* 150ms · exit curve */
Things leaving ease in – slow to start, fast to clear. Lower duration than the entrance.
Overshoot reads as playful. Editorial brands are settled – pick from the five sanctioned curves.
opacity: 0 → 1;
Both run on the GPU. Animating top or height repaints the layout and stutters.
Body copy stays still. Reveal the container; never animate the words inside it.
.card { transition: var(--es-motion-reveal); }Consume the semantic tokens. The reduced-motion override is built in – no extra media query needed.
infinite alternate; }
Anything past 480 ms is editorial – it plays once, on user input. Never on a loop.
Tokens to ship
The full reference. Components consume the semantic aliases; the primitives sit beneath, available for one-off composition.