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
.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
@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
/* ✅ Performant */
.good { transition: transform 0.3s, opacity 0.3s; }
/* ❌ Layout thrashing */
.bad { transition: width 0.3s, margin 0.3s; }
Practical Examples
1. Staggered List
@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
@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
@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
@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
- Animating layout properties triggers expensive recalculations.
- No prefers-reduced-motion support for sensitive users.
- Animations >500ms feel sluggish for UI interactions.
- Overusing will-change wastes GPU memory.
Browser Support
Transitions and @keyframes: universal. animation-timeline (scroll-driven): Chrome 115+. prefers-reduced-motion: universal.
Related Guides
- CSS View Transitions — page-level animations
- CSS Filter Effects — animate filter values
- JavaScript Performance Tips — when to use JS animations