Skip to content
CSS

CSS Animation & Keyframes

From simple transitions to complex keyframe sequences — performant animations that feel alive.

stellae.design

9 min read

Introduction

CSS animations bring interfaces to life. Well-crafted animations guide attention, communicate state, and create quality. CSS provides transitions (A→B) and @keyframes (multi-step sequences), both GPU-accelerated.

The key: what to animate, how long, and which easing gives the right feel. Only animate transform and opacity for guaranteed 60fps.

This guide covers transitions, keyframes, performance, and motion preferences.


Key Concepts

Transitions

css
.button {
  background: var(--color-primary);
  transform: scale(1);
  transition: background 0.2s ease, transform 0.15s ease;
}
.button:hover { background: var(--color-primary-dark); transform: scale(1.02); }
.button:active { transform: scale(0.98); }

Keyframe Animations

css
@keyframes fade-in-up {
  from { opacity: 0; transform: translateY(1rem); }
  to { opacity: 1; transform: translateY(0); }
}
.card { animation: fade-in-up 0.4s ease-out both; }

Performant Properties

css
/* ✅ Performant */
.good { transition: transform 0.3s, opacity 0.3s; }
/* ❌ Layout thrashing */
.bad { transition: width 0.3s, margin 0.3s; }

Practical Examples

1. Staggered List

css
@keyframes slide-in { from { opacity: 0; transform: translateX(-1rem); } }
.list-item { animation: slide-in 0.3s ease-out both; }
.list-item:nth-child(1) { animation-delay: 0ms; }
.list-item:nth-child(2) { animation-delay: 50ms; }
.list-item:nth-child(3) { animation-delay: 100ms; }

2. Loading Spinner

css
@keyframes spin { to { transform: rotate(360deg); } }
.spinner {
  width: 2rem; height: 2rem;
  border: 3px solid var(--border);
  border-top-color: var(--color-primary);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

3. Skeleton Shimmer

css
@keyframes shimmer {
  0% { background-position: -200% 0; }
  100% { background-position: 200% 0; }
}
.skeleton {
  background: linear-gradient(90deg, var(--surface) 25%, var(--highlight) 50%, var(--surface) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite;
}

4. Reduced Motion

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

Best Practices

  • Interactive transitions under 300ms.
  • ease-out for entrances, ease-in for exits.
  • Only animate transform and opacity.
  • Always respect prefers-reduced-motion.
  • animation-fill-mode: both to maintain final state.
  • Stagger delays 30-80ms per item.
  • will-change sparingly.

Common Pitfalls

  1. Animating layout properties triggers expensive recalculations.
  2. No prefers-reduced-motion support for sensitive users.
  3. Animations >500ms feel sluggish for UI interactions.
  4. Overusing will-change wastes GPU memory.

Browser Support

Transitions and @keyframes: universal. animation-timeline (scroll-driven): Chrome 115+. prefers-reduced-motion: universal.


CSScss

Was this article helpful?