Reading position
The indicator above is driven by this box, not by a listener. Its animation is attached to a scroll progress timeline, so the browser maps the scroll offset of the nearest scroll container onto the animation and keeps the two in step itself.
Nothing here runs on the main thread. There is no scroll handler, no requestAnimationFrame loop and no throttle, which is the usual reason a progress bar on a heavy page lags a frame or two behind the content it is supposed to be measuring.
Because the timeline is the scrollbar itself, the indicator is exact at both ends by construction. It reads zero at the top and one at the bottom, and it cannot drift out of sync when the content resizes under it.
Scroll back up and the fill runs backwards at the same rate. A timeline is a mapping rather than a playback, so reversing the input reverses the output with no extra code.
That is the whole component. Two keyframes, one timeline and a sticky wrapper to keep the thing on screen while you read.
Reading position
The indicator above is driven by this box, not by a listener. Its animation is attached to a scroll progress timeline, so the browser maps the scroll offset of the nearest scroll container onto the animation and keeps the two in step itself.
Nothing here runs on the main thread. There is no scroll handler, no requestAnimationFrame loop and no throttle, which is the usual reason a progress bar on a heavy page lags a frame or two behind the content it is supposed to be measuring.
Because the timeline is the scrollbar itself, the indicator is exact at both ends by construction. It reads zero at the top and one at the bottom, and it cannot drift out of sync when the content resizes under it.
Scroll back up and the fill runs backwards at the same rate. A timeline is a mapping rather than a playback, so reversing the input reverses the output with no extra code.
That is the whole component. Two keyframes, one timeline and a sticky wrapper to keep the thing on screen while you read.
Scroll inside either box. Same timeline, two shapes.
Scroll Progress CSS
A reading progress bar driven by animation-timeline: scroll(), no JavaScript
Read the blog post