The Modern Web Animation Stack: CSS, WAAPI, Libraries and Runtimes
A practical map of the web animation stack: CSS, the Web Animations API, Motion, GSAP, Lottie, Rive and runtime files, and when each is the right layer.
Core Takeaway
The modern web animation stack has four layers: CSS for simple state changes, the Web Animations API for imperative control, JavaScript libraries for springs and orchestration, and format runtimes for authored interactive assets. Most products use more than one. Choose by the interaction, not by the tool with the most features.
Layer 1: CSS
CSS transitions and keyframes are the cheapest way to animate simple changes: hover, focus, colour, opacity and small transforms. They are declarative, work without JavaScript and are easy to reduce for prefers-reduced-motion. They become awkward when the motion needs to be interrupted with velocity, sequenced across many elements or driven by gesture data. Use CSS when the state change is simple and the browser can own it.
Layer 2: the Web Animations API
The Web Animations API gives JavaScript control over the browser's animation engine. You can play, pause, reverse, seek and inspect animations, and you can use keyframes without a library. It is a good middle ground when CSS is too static but a full animation library is too much. Browser support is broad, but check the specific features you use, especially around composite animations and view transitions.
Layer 3: JavaScript motion libraries
Libraries such as Motion and GSAP add springs, timelines, gesture integration and orchestration. They are useful when the interaction is complex but still lives in one codebase. The trade-off is bundle size, a new API and the need to keep animation code out of the render loop. Use a library when it removes code you would otherwise write badly, not because it is popular.
Layer 4: authored format runtimes
Lottie and dotLottie play animation exported from design tools. Rive plays interactive .riv files with state machines. Other runtime formats hold layout, states, springs and inputs. These runtimes are useful when the animation is an asset authored outside the app, reused across platforms, or edited by someone who does not work in the codebase. The cost is another file format and runtime to maintain.
How the layers combine
A product page might use CSS for hover states, a JavaScript library for a scroll sequence, and a runtime file for an interactive product demo. That is normal. The mistake is using one layer for everything: a full runtime for a hover colour, or hand-written spring code for every button. Match the layer to the job.
A decision path
Ask four questions in order.
- Is the change simple and non-interruptible? Start with CSS
- Does it need imperative control but not a library? Try the Web Animations API
- Does it need springs, gestures or orchestration in one codebase? Use a motion library
- Is it an authored asset reused across platforms or edited outside code? Use a format runtime
What to measure
Measure the cost that matters for the layer: first paint and bundle size for libraries, runtime frame time for format players, and maintenance cost for hand-written animation. Test on a mid-range device and with reduced motion. A stack is healthy when each layer is used for what it is good at and the team can explain why.
Key Principles to Remember
- Use CSS for simple, non-interruptible state changes
- Use the Web Animations API when you need imperative control without a library
- Use a JavaScript library for springs, gestures and orchestration in code
- Use a format runtime for authored assets reused across platforms
- Most products combine layers; match each layer to the job
Frequently Asked Questions
Sources
- Web Animations API — MDN Web Docs
- Motion documentation — Motion
- GSAP documentation — GSAP
- dotLottie specification — dotLottie