Introduction
Scroll Progress
A thin indicator that reports how far the reader has moved through a page or a scroll container. It sits quietly at the edge of the viewport and fills as the content advances.
Why it exists
A sense of position on long pages
Long articles hide their own length. A progress bar gives readers a continuous cue for how much remains, without adding another block of chrome to the layout.
The signal
One value, mapped to width
The component tracks a single scalar between zero and one and maps it to the fill. Everything else — color, thickness, placement — is presentation on top of that value.
Placement
Pinned to the top by default
The indicator is fixed to the top edge so it stays visible while the content scrolls beneath it. It can also be attached to the bottom or to a specific container.
Interaction
Behavior
The bar follows scroll position directly. It should feel like a physical readout of the page rather than an animation that plays on its own.
Tracking
Progress is derived, not stored
Scroll offset divided by the total scrollable distance gives the progress value. Because it is derived on every frame, it stays correct through resizes and content changes.
Smoothing
A spring keeps the fill from stuttering
Raw scroll values can arrive in jumps. Passing the value through a spring smooths the fill so it glides toward the target instead of snapping between frames.
Performance
Animate transform, never layout
The fill is driven by scaleX with a left transform origin. Scaling avoids layout work on every scroll event and keeps the indicator smooth on long documents.
Visual System
Styling
The default look is deliberately minimal: a few pixels tall, full width, and tied to the foreground color so it reads clearly in both themes.
Thickness
Keep it thin
Two to four pixels is enough to register in peripheral vision. A heavier bar competes with the content and starts to feel like a loading state rather than a position cue.
Contrast
Lean on the foreground token
Using the foreground color keeps the bar legible without a hardcoded value, so it inverts correctly when the theme flips between light and dark.
Direction
Fill from the leading edge
A left transform origin makes the bar grow in the reading direction. For right-to-left layouts the origin flips so the motion still matches the flow of text.
Implementation
Usage
Drop the component near the top of a scrollable layout and it works with no configuration. Everything past that is optional refinement.
Mounting
One instance per scroll surface
Render a single indicator for the page. If you have an independent inner scroll area, give that region its own instance pointed at the container.
Layout
Use className for placement and color
The component owns the tracking logic; the surrounding page controls where the bar sits and how it is themed through a className.
Accessibility
Respect reduced motion
The progress bar is decorative, so it should not be the only indicator of position. When reduced motion is requested, drop the spring and track scroll directly.