CSS Animations & Transitions
Create smooth CSS animations: transitions vs keyframes, animation performance, View Transitions API, and reduced motion accessibility.
CSS animations and transitions enable smooth visual feedback without JavaScript. The key to performant animations is animating only GPU-composited properties (transform, opacity) and using CSS animations on the element directly rather than forcing layout recalculations.
Animate only `transform` and `opacity` for performance - these are composited by the GPU and never trigger layout or paint. Use CSS transitions for state changes (hover, active), keyframes for looping or complex sequences. Always respect `prefers-reduced-motion`.
Workflow
- Write performant transitionsCSS transitions automatically animate property changes between states.
- Create keyframe animationsKeyframes define complex multi-step animations independent of state changes.
- Force GPU compositingGPU-composited layers animate without involving the main thread, preventing jank.
- Use the View Transitions APIView Transitions enable smooth same-page and cross-page transitions with minimal code.
- Respect prefers-reduced-motionUsers with vestibular disorders or motion sensitivity can be harmed by animations.
Output Format
Transitions on base element (not :hover), keyframes with `both` fill-mode, transform/opacity only, `will-change` before animation, View Transitions for route changes, `prefers-reduced-motion` media query.
Examples
Performant button: `.btn { transition: transform 0.15s ease, box-shadow 0.15s ease; } .btn:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,0.15); } .btn:active { transform: translateY(0); box-shadow: none; }`Page transition: `document.startViewTransition(async () => { const response = await fetch(url); document.querySelector("#content").innerHTML = await response.text(); }); ::view-transition-old(root) { animation: slide-out 0.3s ease-in forwards; } ::view-transition-new(root) { animation: slide-in 0.3s ease-out forwards; }`Reduced motion: `@media (prefers-reduced-motion: no-preference) { .notification { animation: bounce 0.5s ease; } } @media (prefers-reduced-motion: reduce) { .notification { animation: fade 0.2s ease; } /* simple fade instead of bounce */}`Edge Cases & Gotchas
- FLIP technique: for animating layout changes (reordering, expanding), use FLIP (First-Last-Invert-Play) with Web Animations API - capture start/end positions, then animate transform between them instead of layout properties
- View Transitions cross-origin: only same-origin navigation qualifies; cross-origin navigations never trigger view transitions
- `will-change` leak: forgetting to remove `will-change` after animation creates persistent compositing layers - memory cost with no benefit
When to Break These Rules
- For complex, timeline-based animations with multiple coordinated elements (character animation, infographics), use GSAP - it's purpose-built for this and handles cross-browser quirks.
Reference Files
View Transitions API: https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_APICSS Animations: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animationsGPU compositing: https://web.dev/articles/animations-guideReduced motion: https://web.dev/articles/prefers-reduced-motion