CSS Animations & Keyframes

Difficulty: Intermediate

Question

How do CSS animations differ from transitions? Explain @keyframes.

Answer

CSS animations use @keyframes for multi-step, auto-playing animations.

Differences from transitions: - Transitions: triggered by state change (hover, class), 2 states only (A to B) - Animations: can auto-play, loop, have multiple steps, run independently

Animation properties: - animation-name: keyframe name - animation-duration: how long - animation-timing-function: easing - animation-delay: wait before start - animation-iteration-count: number or infinite - animation-direction: normal, reverse, alternate - animation-fill-mode: forwards (keep end state), backwards, both - animation-play-state: running, paused

Code examples

Keyframes and Animation Properties

/* Define keyframes */
@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Apply animation */
.card {
  animation: fadeInUp 0.5s ease-out forwards;
  /* forwards: keeps the final state */
}

/* Multi-step animation */
@keyframes pulse {
  0%   { transform: scale(1); }
  50%  { transform: scale(1.05); }
  100% { transform: scale(1); }
}
.badge {
  animation: pulse 2s ease-in-out infinite;
}

/* Shorthand */
.element {
  animation: name 1s ease 0.5s 3 alternate forwards;
  /* name duration timing delay count direction fill */
}

from/to is shorthand for 0%/100%. Use percentages for multi-step animations. forwards keeps the final keyframe state after the animation ends.

Practical Animation Examples

/* Loading spinner */
@keyframes spin {
  to { transform: rotate(360deg); }
}
.spinner {
  width: 2rem;
  height: 2rem;
  border: 3px solid #e5e7eb;
  border-top-color: #3b82f6;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

/* Skeleton loading shimmer */
@keyframes shimmer {
  0%   { background-position: -200% 0; }
  100% { background-position: 200% 0; }
}
.skeleton {
  background: linear-gradient(
    90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%
  );
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite;
}

/* Staggered entrance */
.list-item {
  animation: fadeInUp 0.4s ease-out forwards;
  opacity: 0;
}
.list-item:nth-child(1) { animation-delay: 0.0s; }
.list-item:nth-child(2) { animation-delay: 0.1s; }
.list-item:nth-child(3) { animation-delay: 0.2s; }

The spinner and skeleton shimmer are used in every modern web app. Staggered delays create a cascading entrance effect.

Animation Performance and Accessibility

/* GOOD: animate only transform and opacity */
@keyframes slideIn {
  from {
    transform: translateX(-100%);
    opacity: 0;
  }
  to {
    transform: translateX(0);
    opacity: 1;
  }
}

/* BAD: animating layout properties */
@keyframes badSlide {
  from { margin-left: -100%; } /* triggers layout */
  to { margin-left: 0; }
}

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

/* Pause on hover (for autoplaying animations) */
.carousel {
  animation: scroll 10s linear infinite;
}
.carousel:hover {
  animation-play-state: paused;
}

Always include prefers-reduced-motion for users with motion sensitivity. Only animate transform and opacity for smooth 60fps performance.

Key points

Concepts covered

Keyframes, Animation Properties, Performance, Multi-step Animations, Accessibility