Animation separates good interfaces from great ones. A well-placed transition makes buttons feel responsive, page loads feel instant, and navigation feel effortless. CSS gives you two animation systems: transitions for simple state changes, and keyframe animations for complex, multi-step sequences.

CSS Transitions

Transitions animate the change between two states of a CSS property — typically triggered by :hover, :focus, or a class toggle.

Basic Transition
.button {
  background: #6366f1;
  color: white;
  padding: 0.75rem 1.5rem;
  border-radius: 8px;
  transition: background 200ms ease, transform 200ms ease;
}

.button:hover {
  background: #4f46e5;
  transform: translateY(-2px);
}

.button:active {
  transform: translateY(0);
}

The Transition Shorthand

Transition Property Breakdown
/* Shorthand: property | duration | timing-function | delay */
transition: opacity 300ms ease-in-out 0ms;

/* Multiple properties */
transition: opacity 300ms ease, transform 300ms ease;

/* All properties (use sparingly) */
transition: all 200ms ease;

Timing Functions

Timing functions control the acceleration curve of the animation. They make the difference between an animation that feels mechanical and one that feels natural.

  • ease — Starts slow, speeds up, then slows down. Good default.
  • ease-in — Starts slow, ends fast. Best for elements leaving the screen.
  • ease-out — Starts fast, ends slow. Best for elements entering the screen.
  • ease-in-out — Slow at both ends. Good for continuous loops.
  • linear — Constant speed. Good for progress bars and spinners.
  • cubic-bezier() — Custom curve for precise control.
Custom Cubic Bezier
/* Bouncy "spring" effect */
transition: transform 500ms cubic-bezier(0.34, 1.56, 0.64, 1);

/* Snappy "material design" feel */
transition: all 225ms cubic-bezier(0.4, 0, 0.2, 1);

CSS Keyframe Animations

When you need more than a two-state transition, keyframe animations let you define multiple steps in an animation sequence.

Keyframe Animation
@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.card {
  animation: fadeInUp 600ms ease-out forwards;
}

/* Stagger cards with delay */
.card:nth-child(2) { animation-delay: 100ms; }
.card:nth-child(3) { animation-delay: 200ms; }
.card:nth-child(4) { animation-delay: 300ms; }
Multi-Step Animation
@keyframes pulse {
  0%   { transform: scale(1); opacity: 1; }
  50%  { transform: scale(1.05); opacity: 0.8; }
  100% { transform: scale(1); opacity: 1; }
}

.live-indicator {
  animation: pulse 2s ease-in-out infinite;
}

Performance: What to Animate

Not all CSS properties are created equal when it comes to animation performance. Animating some properties triggers expensive layout recalculations; others are handled entirely by the GPU.

  • ✅ GPU-accelerated (cheap): transform, opacity
  • ⚠️ Triggers paint (moderate): background-color, box-shadow, border-color
  • ❌ Triggers layout (expensive): width, height, margin, padding, top, left

Rule: Animate transform and opacity whenever possible. Instead of animating width, use transform: scaleX(). Instead of animating top, use transform: translateY().

Force GPU Acceleration
.animated-element {
  /* Hint to the browser to promote to its own compositor layer */
  will-change: transform, opacity;

  /* Fallback for older browsers */
  transform: translateZ(0);
}

Respecting User Preferences

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

Some users experience motion sickness or vestibular disorders. Always include a prefers-reduced-motion media query to disable or reduce animations for users who have enabled this setting.

Try Our Free CSS Tools

Minify and beautify your CSS instantly in your browser.