How to create a horizontal progress indicator for your Starlight site

In this post, we would like to show you how FrostyBee created a clean horizontal progress indicator for Starlight sites.
Prerequisites
Section titled “Prerequisites”You will need to have an existing Starlight website.
Create the progress indicator component
Section titled “Create the progress indicator component”Let’s create a new component called ProgressIndicator.astro
in the src/components/
directory. This component will be responsible for rendering the progress indicator.
First, let’s take a look at a classic solution using JavaScript.
We create a container which spans across the whole width with the progress bar <div>
inside. The width of the inner <div>
is the updated whenever the user scrolls (and when the page loads) to reflect the current scroll position.
<div class="progress-scroll-container" aria-hidden="true"> <div id="progress-scroll"></div></div>
<script> window.addEventListener("scroll", function () { updateProgressScroll(); }); window.addEventListener("load", function () { updateProgressScroll(); });
function updateProgressScroll() { if (document) { const progressScroll = document.getElementById("progress-scroll"); if (progressScroll) { var scrollTop = window.scrollY || document.documentElement.scrollTop; var scrollHeight = document.documentElement.scrollHeight - window.innerHeight; var progress = (scrollTop / scrollHeight) * 100; progressScroll.style.width = progress + "%"; } } }</script>
17 collapsed lines
<style> html:is(:not([data-has-hero]), [data-has-sidebar], [data-has-toc]) .progress-scroll-container { position: fixed; top: var(--sl-nav-height); left: 0; height: 0.25rem; width: 100%; background-color: transparent; z-index: 3; }
#progress-scroll { height: 100%; width: 0px; background-color: var(--sl-color-accent-high); }</style>
<style is:global> #starlight__on-this-page--mobile { margin-top: 0.25rem; }</style>
In the hidden lines is the styling which makes sure that the progress bar looks like a typical progress bar. It is also suited to Starlight because the bar is automatically hidden when the site has no sidebar and table of contents, but a hero element as most landing pages do.
One thing to point out is what the global CSS (line 45) does: It pushes the mobile table of contents dropdown menu down to create more whitespace to the progress bar. This is a personal preference and you can remove it if you want.
Starlight Override
Section titled “Starlight Override”Now we can use this component in a Starlight override. In this case it makes sense to override the Header.astro
component like this:
---import Default from "@astrojs/starlight/components/Header.astro";import ScrollIndicator from "./ScrollIndicator.astro";---
<Default><slot /></Default><ScrollIndicator />
Do not forget to add this component override to your astro.config.mjs
file:
import { defineConfig } from 'astro/config';import starlight from '@astrojs/starlight';
export default defineConfig({ integrations: [ starlight({ title: 'My Docs with a Progress Indicator', components: { // Override the default `Header` component. Header: './src/components/Header.astro', }, }), ],});
Source Code
Section titled “Source Code”You can find the whole project code on GitHub and in this Stackblitz example.
Conclusion
Section titled “Conclusion”With this setup, the progress indicator will be displayed on every page (except landing pages) of your Starlight site. 🥳