Scroll progress bar
A bar at the top fills as you read down the page. Pure CSS with animation-timeline: scroll(), plus a JS fallback.
CSSJavaScriptBeginner
<div class="bar" id="bar"></div>
<article>
<h1>Keep reading</h1>
<p>The bar above tracks how far down you are. Scroll inside this frame.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
<p>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
<p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p>
<p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
<p>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
<p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p>
</article>
body{display:block;height:auto;padding:60px 0}
.bar{position:fixed;left:0;top:0;height:5px;width:100%;background:linear-gradient(90deg,#f08a5d,#a8523a);
transform-origin:0 50%;transform:scaleX(0);z-index:9}
@supports (animation-timeline:scroll()){
.bar{animation:grow linear both;animation-timeline:scroll(root)}
@keyframes grow{to{transform:scaleX(1)}}
}
article{width:min(460px,88vw);margin:0 auto;color:#cfc3c1}
article h1{color:#fff}
// Fallback for browsers without scroll-driven animations
if (!CSS.supports('animation-timeline: scroll()')) {
const bar = document.getElementById('bar');
addEventListener('scroll', () => {
const max = document.documentElement.scrollHeight - innerHeight;
bar.style.transform = `scaleX(${max > 0 ? scrollY / max : 0})`;
}, { passive: true });
}
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Scroll progress</title>
<style>
*{box-sizing:border-box}
html,body{margin:0;height:100%}
body{display:grid;place-items:center;background:#140807;color:#fffffc;font:16px/1.5 system-ui,-apple-system,"Segoe UI",sans-serif}
body{display:block;place-items:normal;height:auto;padding:60px 0}
.bar{position:fixed;left:0;top:0;height:5px;width:100%;background:linear-gradient(90deg,#f08a5d,#a8523a);
transform-origin:0 50%;transform:scaleX(0);z-index:9}
@supports (animation-timeline:scroll()){
.bar{animation:grow linear both;animation-timeline:scroll(root)}
@keyframes grow{to{transform:scaleX(1)}}
}
article{width:min(460px,88vw);margin:0 auto;color:#cfc3c1}
article h1{color:#fff}
</style></head>
<body>
<div class="bar" id="bar"></div>
<article>
<h1>Keep reading</h1>
<p>The bar above tracks how far down you are. Scroll inside this frame.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
<p>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
<p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p>
<p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
<p>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
<p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p>
</article>
<script>
// Fallback for browsers without scroll-driven animations
if (!CSS.supports('animation-timeline: scroll()')) {
const bar = document.getElementById('bar');
addEventListener('scroll', () => {
const max = document.documentElement.scrollHeight - innerHeight;
bar.style.transform = `scaleX(${max > 0 ? scrollY / max : 0})`;
}, { passive: true });
}
</script>
</body></html>
Good for
Articles, docs and long landing pages.
Watch out for
animation-timeline is Chrome, Edge and Safari 26+. The small script runs only where it is not supported.
Accessibility
Wrap motion in @media (prefers-reduced-motion: reduce) and switch it off or shorten it for people who ask for less movement.