CSS Animations
Using @keyframes for multi-step animations that transitions alone can't express.
2 min de lecture
Transitions handle animating between two states — a start and an end. When you need something more elaborate — several steps, a repeating loop, or an animation that plays automatically without a trigger — @keyframes is the tool.
Defining keyframes
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.spinner {
animation: spin 1s linear infinite;
}@keyframes names a sequence of steps (from/to, equivalent to 0%/100%). The animation shorthand then applies it: spin 1s linear infinite means play the spin keyframes over 1s, at a constant rate, looping forever. Unlike a transition, this animation runs immediately on page load — no hover or class change needed to trigger it.
Multiple steps with percentages
@keyframes pulse {
0% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.05);
opacity: 0.8;
}
100% {
transform: scale(1);
opacity: 1;
}
}
.notification-dot {
animation: pulse 2s ease-in-out infinite;
}Percentages let you define any number of intermediate steps, not just a start and end — here, the element grows slightly and fades partway before returning to normal, producing a "pulsing" attention-grabbing effect useful for a notification badge.
Controlling playback
.toast {
animation: slide-in 0.3s ease-out forwards;
}
@keyframes slide-in {
from {
transform: translateY(-20px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}animation-fill-mode: forwards (bundled into the shorthand here) keeps the element in its final keyframe state after the animation finishes, instead of snapping back to its pre-animation styles. Without forwards, a toast that slides in would revert to opacity: 0 the instant the animation completed — visually disappearing right after appearing.
Other useful longhand properties include animation-delay (wait before starting), animation-direction: alternate (play forward then backward on each iteration, instead of resetting), and animation-iteration-count (a specific number instead of infinite).
Triggering an animation with a class
.card {
opacity: 0;
}
.card.visible {
animation: fade-in 0.4s ease-out forwards;
}
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}A common pattern pairs @keyframes with a class toggled by JavaScript (for example, when an element scrolls into view) — CSS owns the actual animation, while JavaScript only decides when it should start by adding a class. This keeps the animation logic itself out of JavaScript entirely.
When to reach for animation vs. transition
Use a transition when a single property changes in response to a state change (hover, focus, a toggled class) and a simple two-point interpolation is enough. Reach for @keyframes when you need multiple distinct steps, looping, or an animation that should start on its own rather than in response to a state change.