CSS Transitions
Animate property changes with transition — timing, delay, which properties to animate, and how transitions differ from keyframe animations.
- css
- transitions
A transition interpolates a property from its old computed value to a new one when that value changes — hover, class toggle, media query, JS state. You don’t author a timeline of keyframes; you author what can change and how long the blend takes.
Docs: MDN CSS transitions, transition.
Minimal example
.button {
background: var(--brand);
transform: translateY(0);
transition:
background 150ms ease,
transform 150ms ease;
}
.button:hover {
background: var(--brand-hover);
transform: translateY(-1px);
}
.button:active {
transform: translateY(0);
}
Shorthand: transition: <property> <duration> <easing> <delay>.
/* All animatable properties — convenient, sometimes wasteful */
.card {
transition: all 200ms ease;
}
Prefer listing properties so unexpected changes (e.g. width from content) don’t animate.
Discrete vs interpolable
Not everything transitions smoothly:
- Numbers/colors/lengths/transforms — usually yes
display: none↔block— not a smooth transition by itself (see@starting-style/ WAAPI / class patterns)height: auto— historically painful; moderninterpolate-size/ grid tricks help in newer engines
.panel {
transition: opacity 160ms ease, transform 160ms ease;
opacity: 0;
transform: translateY(0.25rem);
pointer-events: none;
}
.panel.is-open {
opacity: 1;
transform: translateY(0);
pointer-events: auto;
}
Toggle visibility with opacity/transform while keeping the element in the tree for the duration, then set display/inert if needed after transitionend.
Timing functions
transition-timing-function: ease;
transition-timing-function: linear;
transition-timing-function: ease-in-out;
transition-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1);
transition-timing-function: steps(4, end);
UI chrome often uses a slightly custom cubic-bezier for “designed” motion. Spinners and progress segments may want linear or steps.
Delay and staggering
.menu__item {
opacity: 0;
transform: translateY(0.25rem);
transition:
opacity 160ms ease,
transform 160ms ease;
}
.menu.is-open .menu__item {
opacity: 1;
transform: none;
}
.menu.is-open .menu__item:nth-child(1) { transition-delay: 20ms; }
.menu.is-open .menu__item:nth-child(2) { transition-delay: 40ms; }
.menu.is-open .menu__item:nth-child(3) { transition-delay: 60ms; }
Stagger on open; consider zero delay on close so the menu disappears snappily.
Reduced motion
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
transition-duration: 0.01ms !important;
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
}
}
Or surgically disable non-essential transitions. Don’t remove all feedback if color/opacity changes are part of state clarity — but cut spatial motion.
Transitions vs animations
| Transition | Animation | |
|---|---|---|
| Trigger | Property change | Runs when applied |
| Timeline | Implicit A→B | @keyframes |
| Loop | No (retrigger) | infinite easy |
| End state | New computed style | Needs fill-mode sometimes |
Interview out-loud
“Transitions interpolate when a computed style changes. I set property, duration, easing, optional delay. I animate transform and opacity for performance, avoid transition: all in large trees, and respect reduced motion. Keyframe animations are for continuous or multi-step timelines; transitions are for state changes.”
Footguns
- Transitioning layout props on scroll-linked UI.
- Hover-only motion that never appears for keyboard users — pair with
:focus-visible. - Snapping because duration is on the end state only — put
transitionon the base rule. - Listening to
transitionendwithout filteringpropertyNamewhen multiple props run. - Infinite accidental transitions when React re-renders thrash classes.
Related
- CSS animations keyframes
- transform and opacity performance
- prefers-reduced-motion
- Pseudo-classes hover focus focus-visible
Further reading
Related guides
- Accent Color and Form ControlsUse accent-color to theme native checkboxes, radios, ranges, and progress — what it paints, what it skips, and when to custom-style instead.
- Auto-fit vs Auto-fillauto-fit collapses empty tracks; auto-fill keeps them. Use both with minmax for responsive card grids without media-query soup.
- BEM Naming MethodologyBlock, Element, Modifier naming for CSS — how it limits specificity wars, what to name, and when utility or CSS modules beat strict BEM.
- blend-mode Basicsmix-blend-mode vs background-blend-mode — how pixels combine, isolation, and the stacking/readability traps in product UI.
- box-sizing border-boxWhy border-box makes width include padding and border, the global reset pattern, and the layout bugs content-box still causes.