Learning CSS - Transitions and CSS Animations
Series/Learning CSS/Episode 14
Episode 14 of 23

Learning CSS - Transitions and CSS Animations

This episode covers CSS transitions and animations: the transition property for smooth changes, transform for shifting and scaling, and @keyframes and animation for sequential motion, closing with prefers-reduced-motion support.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Smooth visual feedback makes an interface feel alive. Episode 13 introduced states like :hover; episode 14 teaches how to make those state changes run smoothly through transition, and how to compose complete sequential motion through @keyframes animations. Animation isn't just decoration. Smooth transitions give hints about what changed and where it's going, speeding up the user's understanding of the interface. Why does it matter? Excessive motion is distracting and makes some users nauseous. The real skill is creating animations that are smooth, short, and respect the user's motion preferences.

Basic Transitions

transition smooths a property's value change from one state to another:

CSStransition.css
.tombol {
  background-color: #2563eb;
  transform: translateY(0);
  transition-property: background-color, transform;
  transition-duration: 0.2s;
  transition-timing-function: ease;
}
.tombol:hover {
  background-color: #1d4ed8;
  transform: translateY(-2px);
}

When :hover is active, the color and position change over 0.2 seconds with an ease curve instead of jumping. Its compact form is transition: background-color 0.2s ease.

CSStiming.css
.cepat {
  transition: all 0.15s ease-in;
}
.mulus {
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.tunda {
  transition: opacity 0.2s ease 0.1s;
}

transition: all is concise but animates every property; for control, list properties explicitly. A cubic-bezier curve provides custom acceleration, and the last value is the delay — the wait before the transition starts.

Transform and GPU Acceleration

transform moves an element without changing the document flow. The transform and opacity combination is the key to smooth animations because it runs on the compositor instead of triggering reflow:

CSStransform.css
.modal { opacity: 0; transform: translateY(16px) scale(0.95); transition: opacity 0.2s ease, transform 0.2s ease; }
.modal.buka { opacity: 1; transform: translateY(0) scale(1); }
.kartu { transition: transform 0.2s ease; }
.kartu:hover { transform: rotate(1deg) scale(1.02); }

translate, scale, and rotate are combined in one transform. Transform and opacity animations run on the compositor, staying at 60fps, while animating width, top, or margin forces the browser to recalculate layout every frame.

Animations with @keyframes

For sequential motion that doesn't depend on state, use @keyframes together with animation:

CSSkeyframes.css
@keyframes muncul {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
.pesan {
  animation: muncul 0.4s ease-out both;
}

from and to define the start and end states. The value both applies the from styles before the animation starts and to after it finishes, preventing the element from flashing.

CSSkeyframes-2.css
@keyframes denyut {
  0% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.15);
  }
  100% {
    transform: scale(1);
  }
}
.spinner {
  animation: denyut 1s ease-in-out infinite;
}

With percentages, you can define many stages. infinite loops forever — common for spinners and loading indicators.

Animation Properties and Control

The various animation properties give full control over motion behavior:

CSSanimation-prop.css
.loader {
  animation-name: denyut;
  animation-duration: 1.2s;
  animation-timing-function: ease-in-out;
  animation-delay: 0.2s;
  animation-iteration-count: 3;
  animation-direction: alternate;
  animation-fill-mode: both;
}
.paused {
  animation-play-state: paused;
}

animation-direction: alternate reverses direction each cycle — a natural up-and-down effect. animation-play-state can stop an animation from JavaScript or :hover without rewriting the keyframes.

Respecting Prefers-Reduced-Motion

Some users disable animations in their operating system. The prefers-reduced-motion media query lets you turn off motion:

CSSreduced-motion.css
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
@media (prefers-reduced-motion: reduce) {
  .banner-berjalan {
    animation: none;
  }
}

The first pattern speeds up all transitions and animations until nearly instant. The second pattern disables non-essential decorative animations. Both keep the interface fully functional for users sensitive to motion.

Exercise: A Button with Feedback

HTMLindex.html
<!DOCTYPE html>
<html lang="id">
  <head>
    <meta charset="UTF-8">
    <title>Animasi Tombol</title>
    <link rel="stylesheet" href="css/style.css">
  </head>
  <body>
    <button class="tombol">Kirim</button>
    <div class="badge">Terkirim</div>
  </body>
</html>
CSScss/style.css
body { font-family: system-ui, sans-serif; padding: 48px; }
.tombol { padding: 12px 24px; border: none; border-radius: 8px; background-color: #2563eb; color: white; font-size: 1rem; cursor: pointer; transition: background-color 0.2s ease, transform 0.2s ease; }
.tombol:hover { background-color: #1d4ed8; transform: translateY(-2px); }
.tombol:active { transform: translateY(0); transition-duration: 0.05s; }
.badge { display: inline-block; margin-top: 16px; padding: 8px 12px; border-radius: 6px; background-color: #dcfce7; color: #166534; opacity: 0; animation: muncul 0.4s ease-out both; }
@keyframes muncul { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }

Run npx serve . and feel the difference between a button that changes instantly versus one that moves smoothly. Enable the reduce-motion setting in your system and reload — the transitions become instant but still work.

Common Mistakes and Solutions

Animating Expensive Properties

Animating width, height, or margin triggers reflow every frame. Use transform and opacity for smooth performance.

Transition Not Running

A transition needs a value difference between states and properties listed in transition-property. Make sure the hover state modifies the same property it started with.

Excessive Motion

Too many animations distract focus and make the page feel slow. Apply motion sparingly, keep it short, and always respect prefers-reduced-motion.

Closing

Key takeaways:

  • transition smooths changes between states over a duration and curve.
  • transform and opacity are the safest properties for performance.
  • @keyframes defines sequential motion; animation runs it.
  • animation-fill-mode, direction, and iteration-count give full control.
  • animation-play-state stops motion without rewriting keyframes.
  • Respect prefers-reduced-motion for motion accessibility. In the next episode, episode 15, we'll cover custom properties and variable-based theming — storing color and spacing values once and swapping themes dynamically.