🌐

CSS Transitions vs Animations: When to Use Each

HTML/CSS4 min readUpdated October 2026

Learn when to use CSS transitions and when to use keyframe animations, which properties stay smooth, and how to respect users who prefer reduced motion.

Small bits of motion make an interface feel alive: a button that lifts when you hover, a menu that slides in, a spinner that shows something is loading. CSS can do all of it without JavaScript.

There are two tools, and picking the right one is mostly a question of whether the motion needs a trigger.

The quick rule

  • A transition animates between two states when something changes, like a hover, focus, or a class being added.
  • An animation (with @keyframes) runs on its own and can have many steps, repeat, and loop.

Hover effect? Transition. Spinner or attention pulse? Animation.

Transitions

Add transition to the element's normal state, and tell it which property to animate and how long it should take.

<style>
  .btn {
    padding: 12px 24px;
    border: none;
    border-radius: 8px;
    background: #16a34a;
    color: white;
    font-size: 16px;
    cursor: pointer;
    transition: transform 0.2s ease, box-shadow 0.2s ease;
  }
  .btn:hover {
    transform: translateY(-3px);
    box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
  }
  .btn:active {
    transform: translateY(0);
  }
</style>

<button class="btn">Hover me</button>

The browser fills in all the frames between the normal state and the hover state. When you move the mouse away, it animates back, because the transition rule is on the normal state.

A transition has four parts: the property (transform), the duration (0.2s), the timing function (ease), and an optional delay. Avoid transition: all. It animates properties you didn't intend and can cause surprise jank.

Timing functions: how motion feels

The timing function controls the acceleration.

  • ease starts and ends gently (the default).
  • ease-out starts fast and slows down, which feels natural for things entering the screen.
  • ease-in starts slowly and speeds up, which suits things leaving.
  • linear has constant speed, which is right for spinners and progress bars.

For UI feedback, 150 to 300 milliseconds feels responsive. Over half a second starts to feel slow.

Keyframe animations

When you need steps, loops, or motion that starts by itself, define the frames with @keyframes and apply them with animation.

<style>
  .spinner {
    width: 40px;
    height: 40px;
    border: 5px solid #bbf7d0;
    border-top-color: #16a34a;
    border-radius: 50%;
    animation: spin 0.9s linear infinite;
  }
  @keyframes spin {
    to { transform: rotate(360deg); }
  }
</style>

<div class="spinner"></div>

When you leave out the from frame, the browser uses the element's current style as the starting point. This spinner only defines where it ends up.

Here's a multi-step example, a gentle pulse for drawing attention:

<style>
  .badge {
    display: inline-block;
    padding: 6px 14px;
    border-radius: 999px;
    background: #dc2626;
    color: white;
    font-family: sans-serif;
    animation: pulse 1.6s ease-in-out infinite;
  }
  @keyframes pulse {
    0%, 100% { transform: scale(1); }
    50%      { transform: scale(1.12); }
  }
</style>

<span class="badge">New</span>

Animation properties you'll use

  • animation-duration and animation-timing-function work like transitions.
  • animation-iteration-count can be a number or infinite.
  • animation-direction: alternate plays forward, then backward.
  • animation-delay waits before starting. Use it to stagger items.
  • animation-fill-mode: forwards keeps the final frame after the animation ends. Without it, the element snaps back to its original style.

A fade-in on page load, with staggered items, shows several of these at once:

<style>
  li {
    opacity: 0;
    animation: fade-up 0.5s ease-out forwards;
    font-family: sans-serif;
  }
  li:nth-child(2) { animation-delay: 0.15s; }
  li:nth-child(3) { animation-delay: 0.30s; }

  @keyframes fade-up {
    from { opacity: 0; transform: translateY(12px); }
    to   { opacity: 1; transform: translateY(0); }
  }
</style>

<ul>
  <li>First</li>
  <li>Second</li>
  <li>Third</li>
</ul>

What to animate (and what not to)

Browsers can animate transform and opacity very cheaply, often on the graphics card, so they stay smooth even on slow phones. Animating width, height, top, left, or margin forces the browser to recalculate the layout on every frame, which can stutter.

So prefer:

  • transform: translateX(...) instead of changing left.
  • transform: scale(...) instead of changing width and height.
  • opacity for fades.

If you really need to animate a height, like an accordion, test it on a mid-range phone, not just your laptop.

Respect reduced motion

Some people get dizzy or distracted from lots of movement. Operating systems have a "reduce motion" setting, and CSS can read it:

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

It's a few lines of CSS and makes your site comfortable for more people. Essential feedback still happens. It's just instant.

Mistakes to watch for

  • Putting transition on the hover state only. The motion works going in but snaps going out. Put it on the base rule.
  • Animating everything. Motion should help people understand something, not decorate every element.
  • Infinite animations that never stop. A looping animation next to text makes it hard to read. Let users pause it, or keep it subtle.
  • Forgetting forwards. The element jumps back to where it started when the animation ends.

Key takeaways

  • Transitions respond to changes. Animations run by themselves and can loop.
  • Animate transform and opacity for smooth results.
  • Keep UI motion short, around 150 to 300 milliseconds.
  • Honor prefers-reduced-motion.

Try it yourself

Reading helps, but typing the code is what makes it stick. Open the editor, paste any example from this article, and change it until it breaks.

Open the HTML/CSS editor

Related Articles