The two timelines
Scroll-driven animation comes in two flavours. The scroll progress timeline: the animation is bound to how far the scroller has travelled (a reading progress bar, a hero that compresses as you leave). The view progress timeline: the animation is bound to how far an element has travelled through the viewport (a section title that reveals as it enters). Both are declarative — no rAF loop, no scroll listener, no layout-thrash opportunity — which is the real gift.
/* reading progress: bound to the scroller */
@keyframes fill { from { scale: 0 1; } to { scale: 1 1; } }
.progress {
transform-origin: left;
animation: fill linear both;
animation-timeline: scroll(root);
}
/* section title: reveals as it crosses the viewport */
@keyframes rise { from { opacity: 0; translate: 0 24px; } }
.section h2 {
animation: rise linear both;
animation-timeline: view();
animation-range: entry 0% entry 40%;
}What it cannot do — today
- No scroll-linked easing curves: the animation follows the scroll linearly; you cannot make a section feel 'spring-loaded' against the scroll position without JavaScript sampling scroll velocity.
- No scrubbing other properties cheaply: animating anything except transform and opacity still runs layout/paint per scroll frame — the same GPU rule as every other animation applies.
- Timeline units and ranges are new and verbose; animation-range syntax (entry, exit, contain) still trips people, and browser prefixes have not fully settled.
- Fallbacks matter: in browsers without support the animation never runs — content stays visible only if you write the 'from' state as the resting state, which is exactly the honest pattern: no support means no animation, never no content.
Write the resting state first (content fully visible, progress at zero). Then layer the scroll-driven animation on top with @supports (animation-timeline: scroll()). No support = clean static page. Support = the enhancement. Scroll-driven motion must never be the only way content appears.
Where it genuinely beats JavaScript
A reading progress bar, a scroll-vignette that deepens as you leave the hero, an image that settles into place as it enters — these are one-liners in scroll-timeline and hundreds of lines of fragile listener code by hand. The bar is also perfectly synced by the browser: no rAF drift, no scroll-position rounding, no jank from a listener fighting the compositor. For atmosphere that tracks the scroll, the platform finally has the primitive.