Home / Articles / Animation & Motion /
CSS transitions from scratch
Smooth state changes with zero JavaScript. Understand every sub-property, pick the right timing, and keep transitions performant.
The transition shorthand
A transition needs at least a property and a duration. The shorthand packs everything into one declaration.
.btn {
background: oklch(55% 0.22 270);
transition: background 0.2s ease, transform 0.2s ease;
}
.btn:hover {
background: oklch(48% 0.25 270);
transform: translateY(-2px);
}
Timing functions
The timing function controls the acceleration curve. ease is the default, but ease-out feels more natural for entrances and ease-in for exits.
/* named keywords */
transition-timing-function: ease-in-out;
/* cubic-bezier for full control */
transition-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
/* step function for sprite-like effects */
transition-timing-function: steps(4, jump-end);
cubic-bezier(0.22, 1, 0.36, 1) for a snappy deceleration that feels responsive and polished.Delay and sequencing
Add a delay to stagger multiple transitions. The fourth value in the shorthand is the delay.
.card {
opacity: 0.8;
box-shadow: 0 1px 2px oklch(0% 0 0 / 0.1);
transition:
opacity 0.15s ease 0s,
box-shadow 0.25s ease 0.05s;
}
.card:hover {
opacity: 1;
box-shadow: 0 8px 24px oklch(0% 0 0 / 0.15);
}
Which properties to transition
Not every CSS property can be interpolated. Stick to properties the browser can animate cheaply on the compositor thread.
- Cheap:
transform,opacity,filter - Moderate:
background-color,color,box-shadow - Expensive:
width,height,top,left— triggers layout
transition: all. It transitions every changed property, including ones that trigger expensive reflows.Transitioning to and from display: none
Traditionally impossible, but modern CSS offers @starting-style and transition-behavior: allow-discrete to handle entry/exit transitions.
.tooltip {
opacity: 0;
display: none;
transition: opacity 0.2s ease, display 0.2s allow-discrete;
}
.tooltip.is-visible {
display: block;
opacity: 1;
@starting-style {
opacity: 0;
}
}
Performance checklist
Follow these rules to keep transitions at 60 fps.
- Transition only
transformandopacitywhen possible. - Use
will-changesparingly — only on elements about to animate. - Keep durations under 400 ms for UI feedback; users perceive longer transitions as sluggish.
- Respect
prefers-reduced-motion— disable or shorten transitions.