Duration says how long.
Easing says how.
Every animation in the system is a pair of these two tokens. Five duration tiers, four easing curves, twenty possible combinations — and most production UI only uses six of them. This page is the exact values, the pairing rules, and the CSS that ships them.
01 — Durations
Five tiers. Don't invent a sixth.
The motion budget compresses the entire duration space into five named tokens. Any animation longer than 600ms or shorter than 100ms (other than instant) is almost certainly the wrong choice — the constraint is part of the value.
Instant
--duration-instant
State that should feel native: focus rings, hover tints, checkbox checks.
Any duration here reads as lag. Snap, don't ease.
Quick
--duration-quick
Micro-feedback: button press, toggle flip, tooltip appear.
Fast enough to feel direct; long enough that the eye registers it happened.
Standard
--duration-standard
The default for almost everything: dropdown open, dialog fade, accordion expand.
The sweet spot — visible without being slow. If a duration token is missing, this is the answer.
Considered
--duration-considered
Choreographed transitions: panel slide-in with content fade, page-to-page navigation.
Long enough to coordinate multiple elements; short enough that the user isn't waiting.
Cinematic
--duration-cinematic
Hero animations, onboarding reveals, marketing transitions. Once per session.
Past 400ms, the user starts to feel they're waiting. Earn every millisecond.
Live demo. Five dots race across identical tracks on a synced 4-second cycle. Instant snaps to the end with no transit. Quick arrives almost as fast. By the time cinematic finishes its run, the others have been parked at the finish line for 1.5 seconds. That visible gap is the felt difference between a tier-two action and a tier-five one. (Demo runs at 3× actual speed so 100ms reads as movement, not a teleport.)
02 — Easings
Four curves cover 95% of motion.
Easing is the shape of a duration over time. The horizontal axis is time; the vertical axis is progress (0 = not started, 1 = complete). A straight line means constant velocity. A curve means accelerating or decelerating. Four canonical curves are enough for nearly every interface state change.
Linear
--ease-linearcubic-bezier(0, 0, 1, 1)
Same speed start to finish. Robotic for state changes; correct for continuous motion.
Use for: Loading indicators, progress bars, anything mechanical or repeating.
Ease-in
--ease-incubic-bezier(0.4, 0, 1, 1)
Slow start, fast finish. Reads as an object building momentum to exit.
Use for: Elements leaving the screen — dismissing a toast, closing a dialog, scroll-out.
Ease-out
--ease-outcubic-bezier(0, 0, 0.2, 1)
Fast start, slow finish. The default for entrances: feels confident, settles gently.
Use for: Elements arriving on screen — dropdown opens, modal mounts, content reveals.
Ease-in-out
--ease-in-outcubic-bezier(0.4, 0, 0.2, 1)
Slow start, fast middle, slow finish. The mathematically natural curve for continuous motion.
Use for: Elements moving from one position to another that stay on screen — tab indicator, slider thumb.
Heuristic
Coming in: ease-out. Going out: ease-in. Moving from A to B and staying: ease-in-out. Continuous loop: linear. Internalize this and you almost never need to pick an easing curve again.
03 — In the wild
Each token, the interaction it's for.
The token names are abstract. The interactions aren't. Five real surfaces, each animating at the literal ms its tier prescribes. Hover the cards to inspect; the demos run on loop so you can feel each tier without clicking anything.
Quick · 100ms
--duration-quickButton press
Tactile feedback. Tier-2 actions feel responsive but never lag. Anything slower and the button feels "stuck."
Quick · 100ms
--duration-quickTooltip appear
Triggered by intent (hover, focus). Has to appear fast enough that it feels like a response, not a delay.
Standard · 200ms
--duration-standardDropdown open
The default for opening a panel. Scale-Y from origin, ease-out, 200ms. Used dozens of times per session — has to feel calm.
Considered · 400ms
--duration-consideredModal open
Coordinated arrival: panel translates + scales while backdrop fades. Long enough to read as a deliberate context shift.
Considered · 400ms
--duration-consideredSide panel slide
Drawers, settings panels, filter sheets. The panel travels far enough that 200ms feels jerky and 600ms feels lazy.
Cinematic · 600ms
--duration-cinematicHero reveal
Onboarding, marketing, page-one moments. Once per session — earns the long duration by setting tone, not by gating action.
03b — Side by side
The same modal, three speeds.
Identical content, identical easing curve. Only the duration changes. Watch them cycle and notice which one feels right — and why the wrong one feels "off" in a way you can't name until you see it next to the others.
100ms · too fast
Pops in. Reads as a glitch — the eye doesn't have time to track the entrance.
300ms · just right
Standard tier. The arrival is visible but doesn't make the user wait.
600ms · too slow
Cinematic tier on a routine action. The user feels they're waiting on the UI.
04 — Pairing rules
The seven combinations a system actually ships.
Five durations × four easings = twenty pairings. Production UI uses six or seven of them on repeat. Memorize this table and 90% of motion decisions become muscle memory.
05 — Implementation
One stylesheet. Use the tokens. Never write a number.
Define the tokens once in CSS custom properties. Every component references the token, not a magic number. When the system tunes its motion later — and it will — one variable change updates every transition in the product.
Token definitions
:root {
/* Durations */
--duration-instant: 0ms;
--duration-quick: 100ms;
--duration-standard: 200ms;
--duration-considered: 400ms;
--duration-cinematic: 600ms;
/* Easings */
--ease-linear: cubic-bezier(0, 0, 1, 1);
--ease-in: cubic-bezier(0.4, 0, 1, 1);
--ease-out: cubic-bezier(0, 0, 0.2, 1);
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
}Component usage
.button {
transition:
background-color var(--duration-quick) var(--ease-out),
transform var(--duration-quick) var(--ease-out);
}
.dialog {
transition: opacity var(--duration-considered) var(--ease-out);
}
.dialog[data-closing] {
transition-duration: var(--duration-quick);
transition-timing-function: var(--ease-in);
}Reduced-motion override
@media (prefers-reduced-motion: reduce) {
:root {
--duration-instant: 0ms;
--duration-quick: 0ms;
--duration-standard: 0ms;
--duration-considered: 0ms;
--duration-cinematic: 0ms;
}
}Setting all durations to 0ms in the reduced-motion query collapses every transition to instant. No component override needed — the tokens carry the accessibility decision through the whole system.
06 — Anti-patterns
Three pairings that always feel wrong.
Specific duration/easing combinations that read as “something is off” to users — even users who can't name why. Audit a system, you find these in the first hour.
Linear everywhere
Avoid
transition: all 200ms linearPrefer
transition: all 200ms ease-outLinear feels robotic on state changes because nothing physical moves at a constant velocity. Reserve linear for genuinely continuous motion: loading spinners, progress bars, scroll indicators.
Ease-in on entrances
Avoid
Dropdown slides in with ease-inPrefer
Dropdown slides in with ease-outEase-in starts slow — the user clicks, then waits for the animation to get going. Ease-out is fast at the start, slow at the end: matches how a thing should arrive on screen and settle.
Same duration for enter and exit
Avoid
Modal opens in 300ms AND closes in 300msPrefer
Modal opens in 300ms; closes in 150msEntering an interface is informational — let the user see it land. Exiting is dismissive — they've already moved on. Halve the exit duration to keep the system feeling responsive.
Related
Motion is one of seven foundations.
Duration and easing tokens fit into the broader motion language — what motion is for, how multiple elements coordinate, how to support users who can't see animation. The motion overview ties it together.