Skip to content

How to create a horizontal progress indicator for your Starlight site

A beautiful cover image with the text "Progress Bar"

In this post, we would like to show you how frostybeeFrostyBee created a clean horizontal progress indicator for Starlight sites.

Prerequisites

You will need to have an existing Starlight website.

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.

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.

src/components/ProgressIndicator.astro
<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

Now we can use this component in a Starlight override. In this case it makes sense to override the Header.astro component like this:

src/components/Header.astro
---
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:

astro.config.mjs
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

You can find the whole project code on GitHub and in this Stackblitz example.

Conclusion

With this setup, the progress indicator will be displayed on every page (except landing pages) of your Starlight site. 🥳

You can also check out frostybeeFrostyBee’s Starlight Scroll To Top plugin which also supports a built in Progress Ring since version 0.3.1. 🙌