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.
.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
/* 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.
/* 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.
@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; }
@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().
.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
@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.