Scrollspy
Behavior hook for navigation that marks the active section while the page scrolls.
Scrollspy is a behavior hook, not a visual component. It marks a navigation region for the JavaScript enhancement. Pair it with .nav-list and .nav-link, or style [aria-current] yourself.
.scrollspymarks the region for JS detection. It does not add visual styles..nav-listand.nav-linkprovide the visible list and active link styling.- The JS enhancer uses
IntersectionObserverto togglearia-current="location"
on the active link. Without JavaScript, the links remain regular anchor links.
- The same markup also works with the CSS-native scroll markers path (see below).
Overview
Tokens
Components
<nav class="scrollspy" aria-label="Page sections">
<ol class="nav-list stack">
<li><a class="nav-link" href="#overview" aria-current="location">Overview</a></li>
<li><a class="nav-link" href="#tokens">Tokens</a></li>
<li><a class="nav-link" href="#components">Components</a></li>
</ol>
</nav>
<main>
<section id="overview" tabindex="-1">
<h2>Overview</h2>
</section>
<section id="tokens" tabindex="-1">
<h2>Tokens</h2>
</section>
<section id="components" tabindex="-1">
<h2>Components</h2>
</section>
</main>
Native scroll markers
Modern browsers are starting to support CSS-native scroll markers with scroll-target-group and :target-current. This can style the active link without JavaScript.
CSS
@supports selector(:target-current) {
.scrollspy {
scroll-target-group: auto;
}
.scrollspy a:target-current {
color: var(--primary);
font-weight: var(--font-weight-medium);
}
}
Keep this as progressive enhancement. The JavaScript enhancement remains the portable fallback — the same markup serves both paths.