On this page
Import
import { TableOfContents } from '@envsemble/envui';
Usage
<article ref={article}>…</article>
<TableOfContents container={article} levels={[2, 3]} />
Without items, TableOfContents reads the headings in container, a ref or a CSS selector, or the page's main element, once it mounts and again as they change. levels chooses the heading levels, [2, 3] by default, and each heading nests under the one above it of a lower level. A heading without an id gets one from its words, such as invite-your-team. filter leaves out headings it returns false for, such as an example's own.
Sections of your own
<TableOfContents
items={[
{ id: 'revenue', label: 'Revenue', children: [{ id: 'by-region', label: 'By region' }] },
{ id: 'customers', label: 'Customers' },
]}
/>
Each item has the id of the element it leads to, any element with an id, its label, and its own sections in children. The server renders them as they are; the current one is marked once the page has hydrated.
The current section
The current section is the last one whose start, less its scroll margin, has passed a line a quarter of the way down the area that scrolls, below its scroll padding; before the first has, it is the first.
- Sections too near the end of the page to reach the line share the scrolling left after the last one that does, evenly, so each becomes current in turn and the last one at the very end.
- It follows whichever area scrolls the sections, the window or an element, measured in that area's own pixels, also inside a transformed or zoomed container, such as a scaled preview.
- A link pressed keeps its section current until the reader scrolls again, also where the page can't scroll it to the top.
Composition
The landmark is env-table-of-contents, around env-table-of-contents-body, which holds the list. Each level is an ordered list, env-table-of-contents-list, of items, env-table-of-contents-item, each a link with its level below it, indented by the depth the component sets on it, --env-table-of-contents-depth. Links before the current one carry data-passed; only the current one's label takes the text color.
The reader's place is env-table-of-contents-wave, an SVG hidden from assistive technology in the body's start column, --env-space-5 wide at its fullest, and a tenth of that elsewhere. The component draws its outline every frame the place moves, without rendering again, from the links' layout positions.
- It is one path in
--env-color-bg-neutral-muted, a round bulb over about two entries either side of the place, fewer in a list of under seven. In right-to-left text, its straight edge is on the right. - It follows the scroll as it is, and glides to a place more than a fifth of an entry away in
--env-motion-duration-mediumwith--env-motion-ease-enter, never past it; a label's color changes in--env-motion-duration-fastwith--env-motion-ease-enter, ahead of it, and the swell under the pointer in--env-motion-duration-fast. No frame is drawn once it rests. At once under reduced motion or with the duration tokens at0s; in forced colors it isCanvasText.
Accessibility
You provide
- An
aria-labelthat tells it apart from other navigation on the page, such as the contents of an article in a page with its own. - Ids on the elements that
itemsname. - A scroll margin on the sections, or scroll padding on the area that scrolls, where something stays at its top, such as a sticky header: a sticky PageHeader in a Page sets it for you.
envUI provides
- A
navlandmark named On this page around nested ordered lists of links. aria-current="location"on the link of the section the reader is in.- Links that scroll smoothly, or at once where reduced motion is preferred, add the section to the address and move focus to it, so reading and the keyboard go on from there.
- A shape hidden from assistive technology, which reads the current link instead; in forced colors it takes the system's text color, its width still marking the place.
Props
TableOfContents
The page's sections as links, nested by level, with the one the reader is in marked as they scroll: the headings in a container by default, or sections of your own.
| Prop | Type | Description |
|---|---|---|
items | readonly TableOfContentsItem[ | The sections to list, nested through `children`, each leading to the element with its `id`: a heading or any other part of the page, under a label of your own. Without it, the component lists the headings it finds in `container`, after it mounts. |
containerDefault The page's `main` element, or the body without one | RefObject<string | Where to find the headings, without `items`: an element's ref or a CSS selector. |
levelsDefault [2, 3] | readonly HeadingLevel[ | The heading levels to list, without `items`. A heading nests under the one above it of a lower level. A heading without an id gets one from its words. |
filter | ( | Leaves out the headings it returns false for, without `items`, such as an example's own. |
aria-labelDefault 'On this page', or EnvUIProvider's messages.tableOfContents.label | string | The accessible name of the navigation landmark. |
className | string | Classes for the `nav` element. |
Also accepts the native props of <nav>, such as ref, className and aria-* attributes.
Part names
Each part's class and state attributes are public and versioned, for an application's CSS to style.
env-table-of-contents- The navigation of the page's sections.
env-table-of-contents-body- Around the list and the shape beside it.
env-table-of-contents-item- One section, with the sections nested in it.
env-table-of-contents-link- A section's link.
env-table-of-contents-list- A level of sections.
env-table-of-contents-wave- The shape down the side of the list that marks the reader's place, widest beside it.
data-passed- Present on env-table-of-contents-link
Strings
EnvUIProvider's messages overrides each, and a component's own label prop wins over it.
messages.tableOfContents.label"On this page"Names the navigation of the page's sections.
Changelog
- 1.0.0
TableOfContents: the page's headings or sections of your own, nested, with the current section marked as the reader scrolls.
- 1.0.0-prerelease.6
The reader's place glides calmly, in the motion tokens' medium duration and entering easing, instead of stretching, leaning and wobbling on springs, and the current label takes its color quickly, ahead of it, so only one label reads as current; with the duration tokens at
0sit moves at once.The current section's label is drawn a little heavier without changing its widths, and a label under the pointer takes the text color.
- 1.0.0-prerelease.5
The reader's place is one shape down the side of the list instead of a dash before each section, in one color: straight along the list's start and bulging broadly toward the labels beside the current section, thin a few sections away. It moves on a spring, stretching as it travels and settling with a short wobble, at once under reduced motion. The labels sit a little further in. Inside a transformed or zoomed container, the current section is measured in the scrolling area's own pixels.