CSS animations with @keyframes
@keyframes defines a timeline. You describe the styles an element should have at selected points, and the browser interpolates the frames between them. Unlike a transition, a keyframe animation can contain many stages, repeat without an interaction, and control its state before and after playback.
Loaders, animated gradients, pulsing badges, and typewriter effects all use the same mechanism. This guide covers the syntax, the animation longhands, sequencing, performance, and the motion preferences that production interfaces must respect.
Timeline syntax and the animation shorthand
An animation has two parts: an @keyframes rule that names the timeline, and an animation declaration that attaches it to an element. from and to are aliases for 0% and 100%; percentages let you insert as many intermediate states as the motion needs.
The shorthand commonly follows name duration timing-function delay iteration-count direction fill-mode. A duration is mandatory for visible motion because its initial value is 0s. During debugging, write the longhands first; compress them only after every value is correct.
- animation-duration
- How long one cycle takes. Use
msfor short feedback andsfor longer ambient motion. - animation-delay
- Wait time before playback. A negative delay starts the timeline partway through its first cycle.
- animation-fill-mode
- Controls whether the first or final keyframe applies outside the active playback interval.
@keyframes pulse {
0%, 100% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.18); opacity: 0.58; }
}
.status-dot {
width: 0.875rem;
aspect-ratio: 1;
border-radius: 50%;
background: #60a5fa;
animation: pulse 1.2s ease-in-out infinite;
}Timing functions, direction, and sequencing
ease, linear, steps(), and cubic-bezier() describe progress between keyframes. Use linear for constant rotation, ease-out for responses that should settle naturally, and steps() when frames must switch discretely rather than interpolate.
Several animations can run on one element as comma-separated lists. Each position across the longhands belongs to the animation in the same position, so mismatched lists are difficult to maintain. For repeated staggered items, a custom property keeps the delay close to the item that owns it.
.item {
--order: 0;
animation: enter 420ms cubic-bezier(.2,.8,.2,1) both;
animation-delay: calc(var(--order) * 70ms);
}
@keyframes enter {
from { opacity: 0; transform: translateY(12px); }
to { opacity: 1; transform: translateY(0); }
}Keep animation on the compositor
Animations of transform and opacity can usually run on the compositor without recalculating layout or repainting the entire element. Animating width, height, top, or large blurred shadows can trigger expensive work on every frame. Prefer translate, scale, and pseudo-elements when the same visual result is possible.
will-change is not a universal speed switch. It reserves resources and should be applied only to an element that is about to animate, then removed when the interaction ends. Always test a representative page, because dozens of promoted layers can consume more memory than they save.
Respect reduced-motion preferences
Motion that communicates state may remain, but large travel, parallax, flashing, and endless ambient loops should be reduced when the visitor requests less motion. The media query is a user preference, not a device-performance hint.
Put the full experience in the default rule and provide a calm alternative under prefers-reduced-motion: reduce. Avoid globally setting every duration to an extremely small number; that can break code that listens for animation events.
@media (prefers-reduced-motion: reduce) {
.status-dot { animation: none; }
.item {
animation: fade-in 160ms ease-out both;
transform: none;
}
}Live catalog examples
These production examples use @keyframes. Open any effect to inspect its complete source and experiment in the browser editor.
Browser support
Support notes refer to stable, unprefixed behavior unless a prefix is explicitly listed.
| Feature | Browsers | Note |
|---|---|---|
| @keyframes / animation | All modern browsers | No prefix is needed for current browser versions. |
| animation-composition | Modern Chromium, Firefox, Safari | Check requirements before relying on additive composition. |
| prefers-reduced-motion | All modern browsers | Treat it as an accessibility requirement. |
Common pitfalls
A second animation declaration replaces the first
Combine timelines in one comma-separated declaration instead of writing
animationtwice.The element snaps back after playback
Use
animation-fill-mode: forwardsorbothwhen the final visual state must remain.Keyframe names collide
Names share a global CSS namespace. Prefix reusable component timelines to prevent accidental overrides.
Layout properties are animated
Prefer
transformandopacity; verify unavoidable paint-heavy motion in browser performance tools.
Frequently asked questions
- When should I use transition instead?
- Use a transition for a simple change between two states, usually triggered by hover, focus, or a class. Use keyframes for multi-stage or self-running timelines.
- Can one element run multiple animations?
- Yes. Separate the names and their corresponding values with commas, keeping each list in the same order.
- Why does my animation not move?
- Check that the duration is not 0s, the keyframe name matches exactly, and the animated values can interpolate.
- Are CSS animations always faster than JavaScript?
- No. The property being animated and the amount of browser work matter more than the syntax that starts it.
Related guides
- :hoverInteraction effects with :hover and transitionsDesign responsive hover feedback that also works for keyboards, touch input, and reduced-motion users.
- ::before / ::afterLayering with ::before and ::afterCreate independent decorative layers with predictable positioning, stacking, clipping, and pointer behavior.
- --custom-propertyThemeable effects with CSS custom propertiesTurn repeated values into a clear component API and animate typed custom properties safely with @property.
- perspective / transformPerspective and CSS 3D transformsConstruct stable 3D scenes, preserve depth across children, and avoid flattening, clipping, and unreadable text.
Last updated:
All guides