Skip to main content
🎨 CSS as a design system·Module B7 · Lesson 4
TaskBuild a pulse animation. @keyframes pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.1); } }. .heart { font-size: 48px; animation: pulse 1.5s ease-in-out infinite; display: inline-block; }. Show a ❤ emoji that pulses.

@keyframes: declaring multi-step animations

75 XP6 min
Theory

Two ways to write keyframes

/* from/to (= 0%/100%) — simple two-state */
@keyframes fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* percentages — multi-step */
@keyframes bounce {
  0%   { transform: translateY(0); }
  40%  { transform: translateY(-30px); }
  70%  { transform: translateY(-10px); }
  100% { transform: translateY(0); }
}

Then apply with the animation shorthand:

.fade-in { animation: fade-in 0.5s ease-out forwards; }
.bounce  { animation: bounce 1s ease-in-out infinite; }

The shorthand: animation: name dur timing iter direction fill

animation: bounce 1s ease-in-out 2 alternate forwards;

Reads: "bounce, 1s long, ease-in-out curve, 2 iterations, alternate direction, hold final keyframe."

  • iteration-countinfinite or a number.
  • directionnormal, reverse, alternate (forward then back), alternate-reverse.
  • fill-modeforwards (hold final), backwards (apply initial before start), both, none (default).

When to use animation-fill-mode: forwards

Without forwards, the animation runs and then SNAPS back to the pre-animation state. Always add forwards for one-shot entrance animations.

Don't forget reduced-motion

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
}

(Recap from A5 lesson 6.) Kill all motion for users who opted out.

🔒

Sign up to start coding

Theory is open to everyone. The interactive editor, live preview, and check are unlocked with a 7-day free trial — card required, cancel anytime.

Sign up — free trial →

First 10 lessons in each track are free. No card needed for those.

PreviousNext lesson →

Get one Python or web tip a day — by email

Short, hand-written, no spam. Unsubscribe in one click.