Menu

CSS Animation: @keyframes, Properties and Examples

CSS animations move an element through the steps you define in @keyframes, on their own and as often as you like. Learn the @keyframes syntax, all eight animation properties, fill-mode and direction, loading indicators, pausing, and how to respect reduced motion.

This page includes runnable editors - edit, run, and see output instantly.

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); }
}
  • from is the same as 0% and to is the same as 100%.
  • 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 to animation-name. It is case sensitive and must not be a keyword like none.

The animation properties

PropertyWhat it setsDefault
animation-namewhich @keyframes to runnone
animation-durationlength of one cycle0s
animation-timing-functionspeed curve between keyframesease
animation-delaywait before the first cycle (negative starts partway through)0s
animation-iteration-countnumber of cycles, or infinite1
animation-directionnormal, reverse, alternate, alternate-reversenormal
animation-fill-modestyles before and after: none, forwards, backwards, bothnone
animation-play-staterunning or pausedrunning

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. With normal that is x = 200 and green; with reverse it ends at x = 0 and blue, because the last keyframe played was from.
  • alternate with a count of 2 goes there and back, so even forwards ends 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:

  1. 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; }
}
  1. 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 is 0s.
  • A name that does not match. animation-name must match the @keyframes name exactly, including case.
  • Jumping back at the end. Add forwards (or both) to keep the last keyframe.
  • Flicker before a delayed animation starts. During animation-delay the element shows its normal styles. backwards (or both) applies the first keyframe during the delay.
  • Restarting an animation by setting the same value. Setting the same animation again 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. Animate opacity and transform instead.

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.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED