Transitions, keyframes & reduced motion

Motion with care

Lesson 15 of 20 · 11 min · Module 4: Images, themes and motion

Small animations make an interface feel responsive: a button that lifts slightly, a message that slides in. For some people, though, motion causes dizziness or nausea. Good motion is subtle, fast and switched off when people ask for that.

Start this lesson →

Free. No installation: you write and run the code in your browser, and it is checked automatically.

By the end, you’ll be able to…

  • Animate state changes with transitions
  • Play entrance animations with @keyframes
  • Respect prefers-reduced-motion

The idea

A transition animates a change between two states, such as hover. @keyframes defines an animation that animation plays, for example when an element appears. @media (prefers-reduced-motion: reduce) matches when someone has asked their system for less motion, so you can turn animations off. Animating transform and opacity is cheapest for the browser.

.card { transition: transform 150ms ease; }
.card:hover { transform: scale(1.02); }

@media (prefers-reduced-motion: reduce) {
  .card { transition: none; }
}

01Transitions animate changes

A transition says how to move between two values when something changes, such as hover or a class being added. List the properties explicitly instead of transition: all, so unexpected properties never animate.

02Keyframes for entrances

@keyframes describes the steps of an animation. The both fill mode keeps the first frame before the animation starts and the last frame after it ends, so the message does not flash.

@keyframes slide-in {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}
.toast { animation: slide-in 400ms ease-out both; }

03Less motion on request

People can turn on "reduce motion" in their system settings. The media query lets you remove movement for them while keeping the rest of the design identical.

Your turn

1) Give .button transition: background-color 200ms ease, transform 200ms ease, and make .button:hover lift with transform: translateY(-2px). 2) Define @keyframes slide-in from opacity: 0; transform: translateY(12px) to opacity: 1; transform: none, and play it on .toast with animation: slide-in 400ms ease-out both. 3) Inside @media (prefers-reduced-motion: reduce), set .toast { animation: none; } and .button { transition: none; }.

What the checks look for

  • The button animates its background and position
  • Both transitions take 200ms
  • The button lifts on hover
  • A slide-in animation is defined
  • The message plays it
  • The animation lasts 400ms
  • Reduced motion switches the animation off
  • Reduced motion switches the transitions off
Try it in the editor →

Take this with you

Keep motion short, purposeful, and optional.

Go a little further

Replace the slide with a simple fade for reduced-motion users, instead of removing the animation completely.

Learn HTML & CSS Intermediate for free

20 short lessons with real code and instant checks.

See the whole course →