CSS Animations

Multi-step animations with @keyframes: spinners, pulses and entrance effects.

Lesson 46 of 49Advanced14 min read

Transitions animate from state A to state B when something changes. Animations go further. They can start on their own, loop forever and pass through many steps along the way. Loading spinners, pulsing notification dots, bouncing arrows and fade-in effects are all CSS animations.

Animations need two pieces: a set of keyframes that describes the motion, and an animation property that applies those keyframes to an element. Keeping the two separate means you can define a motion once and reuse it on many elements with different speeds and delays.

Step 1: define the keyframes

CSS
@keyframes pulse {
  0%   { transform: scale(1); }
  50%  { transform: scale(1.15); }
  100% { transform: scale(1); }
}

@keyframes gives the animation a name and describes how the element should look at points along its timeline. from and to are shortcuts for 0% and 100%.

If you leave out the 0% keyframe, the animation starts from the element's current style, which is handy for simple fade-ins.

Step 2: apply the animation

CSS
.dot { animation: pulse 1.5s ease-in-out infinite; }
PropertyExampleMeaning
animation-namepulseWhich keyframes to use
animation-duration1.5sLength of one cycle
animation-timing-functionease-in-out, linear, steps(8)The speed curve
animation-delay0.2sHow long to wait before starting
animation-iteration-count3, infiniteHow many times to play
animation-directionalternate, reversePlay backwards on alternate cycles
animation-fill-modeforwards, bothKeep the final state after finishing
animation-play-statepausedPause the animation

Without animation-fill-mode: forwards, an element jumps back to its original style when a one-off animation ends. That's the most common surprise with entrance effects.

Staggering animations

Give several elements the same animation with increasing animation-delay values and they move one after another, like a wave or a "someone is typing" indicator. A delay of 0.1 to 0.2 seconds between items is usually enough to create the effect without feeling slow.

When to use an animation

Good animations have a job. A spinner says "loading", a pulse draws attention to a new message, and a short entrance effect shows where new content appeared. Constant decorative movement, on the other hand, distracts from the content and drains phone batteries. Animate transform and opacity where possible, keep durations short, and ask whether each animation helps the visitor.

CSS Animations Syntax

Animation syntax

@keyframes name {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.el { animation: name 0.5s ease-out 0s 1 forwards; }

CSS Animations Summary

  • @keyframes defines the steps of an animation and gives it a name.
  • The animation property applies keyframes with a duration, timing and repeat count.
  • animation-fill-mode: forwards keeps the final state after the animation ends.
  • Increasing delays create staggered, wave-like effects.
  • Respect prefers-reduced-motion and give every animation a purpose.

CSS Animations Examples

Each example runs in the page. Edit the code and press Run to see what changes.

Example 1

A loading spinner

.spinner {
  width: 48px;
  height: 48px;
  border: 5px solid #e1e6ff;
  border-top-color: #4467ff;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}
ResultEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Lines 4 to 6A circle with one coloured side.
  2. Line 7Run "spin" every 0.8s, at constant speed, forever.
  3. Lines 10 to 12Only the end state is needed. It starts from the current rotation (0deg).
Example 2

Typing indicator with staggered delays

.typing { display: flex; gap: 6px; padding: 14px 18px; background: #f3ede2; border-radius: 20px; width: fit-content; }

.typing span {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #6b6d79;
  animation: bounce 1s ease-in-out infinite;
}

.typing span:nth-child(2) { animation-delay: 0.15s; }
.typing span:nth-child(3) { animation-delay: 0.3s; }

@keyframes bounce {
  0%, 60%, 100% { transform: translateY(0); }
  30% { transform: translateY(-8px); }
}
ResultEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Line 8All three dots share one animation.
  2. Lines 11 to 12Each dot starts a little later, creating the classic "someone is typing" wave.
  3. Line 15Several percentages can share one keyframe.
Example 3

Fade-in entrance that stays

Press Run again to replay it.

.toast {
  display: inline-block;
  padding: 12px 18px;
  background: #15161b;
  color: white;
  border-radius: 12px;
  font-family: sans-serif;
  opacity: 0;
  animation: slide-in 0.5s ease-out 0.3s forwards;
}

@keyframes slide-in {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: translateY(0); }
}
ResultEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Line 8Starts invisible.
  2. Line 9Wait 0.3s, animate for 0.5s, and forwards keeps the final (visible) state.

Common CSS Animations Mistakes

Defining keyframes but never applying them

Don't
@keyframes wiggle { 50% { rotate: 10deg; } }
.bell { animation-duration: 1s; }
Do
@keyframes wiggle { 50% { rotate: 10deg; } }
.bell { animation: wiggle 1s infinite; }

An animation needs at least a name and a duration.

The animation jumps back at the end

Don't
.box { animation: grow 1s; }
@keyframes grow { to { width: 300px; } }
Do
.box { animation: grow 1s forwards; }
@keyframes grow { to { width: 300px; } }

Without forwards, the element returns to its original styles when the animation ends.

CSS Animations in Real Projects

Animation in products

Skeleton loading screens that shimmer, notification badges that pulse, onboarding illustrations, "scroll down" arrows and success ticks that draw themselves are often pure CSS animations. Good teams keep them short and purposeful: motion should explain what's happening, not distract.

CSS Animations Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Fill in the blankseasy

Apply the "spin" animation for 2 seconds, forever.

.logo {
  animation:  2s linear ;
}
Multiple choiceeasy

Which at-rule defines an animation's steps?

Multiple choicemedium

Which value keeps the final keyframe's styles after the animation ends?

Predict the outputmedium

How many times does the bell ring?

.bell { animation: ring 0.5s ease 3 alternate; }
Fix the bugmedium

The heart should pulse forever, but nothing moves. Fix the CSS (keep the name "beat").

.heart {
  display: inline-block;
  font-size: 40px;
  animation: beat;
}

@keyframe beat {
  50% { transform: scale(1.3); }
}
ResultEdit the code, then press Run or Ctrl+Enter
Checks
  • Animation name is beat
  • Has a duration
  • Loops forever
  • @keyframes beat is defined

CSS Animations Quiz

3 questions to check what you learned.

  1. What does infinite mean in an animation?

    • It repeats forever
    • Very slow
    • It never starts
    • It runs once
  2. What's the main difference between a transition and an animation?

    • Animations can't use transform
    • Animations can run on their own, loop and have many steps; transitions only animate a change between two states
    • Transitions are newer
    • There is no difference
  3. How do you make several elements animate one after another?

    • Use different keyframes for each
    • Use animation-order
    • Give each a slightly larger animation-delay
    • It's impossible in CSS

CSS Animations Challenge

Pulsing notification dot

Make .dot pulse:

  1. Define @keyframes pulse that scales to 1.4 and fades to opacity: 0.5 at 50%
  2. Apply it with a duration of 1.2s, ease-in-out, infinite
.bell { position: relative; font-size: 28px; background: none; border: 0; }
.dot { position: absolute; top: 2px; right: 0; width: 10px; height: 10px; border-radius: 50%; background: #e5484d; }
ResultEdit the code, then press Run or Ctrl+Enter
Checks
  • Keyframes "pulse" exist with scale and opacity at 50%
  • Dot uses pulse
  • 1.2s duration
  • ease-in-out timing
  • Loops forever