A CSS animation in one example
An animation has two parts: @keyframes, which describes the steps, and the animation property, which applies them to an element with a duration. This card slides and fades in when the page loads:
Edit the code (or change the duration to 2s) and the preview reloads, so you can watch the card come in again. The @media (prefers-reduced-motion: reduce) block replaces the keyframes with a fade only for readers whose system is set to reduce motion.
@keyframes syntax
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.08); }
100% { transform: scale(1); }
}
fromis the same as0%andtois the same as100%.- Add as many percentage steps as you need. Steps with the same styles can share a line:
0%, 100% { ... }. - A property missing from the first or last keyframe is animated from or to the element's own value.
- The name (
pulse) is what you pass toanimation-name. It is case sensitive and must not be a keyword likenone.
The animation properties
| Property | What it sets | Default |
|---|---|---|
animation-name | which @keyframes to run | none |
animation-duration | length of one cycle | 0s |
animation-timing-function | speed curve between keyframes | ease |
animation-delay | wait before the first cycle (negative starts partway through) | 0s |
animation-iteration-count | number of cycles, or infinite | 1 |
animation-direction | normal, reverse, alternate, alternate-reverse | normal |
animation-fill-mode | styles before and after: none, forwards, backwards, both | none |
animation-play-state | running or paused | running |
The shorthand takes them in almost any order. The only rule is that the first time value is the duration and the second is the delay:
.loader {
animation: pulse 1.2s ease-in-out 0.2s infinite alternate both;
/* name duration timing delay count direction fill */
}
direction and fill-mode, live
Choose a direction, a fill mode and a count, then press Play. The box moves 200px to the end of the track. The readout says where it rested after the animation finished:
What to look for:
fill-mode: none(the default): after the last frame the box jumps back to x = 0 and turns amber again.forwards: it stays on the last keyframe. Withnormalthat is x = 200 and green; withreverseit ends at x = 0 and blue, because the last keyframe played wasfrom.alternatewith a count of 2 goes there and back, so evenforwardsends at x = 0.
Infinite loops: a loading indicator
infinite repeats forever, and animation-delay on each child offsets them so they move in turn. Keep loops calm: slow, small, no flashing.
role="status" with an aria-label tells screen reader users that something is loading, which the dots alone do not say. For a spinning circle, see the rotate page.
Pausing and controlling animations
animation-play-state: paused freezes an animation where it is, and running continues from there. Hover the bar below to pause it:
From JavaScript, element.getAnimations() returns the running CSS animations, each with pause(), play(), cancel() and a currentTime you can read or set. The animationend event fires when a non-infinite animation finishes, and animationiteration at the end of each cycle.
Respect reduced motion and never flash
Two accessibility rules apply to every animation:
- Reduced motion. People can ask their system for less motion because movement makes them unwell. The safest pattern is to add motion only when they have not asked for less:
@media (prefers-reduced-motion: no-preference) {
.hero-title { animation: slide-in 0.8s ease-out both; }
}
- No flashing. Content that flashes more than three times in one second can trigger seizures. Avoid fast, high-contrast blinking entirely.
Keep animations smooth
Animate transform and opacity when you can. The browser can apply them without laying out the page again, so they stay smooth even on slow phones. Animating width, height, top, left or margin recalculates layout on every frame. To move an element, use translate() from the transform page.
Common mistakes
- No duration.
animation: fade;does nothing, because the default duration is0s. - A name that does not match.
animation-namemust match the@keyframesname exactly, including case. - Jumping back at the end. Add
forwards(orboth) to keep the last keyframe. - Flicker before a delayed animation starts. During
animation-delaythe element shows its normal styles.backwards(orboth) applies the first keyframe during the delay. - Restarting an animation by setting the same value. Setting the same
animationagain does nothing. Remove it, force a reflow (el.offsetWidth), and set it again, as the Play button above does. - Animating
display. It switches at once. Animateopacityandtransforminstead.
Frequently Asked Questions
How do you make an animation in CSS?
Define the steps with @keyframes name { from { ... } to { ... } }, then apply it to an element with the animation property and a duration: animation: name 1s ease-out;. The duration is required, because its default is 0s.
What does animation-fill-mode: forwards do?
It keeps the styles of the last keyframe after the animation ends. Without it, the element jumps back to its normal styles. backwards applies the first keyframe during the delay, and both does both.
How do I make a CSS animation loop forever?
Set animation-iteration-count: infinite, or add infinite to the shorthand: animation: spin 1s linear infinite;. Add alternate to play it forward and then backward on each cycle.
What is the difference between CSS animation and transition?
A transition animates a property between two values when it changes, usually on hover or a class change. An animation runs on its own as soon as it is applied, can have any number of keyframes, and can repeat, reverse and pause.
How do I pause a CSS animation?
Set animation-play-state: paused on the element, for example on :hover or through a class added by JavaScript. Setting it back to running continues from where it stopped.
How do I turn off animations for users who prefer reduced motion?
Wrap the animation in @media (prefers-reduced-motion: no-preference) { ... }, or remove it inside @media (prefers-reduced-motion: reduce) { ... }. The query follows the reduce motion setting of the user's operating system.