CSS Keyframe Animation Tutorial: @keyframes Explained

CSS animations let you move and change elements over time with zero JavaScript. The whole system is two parts: @keyframes defines what happens, and the animation property controls how it plays. Here’s how to use them.

Step 1: define keyframes

@keyframes lists the states your element passes through. Use from/to for two-step, or percentages for more control:

@keyframes fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes bounce {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-20px); }
}

Step 2: apply the animation

.box {
  animation: fade-in 0.5s ease forwards;
}

That shorthand sets the name, duration, timing-function and fill-mode in one line.

The animation sub-properties

PropertyWhat it does
animation-namewhich @keyframes to use
animation-durationhow long one cycle takes (e.g. 0.5s)
animation-timing-functioneasing: ease, linear, cubic-bezier()
animation-delaywait before starting
animation-iteration-countnumber or infinite
animation-directionnormal, reverse, alternate
animation-fill-modewhat state to hold before/after

fill-mode: the property people forget

Without fill-mode, an element snaps back to its pre-animation state when the animation ends. Use forwards to keep the final keyframe:

.reveal { animation: fade-in 0.5s ease forwards; }

fill-mode: both applies the first keyframe before start and the last after end — usually what you want for entrance effects.

reverse vs alternate

  • reverse plays the keyframes backwards every cycle.
  • alternate plays forward, then backward, then forward… (ping-pong). Combine with infinite for a smooth loop:
.pulse {
  animation: bounce 1s ease-in-out infinite alternate;
}

Staggering multiple elements

Apply the same animation with increasing animation-delay to create a cascade:

.item:nth-child(1) { animation-delay: 0s; }
.item:nth-child(2) { animation-delay: 0.1s; }
.item:nth-child(3) { animation-delay: 0.2s; }

Respect motion preferences

Some users get motion sickness. Gate non-essential animation:

@media (prefers-reduced-motion: no-preference) {
  .box { animation: fade-in 0.5s ease forwards; }
}

Performance tip

Animate transform and opacity whenever possible — they run on the GPU compositor. Animating width, height, top or margin forces layout recalculation and can stutter.

Generate animations visually

Tuning duration, easing and keyframes by hand takes trial and error. The CSS Animation Generator gives you Fade, Slide, Bounce and Pulse presets with live preview, sliders for every property, and copies the full @keyframes block plus the shorthand.

🛠️
Try the CSS Animation Generator

Generate this CSS visually — no coding required. Instant live preview.

Open Tool →