JavaScript scroll handlers run on the main thread and spend their lives being throttled. Scroll-driven CSS animations run in the compositor: the animation's progress IS the scroll position, so a reading bar costs two properties and zero listeners.
.reading-bar {
position: fixed; inset: 0 0 auto 0; height: 3px; background: var(--brand);
transform-origin: 0 50%; transform: scaleX(0);
animation: reading linear;
animation-timeline: scroll(root block);
}
@keyframes reading { to { transform: scaleX(1); } }Where it fits, where it does not
- Article text that scrolls one document: perfect fit, the bar mirrors exactly what the reader sees.
- Multi-column dashboards or scroll containers inside pages: scope the timeline to the container, not the root, or it lies.
- Support is Chromium-and-Safari era; browsers without it draw no bar. A page minus a bar loses nothing.
Take color from the brand and motion from nothing else. A bar that tracks scroll quietly is a feature; a bar that fades in and out at 60Hz is a demo. This site's measured LCP stayed identical after adding nothing at all, which is the honest feature request behind most progress bars.
