1 What Are CSS Scroll-Driven Animations?
CSS Scroll-Driven Animations is a W3C specification that fundamentally reimagines how animations are linked to time. Rather than tying animation progress to a wall-clock timer (milliseconds elapsed), this API allows developers to bind the progress of any standard @keyframes animation directly to the scroll position of a container or the intersection of an element with its viewport — using only CSS, with zero JavaScript.
Before this API, achieving scroll-linked effects required complex JavaScript. Developers had to choose between:
- Native scroll event handlers — synchronous listeners that cause main-thread jank, poor performance on low-end devices, and require manual throttling/debouncing.
- IntersectionObserver — async, no exact progress tracking, only binary in/out state.
- GSAP ScrollTrigger — powerful but adds ~60KB of JavaScript payload, licensing considerations, and main-thread execution.
- requestAnimationFrame loops — manual progress tracking, difficult to optimize, memory-leaking if not cleaned up.
The CSS Scroll-Driven Animations specification, shipped in Chrome 115 (July 2023), introduces two new CSS properties:
| Property | Purpose | Example Value |
|---|---|---|
animation-timeline | Specifies what drives the animation's progress | scroll(), view(), --my-timeline |
animation-range | Constrains the active range within the timeline | entry, cover 20% 80% |
scroll-timeline-name | Names a scroll container as a reusable timeline | --my-scroll |
scroll-timeline-axis | Declares the axis for a named scroll timeline | block, inline |
view-timeline-name | Names a subject element as a view timeline | --card-reveal |
view-timeline-axis | Declares the axis for a named view timeline | block |
animation-duration: auto or omitting it entirely is required when using scroll-driven timelines. The browser automatically maps scroll progress 0–100% to animation progress 0%–100%.
The specification is a collaboration between Google, Apple, Mozilla, and Microsoft engineers within the CSS Working Group. The canonical reference is the CSS Animations Level 2 spec and the Scroll-driven Animations spec at the W3C.
2 scroll() vs view() Timelines — Choosing the Right Tool
The two timeline functions serve fundamentally different use cases. Understanding which to use is the most important conceptual step in mastering scroll-driven animations.
| Feature | scroll() | view() |
|---|---|---|
| Tracks | Scroll container's absolute offset | Element's intersection with scrollport |
| 0% when | Scroll container is at its topmost position | Element's leading edge enters the scrollport |
| 100% when | Scroll container is at its bottommost position | Element's trailing edge exits the scrollport |
| Best for | Reading progress bars, parallax backgrounds, sticky header opacity | Reveal on scroll, card animations, staggered list items |
| Axis param | block, inline, x, y | block, inline, x, y |
| Container param | nearest, root, self, custom | Implicit (nearest scroll ancestor) |
scroll() Syntax
view() Syntax
view(), the animation-fill-mode: both (or the shorthand both in the animation property) is critical. Without it, the element may snap back to its initial state after the animation ends, creating a jarring flash.3 Mastering animation-range — Precision Control Over Intersection
The animation-range property is the most nuanced part of the view() timeline system. It controls the precise sub-range of the element's intersection journey during which the animation is active. Without it, a view() animation runs during the entire time the element is intersecting the scrollport — from first touch to final exit — which is often not what you want.
The full set of named range keywords maps to specific phases of the element's intersection journey:
| Keyword | Start Point | End Point | Use Case |
|---|---|---|---|
cover | Leading edge enters scrollport | Trailing edge exits scrollport | Full parallax overlay effects |
contain | Trailing edge enters scrollport | Leading edge exits scrollport | Animations only while fully visible |
entry | Leading edge enters scrollport | Trailing edge enters scrollport | Reveal/fade-in as element enters |
exit | Leading edge exits scrollport | Trailing edge exits scrollport | Fade-out as element leaves |
entry-crossing | Leading edge enters scrollport | Leading edge reaches center | Subtle entry cross-fade |
exit-crossing | Trailing edge reaches center | Trailing edge exits scrollport | Subtle exit cross-fade |
Beyond named keywords, animation-range accepts a start and end value for ultra-precise control. These values can be expressed as percentages or length units relative to the range keyword:
animation-range: entry 0% cover 30% with a custom property --i for each list item index, then compute animation-delay using calc(var(--i) * 50ms). This creates a beautiful waterfall effect with zero JS.
4 GPU Compositor Thread — Why Native CSS Beats JavaScript for Performance
The single most compelling reason to use native CSS Scroll-Driven Animations over JavaScript solutions is architectural performance superiority. To understand why, you need to understand how browsers process and render frames.
The Browser Rendering Pipeline
Every frame rendered by the browser goes through a pipeline: JavaScript → Style → Layout → Paint → Composite. The first four stages execute on the Main Thread — the same thread that handles JavaScript execution, HTML parsing, and CSS recalculation. If any stage takes too long, the frame is dropped and the user experiences jank.
The Composite stage, however, can execute on the GPU's Compositor Thread, which is completely independent of the Main Thread. Only two CSS properties can be composited without triggering Layout or Paint: transform and opacity.
| Approach | Thread | Jank Risk | Max FPS | Payload |
|---|---|---|---|---|
| CSS SDA (transform/opacity) | GPU Compositor | None | 120+ | 0 KB JS |
| CSS SDA (layout props) | Main Thread | Medium | 60 | 0 KB JS |
| GSAP ScrollTrigger | Main Thread | Medium | 60 | ~67 KB |
| scroll event + rAF | Main Thread | High | 60 | Custom |
| IntersectionObserver only | Main Thread (callback) | Low-Med | 60 | Custom |
transform and opacity. Animating width, height, top, left, background-color, or border-radius will re-trigger Layout or Paint on the Main Thread, negating the performance advantage.When a CSS scroll-driven animation only uses transform and opacity, Chrome's renderer creates a compositor animation. This means the browser can scroll the page and update the animation simultaneously, even if a 50ms JavaScript long task is blocking the main thread. The result is buttery-smooth 120fps animations on ProMotion displays, completely immune to main-thread congestion.
5 Named Scroll & View Timelines — Cross-Element Animation
Anonymous scroll() and view() timelines work when the animated element is the same as the scroll source or subject. But real-world designs often require animating one element based on the scroll progress of another — for example, animating a sticky sidebar based on the content section scroll, or animating multiple elements based on one shared scroll container.
This is solved by named timelines. You declare a scroll container or subject element as a named timeline using scroll-timeline-name or view-timeline-name, then reference that name from any descendant element's animation-timeline.
Named Scroll Timeline
Named View Timeline
6 Easing Curves — Shaping the Animation Response Curve
The animation-timing-function (or the easing value in the animation shorthand) controls the rate of change of the animation progress. In time-based animations, this shapes how fast the animation accelerates and decelerates. In scroll-driven animations, it shapes how the animated property responds within each keyframe interval as scroll progress changes.
A crucial conceptual point: the overall animation-timeline progress is always linear with scroll position (scrolling down by 10% always advances the timeline by 10%). The easing function only applies within each keyframe interval, controlling how property values interpolate between keyframe stops.
| Easing Function | Curve Shape | Best Use Case in SDA | Feel |
|---|---|---|---|
linear | Straight diagonal | Progress bars, parallax, color gradients | Mechanical, precise |
ease | Slow→fast→slow | General card reveals | Natural, organic |
ease-in | Slow start | Exit animations (fading out) | Gradual buildup |
ease-out | Slow end | Entry animations (landing softly) | Deceleration, landing |
ease-in-out | Slow both ends | Contained animations (fully visible) | Polished, premium |
cubic-bezier(0.68,-0.55,0.265,1.55) | Overshoot + bounce | Playful icon reveals | Spring, bouncy |
steps(N, end) | Stepped jumps | Frame-by-frame sprite animation | Cinematic, retro |
linear(0,0.2,0.8 50%,1) | Custom piecewise | Complex spring physics approximation | Engineering precision |
linear() timing function accepts a list of points and optionally their positions, allowing you to encode complex spring/bounce physics as a pure CSS easing curve — something previously impossible. Use it to approximate GSAP Elastic or Bounce eases without any JavaScript.7 Real-World Usage Patterns & Recipes
Beyond the basics, scroll-driven animations unlock a wide range of production-grade UI patterns. Here are the most commonly needed recipes with production-ready code.
Pattern 1 — Reading Progress Bar
Pattern 2 — Sticky Header Opacity Fade
Pattern 3 — Staggered Card Reveal
Pattern 4 — Parallax Hero Image
8 Browser Support, Polyfills & Progressive Enhancement Strategy
As of mid-2026, animation-timeline is natively supported in Chromium-based browsers representing approximately 65–70% of global web traffic. Firefox and Safari are in various stages of implementation. For production use today, a layered progressive enhancement strategy is recommended.
Layer 1 — CSS @supports Feature Detection
The most resilient approach is to use @supports to apply scroll animations only where natively supported, with a static fallback for other browsers:
Layer 2 — Google Chrome Labs Polyfill
For teams that need scroll animations to work identically across all browsers today, the official polyfill from Google Chrome Labs provides complete API coverage using IntersectionObserver and requestAnimationFrame as the fallback engine:
Layer 3 — IntersectionObserver for Older Browsers
For browsers that support neither native SDA nor the polyfill well (very old Firefox versions, IE 11), IntersectionObserver with a simple class toggle provides a usable fallback with CSS transitions:
9 CSS Scroll-Driven Animations vs GSAP ScrollTrigger — Detailed Comparison
GSAP ScrollTrigger has been the industry standard for scroll-linked animations since 2020. With native CSS SDA now shipping, many teams are evaluating whether they can replace GSAP. The answer depends heavily on use-case complexity.
| Criteria | CSS Scroll-Driven Animations | GSAP ScrollTrigger |
|---|---|---|
| JavaScript required | ✓ None | ✗ Required |
| Bundle size impact | ✓ 0 KB | ~67 KB (GSAP + ST) |
| Compositor thread | ✓ (transform/opacity) | ✗ Main thread |
| Browser support (2026) | ~70% native, polyfill for rest | ✓ All browsers |
| Scroll-linked pinning | ✗ Limited | ✓ Full support |
| Horizontal scroll sections | ⚠ Complex | ✓ Built-in |
| Callbacks / JS hooks | ✗ CSS-only | ✓ onEnter, onLeave, etc. |
| Ease authoring | cubic-bezier, linear() | Hundreds of named easings |
| DevTools integration | ✓ Chrome Animations panel | ⚠ Limited |
| Cost | ✓ Free (web standard) | Free (non-commercial) / $150+ commercial |
10 Accessibility — Respecting prefers-reduced-motion
Scroll-driven animations can cause significant discomfort for users with vestibular disorders, motion sensitivity, or epilepsy. The W3C recommends that any motion animation be suppressible by the user's OS-level Reduce Motion setting, exposed in CSS via the prefers-reduced-motion media query.
This is not optional for WCAG 2.1 AAA compliance and is strongly recommended for AA. All scroll-driven animations should respect this preference:
For scroll-driven animations, the most semantically correct approach is to remove the animation-timeline and ensure the element's final state (visible, no transform) is preserved:
11 Performance Impact on Core Web Vitals & SEO
CSS Scroll-Driven Animations have a direct, measurable impact on Core Web Vitals — Google's primary performance signals used in ranking. Understanding this impact allows you to use the API to improve scores rather than hurt them.
| Core Web Vital | Impact of CSS SDA | Compared to JS Scroll Animations |
|---|---|---|
| LCP (Largest Contentful Paint) | Neutral — no render-blocking | Better — no parser-blocking scripts |
| INP (Interaction to Next Paint) | ✓ Positive — compositor thread | Better — JS scroll listeners inflate INP |
| CLS (Cumulative Layout Shift) | ⚠ Risk if animating layout props | Same risk applies to JS animations |
| FID / TBT (Total Blocking Time) | ✓ Positive — zero JS parsing | Better — no JS runtime overhead |
width, height, margin, padding, top, left) with scroll-driven animations will trigger Layout recalculation and may cause Cumulative Layout Shift, directly harming your CLS score. Always animate only transform and opacity to avoid CLS and maximize compositor performance.From a pure SEO perspective, CSS SDA can improve your search ranking by:
- Reducing JavaScript payload — eliminating scroll animation libraries reduces parse/execute time, improving TBT and indirectly LCP.
- Improving INP — scroll listeners and rAF callbacks add microtask queue pressure. Replacing them with CSS SDA eliminates this entirely for supported browsers.
- Enhancing engagement metrics — smooth scroll animations improve time-on-page and reduce bounce rate, both behavioral signals Google measures.
- Improving perceived performance — progressive reveal animations make pages feel faster even if technical metrics are similar.
12 Debugging CSS Scroll-Driven Animations in Chrome DevTools
Chrome DevTools (115+) includes a dedicated Animations panel with full support for visualizing and scrubbing scroll-driven animations — a massive advantage over JavaScript-based animation debugging.
Opening the Animations Panel
- Open Chrome DevTools (F12 or Cmd+Opt+I)
- Click the three-dot menu → More tools → Animations
- Or press Ctrl+Shift+P → type "Animations" → select "Show Animations"
Key DevTools Features for SDA
| Feature | How to Use |
|---|---|
| Scroll Timeline Scrubber | In the Animations panel, scroll-driven animations show a scrubber. Drag it to manually advance the animation timeline without scrolling. |
| Animation Delay/Duration Edit | Click any animation bar to edit its duration or delay inline — changes reflect live in the canvas. |
| Easing Curve Editor | Click the cubic-bezier icon on any animation to open the graphical curve editor — adjust handles and see the animation update in real-time. |
| Compositor Layer Inspection | In Layers panel, verify your animated element is on its own compositor layer (shows as a separate green/blue rectangle). |
| Performance Timeline | Record a performance trace while scrolling. Look for "Composite" tasks on the GPU Process thread — scroll-driven CSS animations should appear there, NOT in the main thread flame chart. |
will-change: transform (use sparingly — it consumes GPU memory). Check "Why this layer was composited?" in the sidebar for the exact reason.
Common Debugging Scenarios
| Symptom | Likely Cause | Fix |
|---|---|---|
| Animation doesn't start | animation-fill-mode missing / wrong scroll container | Add both to animation shorthand; check overflow on ancestors |
| Animation snaps at end | Missing fill-mode: both | Use animation: name linear both |
| Animation jank on scroll | Animating layout-triggering properties | Stick to transform and opacity only |
| Works in Chrome, not Safari | Safari partial support | Add polyfill script or use @supports with static fallback |
| Animation-range not working | Using scroll() instead of view() | animation-range only applies to view() timelines |
13 Advanced Techniques, Patterns & Upcoming CSS Features
Beyond the core primitives, CSS Scroll-Driven Animations unlock a set of powerful advanced patterns that are already used in production by companies like Apple, Google, and Stripe. Here we cover the cutting-edge techniques — including features landing in browsers in 2025–2026 — that will define the next generation of scroll-driven UI.
Pattern 5 — Pure CSS Scroll-Snapped Carousel
Combining scroll-snap-type with a scroll() timeline enables a snapped slide carousel with a native dot indicator that updates position without any JavaScript:
Pattern 6 — Scroll-Linked Color Theme Transition
Animate CSS custom properties to smoothly transition entire page themes (background, text, accent colors) as the user scrolls through distinct content sections — a technique that was previously JavaScript-only:
Pattern 7 — Multi-Stage Animation with Keyframe Stops
You're not limited to from and to. Use percentage-stop keyframes to create multi-stage scroll animations — for example, an element that fades in as it enters, holds visible in the center, then fades out as it exits:
Upcoming: Scroll-Driven Animations in CSS Nesting & Layers
Two upcoming CSS features will make scroll-driven animations even more composable:
- CSS Nesting (Chrome 112+): Combine
@keyframesdeclarations directly inside rule blocks with native CSS nesting, eliminating the need to hoist keyframe declarations to the global scope. - CSS @layer: Scope your scroll animation rules inside a cascade layer (
@layer animations { }) for better specificity management in design systems — crucial when adding scroll animations to component libraries. - view-timeline-inset: An upcoming property that adjusts the scrollport boundaries for view() timelines, similar to IntersectionObserver's
rootMargin. Use it to trigger animations earlier or later than the natural viewport boundary:view-timeline-inset: 100pxwill trigger the animation 100px before the element enters the natural viewport. - CSS Scroll State Queries (Level 5): A proposed addition that would allow container queries to respond to scroll state — e.g., whether a sticky element is currently stuck. This would enable CSS-native sticky header style changes without JavaScript.
AEO & GEO Optimization: Direct Answer for AI Search
CSS Scroll-Driven Animations are best used for: reading progress bars (use scroll(root block) with a width keyframe on a fixed element), reveal-on-scroll card grids (use view() with animation-range: entry on each grid item), parallax backgrounds (use view() with animation-range: cover and opposing translate values), and sticky header blur transitions (use scroll(root block) with animation-range: 0px 200px and backdrop-filter keyframes). All these patterns require zero JavaScript, run on the GPU compositor thread, and produce no Cumulative Layout Shift when restricted to transform and opacity properties.
transform/opacity for compositor performance; (2) animation-fill-mode: both is set; (3) prefers-reduced-motion override is present; (4) @supports fallback or polyfill is configured; (5) test in Chrome Animations DevTools panel; (6) validate with Chrome Lighthouse to confirm no CLS regression.