Add a sticky progress bar that fills as users scroll your page, using CSS scroll-timeline or JavaScript. Live demo, configurable, copy the code.
A scrollable container with scroll-timeline-name: --pb-tl drives a sticky bar's fill animation. No JavaScript; the bar's width tracks scroll position via animation-timeline: --pb-tl. Scroll inside the demo to see it fill.
Listens to the scroll event on the viewport container. Calculates progress as scrollTop / (scrollHeight - clientHeight) * 100 and updates the bar's width directly. Works in all browsers including Firefox.
On fullscreen section-based sites, a progress bar can't use scrollTop. Instead, it tracks which section you're on, e.g. section 2 of 5 = 40%. Scroll inside the preview to see it in action.
Scroll inside the preview to see the progress bar track fullscreen sections
The CSS approach defines a named Scroll Progress Timeline on the scroll container. Any element within (or referencing) that container can then animate based on the container's scroll position.
/* 1. Give the scroll container a named timeline */
.article {
overflow-y: scroll;
scroll-timeline-name: --article-tl;
scroll-timeline-axis: block;
position: relative;
}
/* 2. Sticky bar sits at the top */
.progress-bar {
position: sticky;
top: 0;
height: 4px;
background: rgba(255,255,255,.1);
z-index: 100;
}
/* 3. Fill animates from 0% to 100% */
.progress-bar-fill {
height: 100%;
background: #3B82F6;
animation: fillBar linear both;
animation-timeline: --article-tl;
}
@keyframes fillBar {
from { width: 0%; }
to { width: 100%; }
}
/* ── Page-wide version ── */
html { scroll-timeline-name: --page-tl; }
.page-progress { animation-timeline: --page-tl; }
| CSS scroll-timeline | JavaScript (scroll event) | |
|---|---|---|
| Browser support | Chrome, Edge, Safari 26+ only> | All browsers |
| Performance thread | Compositor (off main thread) | Main thread |
| Code complexity | CSS only, ~15 lines | ~10 lines JS |
| Scroll direction reversal | Automatic | Automatic |
| Custom easing | linear only | Any calculation |
| Multiple containers | Named timelines | Multiple listeners |
Use CSS for new projects targeting modern browsers. Use JavaScript if you need Firefox support or more control over the easing and calculation logic.
scroll-timeline-name: --tl to your scroll container (html for page-wide). Create a sticky bar at the top with a fill element that has animation: fillBar linear both; animation-timeline: --tl;. The keyframes animate width from 0% to 100%.scroll-timeline-name creates a named Scroll Progress Timeline on a scroll container. Descendants reference it via animation-timeline: --your-name, linking their animation progress directly to the container's scroll position.container.addEventListener('scroll', () => { const pct = container.scrollTop / (container.scrollHeight - container.clientHeight) * 100; bar.style.width = pct + '%'; });. For page-wide, use window.scrollY and document.body.scrollHeight - window.innerHeight.scroll-timeline is not enabled in Firefox by default. Use the JavaScript approach for Firefox support, or use CSS.supports('animation-timeline', 'scroll()') to detect support and apply a JavaScript fallback when needed.Add scroll transitions, parallax and animations to your site with fullPage.js. One component, 80+ effects.
Powering fullscreen design for Google, Sony, BBC, eBay & more