Skip to content

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.

html
<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" />
ts
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-scroll uses the nearest scroller; for a page-wide bar this is the document.
  • anim-range-cover makes the animation span the entire scrollable range.
  • origin-left ensures scaleX grows from the leading edge.

Per-section progress

Pair with a named view timeline to show a bar that tracks one specific section:

html
<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" />

Released under the MIT License.