Progress Bar
A native scroll progress indicator powered by CSS scroll-driven animations. The bar tracks the root scroll timeline and grows from left to right without JavaScript.
0 KB JavaScript
~20 CSS lines
scroll() timeline
fixed page indicator
Preview
Scroll through the container to see the progress bar
The code
The markup
<div class="scroll-progress" aria-hidden="true"></div>CSS mechanisms
/* ----------------------------------------------------------------------------
* Scroll progress bar
* ---------------------------------------------------------------------------- */
/**
* Hidden by default so unsupported browsers do not show a static bar.
* Custom properties expose the visual knobs without changing the mechanism.
*/
.scroll-progress {
--scroll-progress-size: 4px;
--scroll-progress-color: currentColor;
--scroll-progress-z-index: 100;
display: none;
}
/**
* Enable the bar only when the browser supports scroll-driven animations.
*/
@supports (animation-timeline: scroll(root block)) {
.scroll-progress {
position: fixed;
inset-block-start: 0;
inset-inline: 0;
z-index: var(--scroll-progress-z-index);
block-size: var(--scroll-progress-size);
background: var(--scroll-progress-color);
pointer-events: none;
transform: scaleX(0);
transform-origin: 0 50%;
animation-name: scroll-progress-grow;
animation-duration: 1ms;
animation-timing-function: linear;
animation-fill-mode: both;
animation-timeline: scroll(root block);
}
}
/**
* Endpoint for the progress animation: full width when fully scrolled.
*/
@keyframes scroll-progress-grow {
to { transform: scaleX(1); }
}Benefits
-
01.
No scroll listener
The progress value is driven by the browser’s scroll timeline. No JavaScript, no scroll events, no manual percentage calculation.
-
02.
Minimal markup
The component only needs one decorative element. The page content does not need to be wrapped or restructured.
-
03.
Transform based
The bar grows with
scaleX(), so the animation avoids layout changes while still reflecting the scroll position. -
04.
CSS fallback
Unsupported browsers can simply skip the indicator. The page remains fully usable because the bar is only a visual helper.
Current limitations
-
01.
Browser support
Scroll-driven animations are still progressive enhancement because Firefox currently requires a browser flag for
animation-timeline. -
02.
Decorative status
Without JavaScript, the bar cannot expose a live
aria-valuenow. Mark it witharia-hidden="true"and keep it visual only. -
03.
Root scroll only
scroll(root block)tracks the page viewport. For a nested scroll area, use another scroller value or a named scroll timeline.