Reading Progress

A pinned bar that tracks how far through the page, or a chosen element, the reader is.

The bar is live at the very top of this page. Scroll and it fills.

Installation

npx shadcn@latest add @loomui/reading-progress

No keyframes to add. The bar is driven by a passive scroll listener.

Usage

import { ReadingProgress } from "@/components/ui/reading-progress"
<ReadingProgress />

Measure one article rather than the whole document by passing a ref:

const article = React.useRef<HTMLElement>(null)
 
return (
  <>
    <ReadingProgress target={article} />
    <article ref={article}>{/* ... */}</article>
  </>
)

The bar scales on the x axis from its left edge, so nothing about it touches layout. There is no transition on it at all. Scrolling is the highest-frequency input a page has, and a bar that eases toward the scroll position just reads as lag.

Props

PropTypeDefaultDescription
position"top" | "bottom""top"Which edge the bar pins to.
thicknessnumber2Bar thickness in pixels.
targetRefObject<HTMLElement | null>noneMeasure this element, not the document.
disabledbooleanfalseRender nothing.
classNamestringnoneMerged onto the track. Colour goes here.

Any other <div> prop is forwarded.

Accessibility

The bar is aria-hidden and pointer-events-none. It duplicates what the scrollbar already conveys, and a progressbar role whose aria-valuenow cannot update on every frame announces worse than nothing. There is also nothing to disable for reduced motion, because nothing is animated: the bar follows the scroll position exactly.