DEV Community

Michael Lip
Michael Lip

Posted on • Originally published at zovo.one

CSS Animations Beyond the Basics: Timing Functions, Performance, and Real Patterns

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);
}
Enter fullscreen mode Exit fullscreen mode

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);
  }
}
Enter fullscreen mode Exit fullscreen mode

Attention: A subtle pulse or shake to draw focus.

@keyframes pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.05); }
}
Enter fullscreen mode Exit fullscreen mode

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); }
}
Enter fullscreen mode Exit fullscreen mode

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;
  }
}
Enter fullscreen mode Exit fullscreen mode

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)