Scroll to fill the bar
The bar above tracks how far through this panel you are, and reaches 100% exactly as the last line reaches the bottom.
It is driven by one scroll listener, throttled with requestAnimationFrame so a trackpad flick writes once per frame rather than dozens of times per frame.
Every layout read happens before every write. Interleaving them forces the browser to recompute layout mid-frame, which is what turns a progress bar from free into a jank source.
The travel is the content height minus one viewport height. That subtraction is the part everyone leaves out.
Without it, the bar tops out around 80% on a long document and never fills — which reads as broken rather than as a design choice.
It is clamped at both ends, because rubber-band scrolling on iOS reports a negative position at the top and an over-height one at the bottom.
A ResizeObserver watches the panel too. Height changes without a scroll — images loading, a font swapping, a filter hiding rows — would otherwise leave the bar stale until the next move.
The bar is aria-hidden. It restates what the scrollbar already carries natively and more precisely, so it earns its place visually rather than semantically.
Announcing "thirty-seven percent" to a screen reader would be noise on top of a control that user already has.
On a real page this shares its listener with the scroll-to-top button and anything else that needs scroll position.
Two listeners each doing their own rAF throttle means two callbacks, two layout reads and two chances to force a synchronous reflow on every frame of every scroll.
On the one page type where scrolling smoothly is the entire product, that is not a trade worth making.
Keep scrolling — there is a little more, so the bar has somewhere left to go.
That is the end. The bar should be full.