Scroll progress bar
A thin bar fixed to the top of the page that fills as the user scrolls.
Live demo
Scroll the box below. The bar at the top fills based on the scroll progress of this scroller, driven by a named scroll-timeline.
Line 1 — scroll inside this box to fill the bar above.
Line 2 — scroll inside this box to fill the bar above.
Line 3 — scroll inside this box to fill the bar above.
Line 4 — scroll inside this box to fill the bar above.
Line 5 — scroll inside this box to fill the bar above.
Line 6 — scroll inside this box to fill the bar above.
Line 7 — scroll inside this box to fill the bar above.
Line 8 — scroll inside this box to fill the bar above.
Line 9 — scroll inside this box to fill the bar above.
Line 10 — scroll inside this box to fill the bar above.
Line 11 — scroll inside this box to fill the bar above.
Line 12 — scroll inside this box to fill the bar above.
Line 13 — scroll inside this box to fill the bar above.
Line 14 — scroll inside this box to fill the bar above.
Line 15 — scroll inside this box to fill the bar above.
Line 16 — scroll inside this box to fill the bar above.
Line 17 — scroll inside this box to fill the bar above.
Line 18 — scroll inside this box to fill the bar above.
Line 19 — scroll inside this box to fill the bar above.
Line 20 — scroll inside this box to fill the bar above.
<div class="fixed top-0 left-0 z-50 h-1 w-full bg-blue-500
origin-left scale-x-0
animate-grow-x animate-fill-mode-both
timeline-scroll anim-range-cover" />export default defineConfig({
theme: {
animation: {
keyframes: {
'grow-x': '{from{transform:scaleX(0)}to{transform:scaleX(1)}}',
},
durations: { 'grow-x': '1s' }, // ignored when bound to a timeline
timingFns: { 'grow-x': 'linear' },
counts: { 'grow-x': '1' },
},
},
})Notes
timeline-scrolluses the nearest scroller; for a page-wide bar this is the document.anim-range-covermakes the animation span the entire scrollable range.origin-leftensuresscaleXgrows from the leading edge.
Per-section progress
Pair with a named view timeline to show a bar that tracks one specific section:
<section class="view-timeline-article">
…
</section>
<div class="fixed top-0 inset-x-0 h-1 origin-left scale-x-0
animate-grow-x animate-fill-mode-both
timeline-article anim-range-cover" />