What is the difference between CSS transitions and animations?

Transitions animate between two states triggered by a change (e.g. hover). Animations use keyframes to define multi-step sequences that can run automatically.

CSS

Overview

Both `transition` and `animation` move elements smoothly between states. The difference: **transitions** react to a state change (like `:hover`) and go from A to B. **Animations** are self-contained - defined with `@keyframes` and can run automatically, loop, or go through multiple steps.

Key Points

  • •`transition: property duration timing-function delay` - specify what to animate and how.
  • •Transitions need a trigger: `:hover`, `:focus`, class change via JavaScript.
  • •`@keyframes` defines named animation sequences with percentage-based steps.
  • •`animation: name duration timing-function delay iteration-count direction` applies a keyframe animation.
  • •`animation-fill-mode: forwards` keeps the final keyframe state after the animation ends.
  • •Use `transform` and `opacity` for performant animations - they do not cause layout reflow.

Examples

Transition on hover

css
.button {
  background: #3b82f6;
  transform: scale(1);
  transition: background 0.2s ease, transform 0.2s ease;
}
.button:hover {
  background: #1d4ed8;
  transform: scale(1.05);
}

Transition takes effect whenever background or transform changes.

Keyframe animation: fade in

css
@keyframes fadeIn {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

.card {
  animation: fadeIn 0.4s ease forwards;
}

Looping spinner animation

css
@keyframes spin {
  to { transform: rotate(360deg); }
}

.spinner {
  width: 24px;
  height: 24px;
  border: 3px solid #e2e8f0;
  border-top-color: #3b82f6;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

Multi-step keyframe animation

css
@keyframes pulse {
  0%   { transform: scale(1); opacity: 1; }
  50%  { transform: scale(1.1); opacity: 0.7; }
  100% { transform: scale(1); opacity: 1; }
}

.badge { animation: pulse 2s ease-in-out infinite; }

Pro Tip

Stick to animating `transform` and `opacity`. Animating `width`, `height`, `top`, `left`, or `margin` triggers expensive layout recalculations. Use `translate()` instead of `top`/`left` and `scale()` instead of changing dimensions.