Web Animations: CSS, JS, and the Right Tool for the Job

Animations make products feel polished. Done badly, they make them feel slow. Frontend interviews increasingly ask about web animation — when to use CSS, when to use JavaScript, what runs on the compositor, and how to keep animations smooth on mid-range mobile.

CSS transitions vs animations

Transitions: animate from one state to another (hover, click, route change). Property changes from A to B over a duration.

.button {
  background: blue;
  transition: background 200ms ease;
}
.button:hover { background: darkblue; }

Animations: keyframe-based, can repeat, can have multiple stops.

@keyframes spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}
.spinner { animation: spin 1s linear infinite; }

What runs on the compositor

Some properties animate without main-thread work — they run on the compositor (GPU) and stay smooth even when JS is busy:

  • transform (translate, rotate, scale)
  • opacity
  • Some filter values

Others trigger layout or paint on every frame — slow:

  • top, left (use transform: translate instead)
  • width, height (animate transform: scale instead)
  • color, background-color (paint, but acceptable for short transitions)

The will-change property

will-change: transform tells the browser to promote this element to its own compositor layer in advance. Useful for elements that will animate soon.

Caveat: don’t apply to many elements. Each layer consumes memory; too many degrades performance.

JavaScript animation libraries

When CSS is not enough (interactive sequences, physics, gestures), use a library:

  • Framer Motion (motion/react): declarative, React-native, ergonomic API. Industry standard for React apps in 2026.
  • GSAP: mature, broad capabilities, paid for commercial use beyond a threshold.
  • Web Animations API: built into browsers, no library needed for simple cases.

Web Animations API

Native API for JS-driven animations:

element.animate([
  { transform: 'translateX(0)' },
  { transform: 'translateX(100px)' }
], { duration: 300, easing: 'ease-out' });

Returns an Animation object you can play, pause, or reverse. No library needed for basic cases.

Scroll-driven animations

2024+ feature: animate based on scroll position with pure CSS:

@keyframes reveal {
  from { opacity: 0; }
  to { opacity: 1; }
}
.element {
  animation: reveal linear;
  animation-timeline: view();
}

Browser support: Chromium-only as of 2026. JS-based fallbacks (IntersectionObserver) still common.

Reduced motion

Some users have motion sensitivity. Respect their preference:

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

This is accessibility, not a nicety. Always include.

The 16.67ms budget

60fps requires each frame to render in 16.67ms. Animation work that exceeds this drops frames.

Profile with Chrome DevTools Performance panel. Look for long tasks in the main thread during animation.

Common patterns

Fade in

.fade-in {
  animation: fadeIn 300ms ease-out;
}
@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

Slide in from below

.slide-up {
  animation: slideUp 300ms cubic-bezier(0.16, 1, 0.3, 1);
}
@keyframes slideUp {
  from { transform: translateY(20px); opacity: 0; }
  to { transform: translateY(0); opacity: 1; }
}

Pulse

.pulse {
  animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.6; }
}

Easing functions

  • linear: constant speed; rarely the right choice
  • ease: default; slight ease-in and ease-out
  • ease-out: fast start, slow end; good for entrances
  • ease-in: slow start, fast end; good for exits
  • cubic-bezier(0.16, 1, 0.3, 1): snappy entrance; common in Apple/Google apps

Common mistakes

  • Animating layout properties (top, left, width)
  • Animations longer than 500ms (feel sluggish)
  • No prefers-reduced-motion support
  • Using JS for everything CSS could handle
  • Cascading animations that block input (modal opens slowly, user cannot tap)

Frequently Asked Questions

How long should an animation be?

Most UI animations: 150–300ms. Page transitions: 300–500ms. Anything longer feels slow on a phone.

Is GSAP worth the license?

For complex commercial projects with elaborate animations: yes. For typical web apps: Framer Motion or Web Animations API is enough.

Should I use animation libraries for simple effects?

No. CSS transitions handle 80% of UI animations. Reach for JS libraries only when CSS hits limits.

Scroll to Top