CSS animations are one of those features where the basics are easy and the nuances matter enormously. You can make something bounce in 30 seconds. Making it bounce in a way that feels natural and does not destroy your frame rate takes considerably more understanding.
I have spent hundreds of hours refining animation patterns, and the lessons are surprisingly consistent.
The performance hierarchy
Not all CSS properties are created equal for animation. The browser handles them at different stages of the rendering pipeline:
Compositor-only properties (transform, opacity): These animate on the GPU without triggering layout or paint. They run at 60fps even on modest hardware. Always prefer these.
Paint properties (background-color, box-shadow, border-radius): These trigger a repaint but not a relayout. Moderately expensive. Acceptable for small elements, problematic for large ones.
Layout properties (width, height, margin, padding, top, left): These trigger relayout, repaint, and composite. Extremely expensive. Animating these on large elements or many elements simultaneously will cause frame drops.
The practical rule: animate transform and opacity. Achieve everything else through those two properties.
Want to animate an element's position? Use transform: translateX(), not left.
Want to animate size? Use transform: scale(), not width/height.
Want to fade? Use opacity.
Want to animate color? You are going to need background-color, but keep the element small.
Timing functions that feel right
The default ease timing function is a reasonable starting point, but it is not appropriate for all animations. The built-in options:
- linear: Constant speed. Feels mechanical and unnatural. Use for progress bars and loading indicators.
- ease: Slow start, fast middle, slow end. The default for most transitions.
- ease-in: Slow start. Use for elements exiting the viewport.
- ease-out: Slow end. Use for elements entering the viewport.
- ease-in-out: Slow start and end. Use for elements that move and then stop.
The key principle from animation design: objects entering should ease out (decelerate to a stop). Objects leaving should ease in (accelerate away). This mimics how physical objects behave.
For custom curves, cubic-bezier() gives you complete control:
/* A snappy overshoot animation */
.element {
animation-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1);
}
The two control points define the curve. Values above 1.0 create overshoot effects where the property temporarily exceeds its target before settling back. This creates bouncy, energetic animations.
Keyframe patterns that work
Entrance: Fade in and slide up. The most universally useful animation.
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
Attention: A subtle pulse or shake to draw focus.
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
Loading: A three-dot loading indicator with staggered delays.
.dot { animation: bounce 1.4s ease-in-out infinite; }
.dot:nth-child(2) { animation-delay: 0.16s; }
.dot:nth-child(3) { animation-delay: 0.32s; }
@keyframes bounce {
0%, 80%, 100% { transform: scale(0); }
40% { transform: scale(1); }
}
Respecting user preferences
Always wrap animations in a prefers-reduced-motion media query:
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
This respects users who have enabled reduced motion in their operating system preferences, typically for accessibility reasons (vestibular disorders, motion sensitivity). Setting duration to 0.01ms rather than 0s ensures animation events still fire correctly.
The generator
Building animation keyframes by hand requires repeated browser refreshes and manual cubic-bezier tweaking. I built a CSS animation generator that provides a visual editor for timing functions, keyframes, and duration, with real-time preview and generated CSS output ready to paste into your stylesheet.
I'm Michael Lip. I build free developer tools at zovo.one. 500+ tools, all private, all free.
Top comments (0)