Motion is a teaching tool. It tells users what happened, where they are, and what they can do next — without adding a single word to the interface. Use these guidelines to make motion that informs, never to entertain.
Why motion matters
A static interface asks the user to do extra work. Did the menu actually open? Did the system register my click? Where did that panel go when I closed it? Motion answers those questions before the user has to ask them. It is not decoration — it is how the interface stays honest about its own state.
But motion is also the easiest thing to over-do. A page that springs, bounces, and shimmers feels alive in the design mock and exhausting in real use. The bar we hold motion to in this system is simple: unobtrusive, brief, and subtle. If you cannot explain in one sentence what an animation is teaching the user, take it out.
Motion has a budget
Treat motion like spacing tokens. Each animation costs the user a few hundred milliseconds of attention. Spend it where it pays the user back — feedback, state changes, navigation — and nowhere else. A page with eight things animating at once has spent the budget on noise.
The four purposes of motion
Every animation in our products should serve exactly one of these. If you cannot pick one, the animation is not earning its place.
Feedback
Motion that confirms the system recognized the user's action. Without it, users wonder whether the click registered. A button that briefly compresses on press, a toast that slides in after a save, a chevron that rotates on accordion open — each one closes the loop the user just opened.
motion.duration.fastmotion.easing.default
State change
Motion that communicates a transition between modes or values — especially when the change is hard to perceive at a glance. A numeric value counting up, an edit icon morphing into a save icon, a row marking itself complete. Same element, new state, the eye keeps track.
motion.duration.normalmotion.easing.default
Spatial navigation
Motion that helps users keep their bearings in an information hierarchy. A panel sliding in from the right, a modal scaling up from the trigger, a drawer pushing content aside — each shows the user where they came from and where they can return.
motion.duration.slowmotion.easing.enter
Signifier
Motion that hints at what the user can do — a list item gently pulsing to suggest it's swipeable, a card peeking over a fold to suggest scroll. Use sparingly; signifiers degrade fast when the interface uses them everywhere.
motion.duration.slowermotion.easing.spring
Principles of effective UI motion
Six rules to apply when designing motion. Hold every animation to them; the rule that would reject it usually wins.
01
Ease, don't lurch
Motion should slow into and out of its endpoints, not start and stop at constant speed. Linear motion feels mechanical because nothing in the physical world moves that way. Use motion.easing.default for general transitions; reach for motion.easing.enter and motion.easing.exit when an element is appearing or disappearing — they're tuned to feel right for each direction.
02
Stagger by importance
When multiple elements appear at once — list items, cards in a grid, fields in a form — animate them in a quick succession rather than all together. The eye follows the last element to settle, so a staggered sequence subtly directs attention. Keep the offset small: 40 to 80ms between siblings is usually right. More than ten elements? Stop staggering and fade the whole group.
03
Transform, don't replace
When an element changes state, morph it into its new form rather than instant-swap. A submit button that smoothly stretches into a progress bar and resolves to a checkmark teaches the user that all three states belong to the same action. Two separate elements popping in and out teach nothing.
04
Layer with intent
When elements stack — modals over content, popovers over panels — use opacity, blur, or scrim to make the relationship unambiguous. Background content should clearly recede; foreground should clearly own the user's attention. The animation that opens the overlay is also the animation that explains the hierarchy.
05
Connect cause to effect
The thing the user clicked should appear connected to the thing that responds. A panel that opens from the menu icon should visibly originate from that icon, not from a random screen edge. Child elements move with their parent, and origins are honored.
06
Earn every millisecond
Motion that takes longer than 500ms is no longer motion — it's a wait. If a transition feels slow, it probably is. The default should be motion.duration.normal (200ms); reach for slower tokens only when the transition covers a large spatial change or carries critical information.
Duration & easing tokens
All motion in our products uses these tokens. Never hardcode a duration in milliseconds or an easing curve in cubic-bezier(...). The tokens are tuned to feel coherent across the product, so two engineers building two features end up with motion that visually agrees.
Duration
Token
Value
When to use
Demo
motion.duration.instant
0ms
Reduced-motion fallback. Effectively disables the transition while preserving the property change.
motion.duration.fast
100ms
Hover states, focus rings, button press feedback, micro-interactions on a single element.
motion.duration.normal
200ms
Default for nearly everything. Tab switches, accordion expand/collapse, toast in/out, value changes.
Complex multi-step animations or signifier nudges. Use sparingly — anything longer is a wait, not motion.
Easing
Easing demos run at motion.duration.slower (500ms) so the shape of each curve is visible. In production, easing is usually paired with a faster duration.
Token
Curve
When to use
Demo
motion.easing.default
General-purpose. The right answer 80% of the time.
motion.easing.enter
Elements appearing — fast at the start, eases into rest. Feels like arrival.
motion.easing.exit
Elements leaving — eases out of rest, fast at the end. Feels like departure.
motion.easing.spring
Playful overshoot. Use for FABs, success confirmations, signifier nudges — never for routine state changes.
Recommended pairings
Situation
Duration
Easing
Hover state on a button or link
motion.duration.fast
motion.easing.default
Tab or accordion toggle
motion.duration.normal
motion.easing.default
Toast or banner appearing
motion.duration.normal
motion.easing.enter
Toast or banner dismissing
motion.duration.normal
motion.easing.exit
Modal or dialog opening
motion.duration.slow
motion.easing.enter
Modal or dialog closing
motion.duration.slow
motion.easing.exit
Drawer or side panel sliding in
motion.duration.slow
motion.easing.enter
FAB expanding to action set
motion.duration.slow
motion.easing.spring
Numeric value counting up
motion.duration.slow
motion.easing.default
Loading skeleton shimmer (looped)
motion.duration.slower
motion.easing.default
Accessibility
The most important rule on this page: always respect prefers-reduced-motion. A non-trivial slice of users — people with vestibular disorders, attention sensitivities, or simply low tolerance for movement on screen — has explicitly asked their device to hold the animation. The system must honor that. WCAG 2.3.3 (Animation from Interactions, Level AAA) is the baseline expectation, not an aspiration.
How we honor it
Wrap every animation in a media query that disables motion when the user has set prefers-reduced-motion: reduce. This pattern already lives in the base styles — component authors don’t need to repeat it, but they do need to ensure custom animations use transition or animation properties (which the query covers), not requestAnimationFrame loops or JS-driven motion (which it does not).
For JS-driven motion, check the user preference explicitly:
const prefersReducedMotion =
typeof window !== 'undefined' &&
window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (prefersReducedMotion) {
// Skip the animation; jump to end state.
} else {
// Run the animation.
}
What “reduced” means in practice
Reduced motion is not “no feedback.” A button still needs to confirm it was clicked; an error still needs to surface. The right substitution depends on the purpose:
Feedback: keep the state change, drop the easing. Replace the slide with an instant color change.
State change: swap the morph for an immediate swap. The information matters more than the transition.
Spatial navigation: replace slide-and-fade with a cross-fade, or skip motion and rely on layout / focus to orient the user.
Signifier: drop the signifier entirely. If the affordance only exists via motion, build a static affordance too.
Vestibular disorders
Large-area motion is the highest-risk pattern for users with vestibular disorders. Parallax backgrounds, full-screen slides, and zoom transitions that fill the viewport can trigger dizziness, nausea, or migraines. Even in non-reduced-motion mode, prefer small, contained motion to full-bleed effects.
Do and don’t
Six paired examples grounded in real situations. Each pair shows what to ship and what to leave on the cutting-room floor.
Hover feedback
DoUse the fast duration with default easing.transition: background-color
var(--motion-duration-fast)
var(--motion-easing-default);
Don’tHardcode a slow ease-in-out — it bypasses tokens and feels sluggish.transition: background-color 0.5s ease-in-out;
List entrance
DoStagger about 5 list items 60ms apart with normal duration and enter easing. The cascade directs attention to the last-settled item.
Don’tStagger 30 list items the same way. The cascade becomes a distraction — cross-fade the whole group instead.
Modal open
DoScale from 96% to 100% and fade in over slow duration with enter easing. Contained motion, clear origin.
Don’tSlide the modal in from outside the viewport with slower duration. Large-area motion for an everyday action sets off vestibular-sensitive users.
Form save confirmation
DoMorph the Save button into a progress indicator, then into a checkmark. Same element, three states — easy to follow.
Don’tHide the button and show a toast that auto-dismisses. The user may miss it — see the toast-for-critical-errors anti-pattern.
Decorative shimmer
DoUse a skeleton shimmer (slower duration, looped) only while content is loading. Stop it the moment content arrives.
Don’tKeep the shimmer running after content loads. It signals "still loading" indefinitely and erodes trust.
Respecting the user
DoRely on the global prefers-reduced-motion rule and test the page in reduced-motion mode before shipping.
Don’tDrive animations via requestAnimationFrame without checking the media query. Those animations sail past the global rule.
Further reading
Nielsen Norman Group, Animation for UX: Purpose, Personality, and Performance — useful background on why UI motion is a feedback tool, not decoration.
Uxcel, 12 Principles of Animation: A Guide to Motion Design — broader survey of animation principles for designers who want a deeper grounding.
WCAG 2.3.3, Animation from Interactions — the conformance criterion behind the accessibility section.
WCAG 2.3.1 and 2.3.2, Three Flashes — flash-rate rules that apply when any element flashes more than three times per second.
The principles in this page stand on their own — they are written for our context and are not numbered against any external source.