@keyframes, Beyond the Basics
Multi-step timelines, animation-fill-mode, staggering with delays, and steps() for sprite-style motion.
You know @keyframes name { from {} to {} }. The craft is in the properties that surround it.
Percentage stops build a timeline
@keyframes pop {
0% { transform: scale(0); opacity: 0; }
70% { transform: scale(1.1); opacity: 1; } /* overshoot */
100% { transform: scale(1); }
}The properties that make it usable
animation-fill-mode: forwards— keep the final frame's styles after it ends, instead of snapping back. This one trips up everyone.animation-delay— stagger a group by giving each item a slightly larger delay for a cascading reveal.animation-iteration-count— a number, orinfinite.animation-direction: alternate— play forward then backward, so a loop breathes instead of resetting.animation-timing-function: steps(n)— jump in discrete steps for sprite sheets and typewriter effects.
Set them all at once with the animation shorthand once you're comfortable with the order.
Example
Loading editorβ¦
Press Run to see the result.
When to use it
- A developer uses animation-fill-mode: forwards to keep a fade-in element at full opacity after the animation completes.
- A developer staggers list item animations with animation-delay: calc(var(--i) * 0.1s) so items animate in one after another.
- A designer uses steps(8) for a sprite-based walking animation so the sprite advances one frame at a time instead of interpolating.
More examples
fill-mode and delay staggering
Stagger each list item using a CSS custom property as a delay multiplier and forwards fill-mode to keep the end state.
@keyframes fade-up {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.list-item {
animation: fade-up 0.4s ease forwards;
animation-delay: calc(var(--i, 0) * 80ms);
opacity: 0; /* starting state before animation */
}Multi-step @keyframes timeline
Uses three percentage stops to change both width and color, simulating a progress bar moving through status phases.
@keyframes progress {
0% { width: 0; background-color: #2965f1; }
60% { width: 60%; background-color: #2965f1; }
80% { width: 80%; background-color: #f39c12; }
100% { width: 90%; background-color: #c0392b; }
}
.progress-bar {
height: 8px;
animation: progress 4s ease-in-out forwards;
}steps() for sprite animation
Uses steps(8) to jump through eight sprite frames discretely, creating frame-by-frame sprite animation without interpolation.
@keyframes run {
to { background-position: -512px 0; }
}
.sprite {
width: 64px;
height: 64px;
background-image: url('runner-sprite.png');
animation: run 0.8s steps(8) infinite;
}
Discussion