Responsive and modern CSS

Transitions, animations, and motion

Add purposeful state transitions and keyframe animation while respecting reduced-motion preferences and avoiding costly layout changes.

Motion in CSS comes in two flavors. A transition animates a property from its old value to its new one when it changes. An animation runs a sequence of keyframes you define, on its own schedule, optionally forever.

Transitions

A button that lifts slightly on hover. Without a transition, it jumps. With one, it glides over 150 milliseconds:

.button {
  transition: transform 150ms ease, background-color 150ms ease;
}

.button:hover {
  transform: translateY(-2px);
}

The transition goes on the base state, not on :hover. That way it plays on the way in and on the way out. Each entry names a property, a duration, and a timing function.

Name the properties one by one. transition: all is tempting but it also animates changes you never planned for, like a width change from a media query, and those look broken. List what you mean.

Animations

For anything with more than two steps, or that repeats, use @keyframes. A spinner:

@keyframes spin {
  to { transform: rotate(1turn); }
}

Then apply it with animation: spin 1s linear infinite. The to keyframe is the end state, and the browser fills in the start from the element’s current style.

What to animate

transform and opacity are the safe choices. The browser can usually animate them without recalculating the page layout, so they stay smooth. Animating width, height, top, or margin forces layout and paint on every frame, and on a busy page it stutters.

If a big animation feels slow, record it in the Performance panel. Purple layout blocks on every frame tell you which property to swap for a transform.

Motion should explain, not decorate

Good motion tells the user what changed. The menu slid down from the button, so it belongs to the button. Bad motion makes them wait, or makes them dizzy. Large zooms, parallax, and anything that never stops can cause real discomfort for people with vestibular disorders.

Never make animation the only signal. If a form saved, say so with text, not only with a green flash.

Respect reduced motion

Operating systems have a “reduce motion” setting, and CSS can read it:

@media (prefers-reduced-motion: reduce) {
  .button {
    transition: background-color 0s;
  }

  .progress-animation {
    animation: none;
  }
}

The button keeps its color change but loses the lift. The progress animation stops. reduce means reduce or replace non-essential motion, not necessarily remove everything. I prefer targeted rules like these over a blanket * { animation: none }, because a global rule can also kill an animation that JavaScript is waiting for with animationend, and then the component hangs.

Try it on the course page: emulate reduced motion in the DevTools Rendering panel and use the page with only the keyboard. Every state change should still be obvious with the motion gone. Then turn motion back on and record a performance trace while hovering the buttons, checking for layout work on each frame.

Lesson completed