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.