Table of contents
The headings of a page as links, with the section in view marked by CSS.
On this page
A table of contents lists the headings of a page as a quiet column of links on a hairline rail. The link of the section in view is in ink, with a mark on the rail that glides from one link to the next. Where the browser supports scroll-target-group, the page marks it by itself: the list watches the scroll, and the link whose target is in view matches :target-current. No observer, no script.
Table of contents
<div class="grid grid-cols(2) gap-lg"> <div class="h(240) overflow-y-auto scroll-area text-sm leading-md fg-text-body"> <h3 class="m(0) fg-text-strong" id="toc-install">Install</h3> <p>Add the package and import it after the preset. The stylesheet has no build step of its own.</p> <p>It ships as plain CSS files, one per component.</p> <h3 class="fg-text-strong" id="toc-theme">Theme</h3> <p>Moonstone and obsidian share one set of tokens. Override a token and every component follows.</p> <p>The brand color is one token.</p> <h3 class="fg-text-strong" id="toc-corners">Corners</h3> <p>Corners turn with continuous curvature where the browser can draw them, and nest by their inset.</p> <p>Elsewhere they are circular.</p> <h3 class="fg-text-strong" id="toc-motion">Motion</h3> <p>One easing family. Interaction is quick, explanation calm, and reduced motion keeps the fade.</p> <p class="mb(160)">That is the whole system.</p> </div> <nav class="toc self-start" aria-label="On this page"> <p class="toc-title">On this page</p> <ul class="toc-list"> <li> <a class="toc-link" href="#toc-install">Install</a> </li> <li> <a class="toc-link" href="#toc-theme">Theme</a> </li> <li> <a class="toc-link" href="#toc-corners">Corners</a> </li> <li> <a class="toc-link" href="#toc-motion">Motion</a> </li> </ul> </nav></div>#Levels
A toc-list inside a list item is a deeper level. It shares the rail and indents its links by one step. Two levels are enough for a page; a third is a sign the page wants splitting.
Levels
<nav class="toc max-w(220)" aria-label="On this page"> <ul class="toc-list"> <li> <a class="toc-link" href="#">Overview</a> </li> <li> <a class="toc-link" href="#" aria-current="true">Theme</a> <ul class="toc-list"> <li> <a class="toc-link" href="#">Surfaces</a> </li> <li> <a class="toc-link" href="#">Lines and shadows</a> </li> <li> <a class="toc-link" href="#">The brand color</a> </li> </ul> </li> <li> <a class="toc-link" href="#">Accessibility</a> </li> </ul></nav>#Marking the section yourself
aria-current="true" on a link draws the same state as :target-current. Set it from the server for a static list, or from an IntersectionObserver where the scroll-target group is not supported. When both are present they agree, since both follow the section in view.
#Accessibility
- The list is a
<nav>with a label such as "On this page", so it can be found among the page's landmarks and told apart from the main navigation. - Links go to headings by
id. Give the scrollerscroll-padding-topwhen a navbar is stuck above it, so a heading does not land under the bar. - The current link changes ink, and the mark is one and a half pixels of ink beside it; neither depends on hue. With reduced motion the mark moves without gliding.
#Classes
| Class | Description |
|---|---|
toc | The container, on a nav with a label such as "On this page"; a scroll-target group |
toc-title | A caption over the list |
toc-list | A list of links on a hairline rail. A toc-list inside a list item is a deeper level: it shares the rail and indents its links by one step |
toc-link | A link to a heading by its id; the one of the section in view is in ink, with a mark on the rail |
#Custom properties
| Property | Default | Description |
|---|---|---|
--toc-indent | .875rem | The distance from the rail to the words, and one level's indent |