animation-timeline: scroll()
A progress bar tied to scroll, with no JavaScript
Scroll this panel. The bar at the top is driven by scroll position, not by a timer and not by a scroll listener.
Nothing measures anything. There is no JavaScript on this demo at all — the animation's timeline is the scroller itself.
Which also means it cannot drift, cannot fire late, and costs nothing on the main thread while you scroll.
Keep going.
Almost there.
That is the end of the panel, and the bar is full.
CaveatAn animation whose timeline is unsupported does not simply switch off — it still applies its fill state. Ship this unguarded and browsers without scroll timelines show a permanently full progress bar.
FallbackHide the bar by default and reveal it inside @supports (animation-timeline: scroll()). No bar reads as a design decision; a stuck full one reads as a broken page.
.bar {
transform-origin: 0 50%;
animation: fill linear both;
animation-timeline: scroll(nearest);
}
@keyframes fill {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
.track { display: none; }
@supports (animation-timeline: scroll()) {
.track { display: block; }
}
Published