Reading Progress Bar Widget

Add a reading progress bar that fills along the top of the window as visitors work through your article. It tracks the article rather than the whole page, so the comments and footer do not drag it out, and a circle variant can show the minutes left instead of a percentage.

Website Essentials Website component No trackers, no cookies Free · no ads

Customize your widget

Indicator
2-10.
Colour
Used when Colour is Custom.
What it measures
CSS selector. Blank = the first article, else main, else the whole page.
Lower it if your sticky header should cover the bar.
Light or dark
Match page reads the background behind the component.

Live preview

Exactly what your visitors will see

Embed code

<div data-a2z-kit="reading-progress-bar"></div>
<script async src="https://a2z.tools/embed/kit.js" referrerpolicy="no-referrer"></script>

One small script from A2Z draws the component on your page inside its own Shadow DOM. It loads no social-network or tracking scripts and sets no cookies. How the A2Z kit works.

Works with

How it works

The kit appends a fixed element to your page and, on each animation frame after a scroll or resize, measures the target element - your selector, else the first article, else main, else the whole document. Progress is zero while the top of that element is still below the top of the window and reaches one when its last line arrives at the bottom of the window. The bar is scaled with a CSS transform, so your layout never moves and nothing is pushed down. It is decorative and hidden from assistive technology: a screen-reader user already knows where they are, and a percentage that changes on every scroll would only interrupt them. The circle variant shows the percentage or, with minutes left switched on, the words still ahead divided by 238 words per minute.

Method

  • progress = -top / (height - window height), clamped to 0..1, with top and height from the target's getBoundingClientRect()
  • Target shorter than the window: progress = (window height - top) / height, which is 1 once its bottom is in view
  • Target = your CSS selector, else the first article, else main, else the document (scrollY against scrollHeight - innerHeight)
  • Bar: transform: scaleX(progress) on a position:fixed element, so there is no layout shift
  • Minutes left = ceil(words in the target x (1 - progress) / 238), words counted once on first use, nav/aside/footer excluded

Worked examples

A 3,000 px article on a 1,000 px tall screen

Inputs: Default bar; the reader has scrolled 1,000 px past the article's top

Result: The bar is half full

progress = 1,000 / (3,000 - 1,000) = 0.5; it was empty while the article's top was below the top of the window and is full when its last line reaches the bottom.

Circle with minutes left

Inputs: variant circle, minutes left on, a 2,380-word article half read

Result: The circle shows '5 min left'

2,380 x (1 - 0.5) = 1,190 words; 1,190 / 238 = 5 minutes, rounded up.

Limitations

  • Pages that scroll inside an inner container instead of the window are not tracked.
  • On infinite-scroll pages the bar jumps back when more content is added below the target.
  • Minutes left counts the words once; text added later is not included, and Chinese and Japanese characters are each counted as one word.

Where publishers use it

  • A long-form magazine feature where a 3 px gradient line under the browser's address bar shows readers how much is left
  • A tutorial blog whose posts end in long comment threads, measuring progress through the article element only
  • A study-notes site using the circle with 'minutes left' so students can decide whether to finish a section now
  • A dark-themed portfolio using a custom brand colour for the bar at the bottom edge so it does not touch the sticky menu

Questions

Why measure the article instead of the whole page?

A progress bar that counts the footer, related posts and comments reaches 100% long after the reader has finished - or never seems to move on pages with long comment threads. Measuring from the top of the article to its last line matches what readers think of as 'the post'. Point the bar at another element with a CSS selector such as .post-body if your theme does not use article.

Do screen readers announce the bar?

No, deliberately. It is marked aria-hidden because it only repeats what the scroll position already tells everyone, and a live percentage would interrupt reading. The page is not changed in any way a screen reader would notice.

Will it sit under or over my sticky header?

By default it is drawn above nearly everything (z-index 2147483000) along the very top edge. If your header should cover it, lower the z-index option; if it clashes visually, move the bar to the bottom edge.

How is 'minutes left' worked out?

The words inside the article are counted once, skipping navigation, asides, footers and A2Z components, then multiplied by the share still unread and divided by 238 words per minute - the average silent reading rate for English non-fiction in Brysbaert's 2019 meta-analysis - and rounded up.

Sources

  1. How many words do we read per minute? A review and meta-analysis of reading rate - Marc Brysbaert, Journal of Memory and Language 109 (2019) 104047 . Silent reading of English: 238 words per minute on average for non-fiction and 260 for fiction, from 190 studies with 18,573 participants.

Cite or recommend this tool

If you reference this tool in an article, course or documentation, these formats are ready to copy. They are optional - nothing is added to your site unless you paste it.

A2Z Tools Reading Progress Bar
https://a2z.tools/embed/reading-progress-bar
  • Back to Top Button

    Website Essentials Website component New

    A scroll-to-top button that waits until readers scroll, with a progress ring and keyboard-friendly focus.

    Get code
  • Estimated Reading Time

    Website Essentials Website component New

    Shows "6 min read" on your post, counted from the page's own text at a researched 238 words per minute.

    Get code
  • Table of Contents

    Website Essentials Website component New

    An automatic table of contents from your headings, with active-section highlight and sticky-header offset.

    Get code
  • Listen to Article Button

    Website Essentials Website component New

    A Listen button that reads your article aloud with the browser's built-in voices - no audio files, no account.

    Get code
  • Last Updated Date

    Website Essentials Website component New

    "Updated 3 October 2026" from your page's real modified date - meta tags or JSON-LD - never the load time.

    Get code
  • Auto-updating Copyright Year

    Website Essentials Website component New

    A footer line like © 2018–2026 Riverside Cafe that moves to the new year on its own every January.

    Get code

Preview