Chapter 1 of ?
css 8 min read

CSS3 Mastery — Chapter 19: CSS Transitions & Keyframe Animations

Chapter 19: CSS Transitions & Keyframe Animations

CSS Transitions provide smooth state-change interpolations (hover, focus, state toggles), while @keyframes animations give complete frame-by-frame control over complex multi-step UI sequences. Mastering next-generation Scroll-Driven Animations (animation-timeline, scroll(), view()), multi-keyframe orchestration, accessibility fallbacks (prefers-reduced-motion), and GPU hardware acceleration (will-change) creates modern enterprise motion design systems.

19.1 CSS Transitions & Timing Functions

Transitions smoothly interpolate CSS property values over time when an element changes state (e.g. :hover, :focus, or active class switches). Rather than instantly snapping to new values, property changes transition gradually:

transition: <property> <duration> <timing-function> <delay>;
Example: transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1) 0.1s, opacity 0.3s ease;
Transition Property Syntax Example Description & Best Practices
transition-property transform, opacity Specifies which CSS properties animate. Avoid all to prevent unintended reflows on performance-heavy properties.
transition-duration 0.3s, 300ms Length of time transition takes to complete. Recommended UI range: 150ms – 400ms.
transition-timing-function ease, ease-in-out, cubic-bezier(), steps() Calculates acceleration rate over time curve. Custom Bezier curves create springy/elastic effects.
transition-delay 0.1s, 100ms Wait time before transition starts. Great for staggered list/card entry effects.
transition (shorthand) transform 0.3s ease 0.1s Combines property, duration, timing-function, and delay into a single clean line.
<!DOCTYPE html>
<html>
<head>
  <style>
    *, *::before, *::after { box-sizing: border-box; }
    body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; margin: 0; display: flex; flex-direction: column; gap: 20px; }
    h4 { margin-top: 0; color: #38bdf8; font-size: 14px; }

    .btn-bounce {
      background: linear-gradient(135deg, #4f46e5, #06b6d4);
      color: #fff; border: none; padding: 12px 24px; border-radius: 8px;
      font-weight: 700; font-size: 14px; cursor: pointer; max-width: 280px;
      /* Elastic bounce cubic-bezier timing curve */
      transition: transform 0.4s cubic-bezier(0.68, -0.55, 0.27, 1.55), box-shadow 0.4s ease;
    }

    .btn-bounce:hover {
      transform: translateY(-6px) scale(1.05);
      box-shadow: 0 10px 24px rgba(6,182,212,0.4);
    }
  </style>
</head>
<body>
  <div>
    <h4>Elastic Spring Transition (cubic-bezier)</h4>
    <button class="btn-bounce">Hover Me for Spring Bounce!</button>
  </div>
</body>
</html>

19.2 The @keyframes Rule & Animation Properties

The @keyframes rule defines multi-stage animation milestones using percentages (from 0% to 100%) or keywords (from and to):

Property Key Values Description
animation-name spin, pulse, fadeIn Matches the name of your @keyframes block.
animation-duration 1s, 800ms Time for one complete cycle of the animation.
animation-timing-function linear, ease, steps(5), cubic-bezier() Pacing of the animation throughout each keyframe segment.
animation-delay 0.5s, -0.2s (negative delay starts mid-cycle) Delay before animation begins execution.
animation-iteration-count 1, 3, infinite Number of times the animation sequence repeats.
animation-direction normal, reverse, alternate, alternate-reverse Controls direction flow on consecutive cycles (alternate = ping-pong).
animation-fill-mode none, forwards, backwards, both Determines styles applied before delay and after completion.
animation-play-state running, paused Allows pausing and resuming animations (e.g. pause on hover).

19.3 Animation Fill Modes (forwards, backwards, both)

By default (animation-fill-mode: none), an element reverts to its pre-animation CSS rules as soon as an animation ends. animation-fill-mode overrides this behavior:

Fill Mode Before Animation Starts (During Delay) After Animation Completes
none Default element styles Reverts to default element styles (snaps back)
forwards Default element styles Retains the final keyframe styles (100% or 0% depending on direction)
backwards Applies first keyframe styles during delay Reverts to default element styles
both Applies first keyframe styles during delay Retains final keyframe styles permanently
<!DOCTYPE html>
<html>
<head>
  <style>
    *, *::before, *::after { box-sizing: border-box; }
    body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; margin: 0; display: flex; flex-direction: column; gap: 20px; }
    h4 { color: #818cf8; margin: 0 0 8px; }

    .box {
      width: 140px; padding: 12px; border-radius: 8px; font-size: 12px; font-weight: bold; text-align: center;
      background: #1e293b; border: 2px solid #6366f1; color: #a5b4fc;
      animation: slideFade 2s ease 1s; /* 1s delay */
    }

    .fill-none     { animation-fill-mode: none; }     /* Snaps back at end */
    .fill-forwards { animation-fill-mode: forwards; } /* Stays at 100% state */
    .fill-both     { animation-fill-mode: both; }     /* Starts at 0% during delay & stays at 100% */

    @keyframes slideFade {
      0%   { transform: translateX(0); opacity: 0.3; background: #1e293b; }
      100% { transform: translateX(160px); opacity: 1; background: #10b981; border-color: #10b981; color: #fff; }
    }
  </style>
</head>
<body>
  <div>
    <h4>animation-fill-mode: forwards vs none</h4>
    <p style="font-size:11px;color:#64748b;">Watch what happens when the 2s animation completes:</p>
    <div class="box fill-forwards" style="margin-bottom:12px;">forwards (Stays at end position)</div>
  </div>
</body>
</html>

19.4 The steps() Timing Function & Typewriter Effect

While Bezier curves produce continuous smooth motion, steps(n, direction) divides the animation into discrete, instant jumps — essential for sprite-sheet animations, digital clock ticks, and typewriter text reveals:

<!DOCTYPE html>
<html>
<head>
  <style>
    *, *::before, *::after { box-sizing: border-box; }
    body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; margin: 0; }
    h4 { color: #34d399; margin: 0 0 12px; }

    /* Typewriter Effect using steps() */
    .typewriter {
      font-family: 'Courier New', monospace;
      font-size: 18px;
      color: #38bdf8;
      white-space: nowrap;
      overflow: hidden;
      border-right: 3px solid #38bdf8;
      width: 0;
      animation: typing 3s steps(22) forwards, blink 0.75s step-end infinite;
    }

    @keyframes typing {
      to { width: 22ch; } /* 22 character width */
    }

    @keyframes blink {
      50% { border-color: transparent; }
    }
  </style>
</head>
<body>
  <h4>Typewriter Effect with steps(22)</h4>
  <div class="typewriter">CSS3 Mastery Tutorial!</div>
</body>
</html>

19.5 CSS Scroll-Driven Animations (animation-timeline, scroll(), view())

Modern CSS allows driving animation progress directly by user scroll movement—without JavaScript! Using animation-timeline: scroll() links keyframes to scroll container position, while animation-timeline: view() links keyframes to an element's visibility inside the viewport:

/* Scroll Progress Bar linked to page scroll position */
.progress-bar { animation: growWidth linear; animation-timeline: scroll(root); }

/* Reveal Card as it scrolls into viewport */
.reveal-card { animation: fadeIn linear; animation-timeline: view(); animation-range: entry 10% cover 40%; }
<!DOCTYPE html>
<html>
<head>
  <style>
    *, *::before, *::after { box-sizing: border-box; }
    body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; margin: 0; padding: 24px; }
    h4 { color: #38bdf8; margin: 0 0 10px; }

    /* Scroll Progress Bar at top */
    .scroll-progress-bar {
      position: fixed; top: 0; left: 0; right: 0; height: 5px;
      background: linear-gradient(90deg, #6366f1, #38bdf8, #10b981);
      transform-origin: left;
      /* Scroll-driven animation timeline linked to root scroll container */
      animation: scrollProgress linear;
      animation-timeline: scroll(root);
    }

    @keyframes scrollProgress {
      from { transform: scaleX(0); }
      to   { transform: scaleX(1); }
    }
  </style>
</head>
<body>
  <div class="scroll-progress-bar"></div>

  <h4>Scroll-Driven Animation Timeline Demo</h4>
  <p style="font-size:12px;color:#94a3b8;">The top progress bar fills from 0% to 100% driven purely by CSS animation-timeline: scroll(root)!</p>
</body>
</html>

19.6 Accessible Motion & prefers-reduced-motion Fallbacks

Respecting user accessibility settings is mandatory. Users with vestibular motion disorders can configure their OS to request reduced motion. Using `@media (prefers-reduced-motion: reduce)` safely replaces intense transforms with subtle opacity fades or disables animations entirely:

<!DOCTYPE html>
<html>
<head>
  <style>
    *, *::before, *::after { box-sizing: border-box; }
    body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; margin: 0; }
    h4 { color: #10b981; margin-top: 0; }

    .motion-card {
      background: #1e293b; border: 2px solid #10b981; border-radius: 12px; padding: 16px;
      animation: bounceRotate 2s ease-in-out infinite alternate;
    }

    @keyframes bounceRotate {
      0%   { transform: translateY(0) rotate(0deg); }
      100% { transform: translateY(-12px) rotate(4deg); }
    }

    /* Accessibility Fallback for Reduced Motion Preference */
    @media (prefers-reduced-motion: reduce) {
      .motion-card {
        /* Disables motion transforms, replacing with static low-impact opacity pulse */
        animation: subtlePulse 2s ease-in-out infinite alternate !important;
        transform: none !important;
      }
      @keyframes subtlePulse {
        0%   { opacity: 0.8; }
        100% { opacity: 1.0; }
      }
    }
  </style>
</head>
<body>
  <h4>Accessible Motion Fallback Stage</h4>
  <div class="motion-card">
    <strong style="color:#34d399;">Motion-Aware Component</strong>
    <p style="font-size:12px;color:#94a3b8;margin:4px 0 0;">Replaces bounce/rotate with gentle opacity fade when prefers-reduced-motion is active!</p>
  </div>
</body>
</html>

19.5 3D Card Flip Animation (perspective & preserve-3d)

Combine 3D transforms with transitions to build interactive 3D card flips. Key properties include perspective (viewing distance), transform-style: preserve-3d, and backface-visibility: hidden:

<!DOCTYPE html>
<html>
<head>
  <style>
    *, *::before, *::after { box-sizing: border-box; }
    body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; margin: 0; display: flex; flex-direction: column; align-items: center; }
    h4 { color: #fbbf24; margin: 0 0 16px; }

    /* 3D Container */
    .flip-container {
      perspective: 1000px; /* Gives 3D depth perspective */
      width: 220px; height: 140px; cursor: pointer;
    }

    .flip-card {
      width: 100%; height: 100%;
      position: relative;
      transform-style: preserve-3d;
      transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
    }

    .flip-container:hover .flip-card {
      transform: rotateY(180deg);
    }

    .card-face {
      position: absolute; width: 100%; height: 100%;
      border-radius: 12px; padding: 16px;
      backface-visibility: hidden; /* Hides back of card when turned away */
      display: flex; flex-direction: column; align-items: center; justify-content: center;
      font-weight: bold; font-size: 14px; text-align: center;
    }

    .card-front {
      background: linear-gradient(135deg, #6366f1, #3b82f6);
      color: #fff;
    }

    .card-back {
      background: linear-gradient(135deg, #10b981, #059669);
      color: #fff;
      transform: rotateY(180deg);
    }
  </style>
</head>
<body>
  <h4>3D Interactive Card Flip (Hover Me!)</h4>
  <div class="flip-container">
    <div class="flip-card">
      <div class="card-face card-front">
        <span style="font-size:24px;">🃏</span>
        Front Side
      </div>
      <div class="card-face card-back">
        <span style="font-size:24px;">✨</span>
        Back Side (Revealed!)
      </div>
    </div>
  </div>
</body>
</html>

19.6 GPU Acceleration & Render Pipeline (will-change)

To maintain a smooth 60 FPS (16.6ms frame budget), web animations must avoid triggering CPU-bound Layout (Reflow) and Paint stages. Animating composite-only properties allows the GPU to handle rendering entirely in hardware:

Browser Pipeline Stage Triggered By Properties Performance Impact
Layout (Reflow) width, height, margin, padding, top, left Worst (CPU Heavy) — Forces recalculation of entire DOM geometry
Paint background-color, box-shadow, color, border-radius ⚠️ Medium — Re-rasterizes pixels onto layer bitmap
Composite Only transform, opacity, filter Best (60 FPS GPU) — GPU shifts layer textures with zero CPU work!
<!DOCTYPE html>
<html>
<head>
  <style>
    *, *::before, *::after { box-sizing: border-box; }
    body { font-family: system-ui, sans-serif; background: #0f172a; color: #f8fafc; padding: 24px; margin: 0; }
    h3 { margin-top: 0; color: #10b981; }

    .stage { display: flex; flex-direction: column; gap: 16px; max-width: 440px; }

    /* 1. CPU Reflow: Animating margin-left */
    .bad-anim {
      background: rgba(239, 68, 68, 0.2); border: 2px solid #ef4444; color: #f87171;
      padding: 12px 16px; border-radius: 8px; font-size: 12px; font-weight: bold;
      transition: margin-left 0.4s ease; cursor: pointer;
    }
    .bad-anim:hover { margin-left: 20px; } /* Triggers CPU reflow! */

    /* 2. GPU Composite: transform + will-change */
    .gpu-card {
      background: rgba(16, 185, 129, 0.2); border: 2px solid #10b981; color: #34d399;
      padding: 12px 16px; border-radius: 8px; font-size: 12px; font-weight: bold;
      will-change: transform, opacity; /* Promotes element to GPU hardware layer */
      transform: translateZ(0);       /* Hardware acceleration fallback */
      transition: transform 0.4s ease, opacity 0.4s ease; cursor: pointer;
    }
    .gpu-card:hover { transform: translateX(20px); opacity: 0.95; }
  </style>
</head>
<body>
  <h3>GPU Acceleration &amp; will-change Demo</h3>
  <p style="font-size:12px;color:#64748b;">Hover both cards below to compare CPU reflow vs 60FPS GPU compositing:</p>

  <div class="stage">
    <div class="bad-anim">
      ❌ CPU Reflow: margin-left animation (Triggers Layout)
    </div>
    <div class="gpu-card">
      ✅ 60FPS GPU Composite: transform + will-change (GPU Layer)
    </div>
  </div>
</body>
</html>

19.7 Interactive Sandbox — Transitions & Animations Playground

Experiment with loading spinners, skeleton shimmer loaders, pulse badges, and typewriter effects live:

Live Playground — Chapter 19 Animations Sandbox
Done with this chapter?
Mark it complete to track your progress and unlock your certificate.
Next Up

Learner Reviews

Write a Review
Share your experience to help other learners.
Your Rating *