← Controllers

reading_progress

Progress bar showing scroll position for long-form content.

See the blue bar at the very top of your browser? Scroll this page and watch it grow. That's this controller in action.

Usage

<!-- Place at the top of your layout -->
<div data-controller="reading-progress" class="reading-progress"></div>

Required CSS

.reading-progress {
  position: fixed;
  top: 0;
  left: 0;
  height: 3px;
  background-color: theme('colors.blue.500');
  z-index: 50;
  width: 0%;
  transition: width 0.1s ease-out;
}

Source

import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  connect() {
    this.boundUpdateProgress = this.updateProgress.bind(this);
    window.addEventListener("scroll", this.boundUpdateProgress, {
      passive: true
    });
    this.updateProgress();
  }

  disconnect() {
    window.removeEventListener("scroll", this.boundUpdateProgress);
  }

  updateProgress() {
    const scrollTop = window.scrollY;
    const docHeight = document.documentElement.scrollHeight - window.innerHeight;
    const progress = docHeight > 0 ? (scrollTop / docHeight) * 100 : 0;
    this.element.style.width = `${Math.min(100, Math.max(0, progress))}%`;
  }
}

Features

  • Uses passive scroll listener for performance
  • Smooth width transitions
  • Clamps progress between 0-100%
  • Properly cleans up on disconnect

Customization

Adjust the appearance via CSS:

.reading-progress {
  height: 4px;                      /* Thicker bar */
  background: linear-gradient(     /* Gradient */
    to right,
    theme('colors.blue.500'),
    theme('colors.purple.500')
  );
}