How to Add a Reading Progress Bar to Your Blog With CSS and JavaScript

You’ve seen it on Medium, on major blogs, on documentation sites: a thin colored bar at the top of the page that fills up as you scroll down an article. It’s small, it’s subtle, and it dramatically improves the reading experience. In this tutorial, we’ll build a reading progress bar with CSS and vanilla JavaScript that you can drop into any blog, WordPress site, or custom project in under 5 minutes.

No frameworks. No libraries. Just clean code you can copy, paste, and style however you like.

What Is a Reading Progress Bar?

A reading progress bar is a visual indicator, usually fixed at the top of the viewport, that reflects how far a user has scrolled through the main content of a page. It answers a simple but powerful question for your readers: how much is left to read?

Benefits of adding one to your blog:

  • Reduces bounce rate on long-form articles
  • Sets clear expectations about article length
  • Provides a subtle, modern UX touch
  • Encourages users to keep scrolling
progress bar website

Two Approaches: CSS-Only vs. JavaScript

Before jumping into code, let’s compare the two main approaches so you can choose the right one for your project.

Feature CSS-Only (animation-timeline) JavaScript
Browser support Modern browsers only Universal
Performance Excellent (native) Very good with throttling
Flexibility Limited Full control
Track only article content Yes Yes

We’ll cover both. Start with the JavaScript version if you need broad support, or jump to the CSS-only version if you’re targeting modern browsers.

Step 1: Add the HTML Markup

The bar itself is just an empty div placed near the top of your body. Add this snippet right after your opening <body> tag:

<div id="reading-progress"></div>

That’s it. The div will be styled and updated dynamically.

Step 2: Style the Bar With CSS

Here is the base CSS. It fixes the bar at the top, gives it height, and prepares the fill state:

#reading-progress {
  position: fixed;
  top: 0;
  left: 0;
  height: 4px;
  width: 0%;
  background: linear-gradient(90deg, #ff6b6b, #f06595);
  z-index: 9999;
  transition: width 0.1s ease-out;
}

@media (max-width: 600px) {
  #reading-progress {
    height: 3px;
  }
}

Key styling choices explained:

  • position: fixed keeps the bar visible while scrolling
  • z-index: 9999 ensures it stays above sticky headers and modals
  • transition smooths out the fill animation
  • Media query reduces height on mobile for a lighter feel
progress bar website

Step 3: The JavaScript Logic

This is where the magic happens. Add this script before the closing </body> tag:

<script>
(function() {
  const bar = document.getElementById('reading-progress');
  const article = document.querySelector('article') || document.body;

  function updateProgress() {
    const rect = article.getBoundingClientRect();
    const total = rect.height - window.innerHeight;
    const scrolled = -rect.top;
    const percent = Math.min(Math.max((scrolled / total) * 100, 0), 100);
    bar.style.width = percent + '%';
  }

  let ticking = false;
  window.addEventListener('scroll', function() {
    if (!ticking) {
      window.requestAnimationFrame(function() {
        updateProgress();
        ticking = false;
      });
      ticking = true;
    }
  });

  updateProgress();
})();
</script>

What the script does:

  1. Selects the progress bar and the article container
  2. Calculates the total scrollable distance within the article
  3. Determines how much of that distance has been scrolled
  4. Updates the bar width using requestAnimationFrame for smooth performance

Note: the script targets the <article> element. This means the bar only fills while the user is inside the article, not while scrolling past headers or footers. If your theme doesn’t use an <article> tag, replace the selector with your content wrapper class, for example .entry-content or .post-content.

Step 4: The CSS-Only Alternative

If you only need to support modern browsers, you can skip JavaScript entirely using animation-timeline: scroll(). Here is the complete snippet:

#reading-progress {
  position: fixed;
  top: 0;
  left: 0;
  height: 4px;
  width: 100%;
  background: linear-gradient(90deg, #ff6b6b, #f06595);
  transform-origin: 0 50%;
  animation: progress linear;
  animation-timeline: scroll(root);
  z-index: 9999;
}

@keyframes progress {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

This uses the scroll-driven animations feature, which is supported in Chrome, Edge, and Opera. Firefox and Safari support is progressing, so treat this as progressive enhancement.

How to Add It to WordPress

There are three easy ways to integrate the code into a WordPress site.

Option 1: Via a Child Theme

  1. Add the CSS to your child theme’s style.css
  2. Add the JavaScript to a new file called progress.js in your child theme
  3. Enqueue both in functions.php using wp_enqueue_style and wp_enqueue_script
  4. Insert the <div id="reading-progress"></div> in your header.php

Option 2: Using a Code Snippet Plugin

Plugins like WPCode or Code Snippets let you inject the HTML div, CSS, and JS without editing theme files. Set the snippet to run only on single blog posts using their conditional logic.

Option 3: Directly in the Block Editor

Use a Custom HTML block at the top of your post template to insert the markup, then add the CSS via Appearance > Customize > Additional CSS.

progress bar website

Mobile Behavior and Best Practices

Mobile users represent the majority of blog traffic, so the bar should feel native on small screens. A few tips:

  • Keep it thin. 3px is enough on mobile. Anything taller competes with your content.
  • Avoid distracting animations. A simple width transition of 0.1s is enough.
  • Respect reduced motion. Wrap animations in a @media (prefers-reduced-motion: no-preference) query.
  • Test with sticky headers. If your header is sticky, make sure the bar sits above it or just below it, not overlapping the logo.
  • Use accessible colors. The bar should have sufficient contrast against your header background.

Styling Variations

The base version uses a gradient, but you can go creative:

  • Solid brand color for a minimal look
  • Animated shimmer using a repeating linear gradient
  • Bottom placement by changing top: 0 to bottom: 0
  • Vertical bar on the right edge, useful for documentation sites

Debugging Common Issues

Problem Likely Cause
Bar doesn’t move Selector doesn’t match your content container
Bar fills too early Article element is shorter than the viewport
Bar hidden behind header Header has higher z-index than 9999
Choppy animation Scroll handler is not throttled with requestAnimationFrame

FAQ

Does a reading progress bar affect page speed?

No. The script is a few lines and uses requestAnimationFrame, which piggybacks on the browser’s paint cycle. Impact on Core Web Vitals is negligible.

Can I use this on non-blog pages?

Yes, but it’s best suited for long-form content. On short landing pages or product pages, the bar can feel out of place.

Should I use the CSS-only version in production?

If your audience uses modern Chromium browsers, yes. Otherwise, use the JavaScript version or combine both with a feature detection fallback.

How do I hide the bar when the article is fully read?

In the JavaScript version, add a check: bar.style.opacity = percent >= 100 ? '0' : '1'; along with a CSS transition on opacity.

Does the bar work with infinite scroll blogs?

Not out of the box. Infinite scroll makes the total scroll height dynamic, so you’ll need to recalculate on each new post load, or scope the bar to the currently visible article using IntersectionObserver.

Wrapping Up

A reading progress bar is one of those tiny UX improvements that punches above its weight. In under 30 lines of code, you give readers a clearer sense of pacing and a more polished experience. Copy the snippets above, tweak the colors to match your brand, and ship it today.

Recent Posts

No Posts Found!

Categories

Tags

    Subscribe

    You have been successfully Subscribed! Ops! Something went wrong, please try again.

    About Us

    Express Jam Studio was founded in 2004 by John Smith. John had previously worked for a courier company, but he saw an opportunity to start his own business in the web design and development industry.

    Contact Info

    Copyright © 2022 Express Jam Studio. All Rights Reserved.