Shots in this scene
Scroll Progress Timelines
In one line
A scroll progress timeline ties an animation to how far a scroller has scrolled: 0% at the top, 100% at the end. animation-timeline: scroll() does it in one line, with no scroll listener, and the animation has no duration in seconds.
Think of it like this
A book's thumb-index. When you riffle the pages, the position of the pages decides what you see, not a clock. Stop riffling and the picture stops. Flip back and it goes backwards. A scroll-driven animation: the scroll bar is the clock.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- timeline
- The thing that says what time it is for an animation. The document timeline counts milliseconds; a scroll timeline counts scroll progress.
- scroll progress timeline
- A timeline made from a scroller's offset: 0% at the start, 100% at the end. Written
scroll(). - block axis
- The direction in which blocks of text stack. In English it is vertical.
inlineis the direction text runs: horizontal. - inactive timeline
- A scroll timeline with no range because there is nothing to scroll. Its progress is
nulland the animation has no effect.
Step by step
01Press Middle and read the track
In "A reading bar from the scroll position" press Middle: the bar fills to half and the track is a Scroll track at 50%. End gives 100%.
02Pause the clock and scroll again
Press Pause, then Middle. The bar still moves: the scroll bar is its clock.
03Read the console
It says CSSAnimation on a ScrollTimeline, source the scroller, axis block, duration auto. scroll() chose all of it.
04Open the five cases
In "Five scrollers, five outcomes" the short box says none, the 10 s bar behaves like the others, and the bad-order bar is full at load.
Try it yourself
- 1
Make the bar start late and finish early
In "A reading bar…" add
animation-range: 25% 75%;after the timeline line. Measured by scrolling to 0, 25%, 50%, 75% and 100% of the way: the bar is at 0, 0, 0.50, 1 and 1. The bar waits through the first quarter and is full for the last quarter.animation-rangepicks which part of the scroll the keyframes spread over (Topic 12.2 explains it).
Code & diagrams
The bar is sticky at the top of a 140 px scroller. Press Top, Middle and End, or scroll with the mouse, and read the Scroll track.
Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.
Console
Nothing logged yet.Expected console output
supports scroll(): true
animations: 1 CSSAnimation on ScrollTimeline
source is the scroller: true · axis: block
animation-duration: autoEach box has a bar. The numbers are the timeline's progress. The last box wrote `animation-timeline` before the `animation` shorthand.
Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.
Console
Nothing logged yet.Expected console output
at load: tall 0%, short none, sideways 0%, 10 s 0%
scrolled to the end: tall 100%, short none, sideways 100%, 10 s 100%
bad order: DocumentTimeline, bar already full: trueBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Put animation-timeline before the shorthand
In "A reading bar…" swap the two lines so the CSS reads animation-timeline: scroll(); animation: fill linear both;.
Myth vs fact
Myth
Scroll-driven animations are the same as listening for the scroll event.
Fact
A listener runs your JavaScript on the main thread. A scroll timeline is declared in CSS and the browser applies it. In Chrome it can run off the main thread for transform and opacity.
Remember this
- 1
The problem this solves: to show reading progress, people used to listen for
scrollevents and set a width on each one. That runs on the main thread and stutters when the page is busy.animation-timelinelets the browser tie an animation to the scroll position itself. (The CSS course, Topic 10.6, shows this in a few lines; here are the rules underneath.) - 2
A timeline says what time it is. Every animation so far used the document timeline: milliseconds since the page started.
animation-timeline: scroll()swaps it for a scroll progress timeline: its "time" is the scroll offset as a percentage, 0% at the scroller's start and 100% at its end. The keyframes are spread over that range. - 3
**
scroll()takes two choices:**scroll(<scroller> <axis>). The scroller isnearest(the closest ancestor that scrolls, the default),root(the page) orself. The axis isblock(the direction text stacks, usually vertical, the default),inline,xory. The first example writesscroll()and gets the nearest scroller and the block axis: its console proves both. - 4
There is no duration in seconds. The duration is
auto: the whole 0 to 100%. If you writeanimation: fill 10s linear both, the10sis ignored (see the "10 s" box in the second example). Easing still applies to the scroll progress, soease-inmakes a bar lag: uselinear. - 5
Order matters. The
animationshorthand resetsanimation-timelinetoauto(the document timeline). Writeanimation-timelineafter the shorthand. Get it wrong and nothing is reported: the animation runs on the clock with duration 0, so the bar is full at once. - 6
Edge cases. A scroller with nothing to scroll has no range: its timeline is inactive, its progress is
null, the animation does not apply, and the element shows its own style (an empty bar). A sideways scroller needs theinlineaxis. And an element needs a scroller above it: the bar isposition: stickyinside the box, so its nearest scroller is the box. - 7
Support and a safe fallback.
animation-timelineworks in about 88% of browsers in use (caniuse, 2026-10-24: 87.84%; Chrome 115, Safari 26, Firefox 159). Write the effect inside@supports (animation-timeline: scroll()): the page must be complete without it. In Chrome a scroll-driventransformoropacityanimation can run off the main thread (Chrome's documentation says so; this course cannot measure it). On the Motion Timeline these areScrolltracks, and Pause does not stop them: only scrolling moves them.
Explain it without notes
How is a scroll progress timeline different from the document timeline?
Why is animation-duration useless with animation-timeline: scroll(), and what do you use instead?
Practice
Make a bar that follows the sideways scroll of a gallery row (overflow-x: auto), not the page's vertical scroll.
Write the @supports and reduced-motion rules for a progress bar that fills with the page's scroll, so that without support or with reduced motion it is not shown at all.
Done when you can
You can write
animation-timeline: scroll()and say which scroller and axis it picksYou know why the
animationshorthand must come beforeanimation-timelineYou can explain what happens with nothing to scroll
You wrap the effect in
@supportsand a reduced-motion rule