Table Of Contents
An "On this page" navigation that lists the headings of a document, nests them by level and highlights the section being read.
c2-table-of-contents turns the headings of a long page into a navigation landmark. It reads the h2 and h3 elements of the page’s <main> (or of the element named by for, with any selector), nests them by level, and keeps up with the reader: the section at the top of the scroll area becomes current, its link gets aria-current="true", and an indicator slides to it along the rail. Activating a link scrolls to the section, moves focus to its heading so the next Tab continues there, and updates the URL fragment (keep-url leaves the URL alone, for an app that routes on it).
A heading needs an id to be listed. data-toc-label gives it a shorter entry and data-toc-ignore leaves it out, and the list follows the page as headings are added, renamed or removed. When the sections are known in advance, hand them over as items ([{ id, label, level }]) instead: the list then renders on the server as well. This documentation site does that for the On this page column on the right, with plain links inside the tag as a fallback: the element’s shadow root has no slot, so they show only until it upgrades (or when JavaScript is off).
Import
import '@c2n/components/table-of-contents'Usage
Overview
A table of contents gives a long page a map. Readers skim it to decide whether the page answers their question, then jump straight to the part that does.
Installation
Install the package once, then import each element where it is used. Nothing else has to be configured.
With npm
Run the install command at the root of the project, next to its package.json.
From a CDN
A module script pointing at a CDN works as well for a prototype or a static page.
Theming
Every colour, size and spacing value is a CSS custom property, so the look follows the application theme.
FAQ
Headings without an id are not listed. Give each heading a stable id so links to it keep working.
The first row reads the h4 and h5 headings of the scrolling article next to it (for="toc-demo-article" selector="h4, h5"); levels are relative, so the outermost heading level found is the top of the list. On a page, place the list in a sticky column and leave for out: it reads the page’s <main>.
Sticky headers
A section becomes current when its heading reaches the top of the scroll area, and a link scrolls its heading to the same line. Under a sticky header, set offset to the header’s height (plus any breathing room) so both happen below it:
<c2-table-of-contents offset="80"></c2-table-of-contents>At the very bottom of the page the last section is current, even when its heading never reached that line. Sections passed on the way to a clicked link never flash current: the list holds the clicked one until the scroll ends.
Long lists
Put the list in a sticky column of your layout and give that column, for a long list, a maximum height with overflow: auto. The element scrolls the nearest scrolling box around it to keep the current entry in view, never the box the content scrolls in.
<aside class="page-toc">
<c2-table-of-contents offset="80"></c2-table-of-contents>
</aside>.page-toc {
position: sticky;
top: 96px;
max-height: calc(100vh - 120px);
overflow: auto;
}