Navigation
Docs TOC
On-this-page navigation with scroll-spy and a readable no-JS fallback
detent-docs-toc
Port status
- Catalog status
- implemented
- Origin
- native-only
- Phase
- 6
- Wave
- native-only
- Group
- Navigation
5.3 KB gzip for the class module
Variants
Example content: the headings “Overview”, “Install” and “API” are sample text for this preview, not real documentation.
Recorded states
activerecorded state; no setteremptyrecorded state; no setterreduced motionrecorded state; no setter
Usage
Import
import '/components/docs-toc/define.js';
// or register it yourself
import { DetentDocsToc } from '/components/docs-toc/docs-toc.js';
Attributes
forlabelmax-leveloffset
Properties
forlabelmaxLeveloffsetitemsactiveId
Slots
No slots are recorded for this component.
Events
detent-toc-select— { id: string }
Example
Runnable example: site/examples/docs-toc.ts. The snippet below is generated from the catalogued attributes.
<detent-docs-toc
for="main"
label="On this page"
max-level="3"
offset="96"
></detent-docs-toc>
Property-only values
const element = document.querySelector('detent-docs-toc');
if (element !== null) {
// items: TocItem[]
element.items = [];
// activeId: string | undefined
element.activeId = undefined;
}
Source
Native-only row: there is no upstream source. The native module lives at:
src/components/docs-toc/docs-toc.ts
Native files
The native module and its artifacts:
src/components/docs-toc/docs-toc.tssrc/components/docs-toc/styles.csssrc/components/docs-toc/define.tsdocs/components/docs-toc.mdsite/examples/docs-toc.tstests/browser/docs-toc.spec.tsevidence/components/docs-toc.md
API
Boolean attributes follow HTML presence semantics: present means true, absent means false, and disabled="false" still means disabled.
Attributes
| Attribute | Property | Type | Default | Notes |
|---|---|---|---|---|
for | for | string (element id) | 'main' | Kebab-case attribute; camelCase property. |
label | label | string | 'On this page' | Kebab-case attribute; camelCase property. |
max-level | maxLevel | number (2-6) | 3 | Kebab-case attribute; camelCase property. |
offset | offset | number (px) | 96 | Kebab-case attribute; camelCase property. |
Properties
| Property | Type | Default | Notes |
|---|---|---|---|
for | string (element id) | 'main' | Property-only: assign from script; not accepted as an attribute. |
label | string | 'On this page' | Property-only: assign from script; not accepted as an attribute. |
maxLevel | number (2-6) | 3 | Property-only: assign from script; not accepted as an attribute. |
offset | number (px) | 96 | Property-only: assign from script; not accepted as an attribute. |
items | TocItem[] | [] | Property-only: assign from script; not accepted as an attribute. |
activeId | string | undefined | undefined | Property-only: assign from script; not accepted as an attribute. |
Events
| Event | Upstream prop | Payload type | Notes |
|---|---|---|---|
detent-toc-select | (native) | (id) => void | Composed, bubbles; fires only for a user action. |
Slots
No slots are recorded for this component.
CSS customisation
- Native-only row: no upstream source file, no upstream route (docs/migration/native-only.md).
- On-this-page navigation; upstream has no equivalent (scroll-progress-nav is a progress meter).
- The static fallback list is authored by the site so the TOC is usable with JavaScript disabled (PC-1.6).
- TocItem is pinned as { id, label, level }; entries with a missing id are dropped and level clamps into 2-6, each with one development warning.
- The scroll-spy decides the current entry from the offset line; the IntersectionObserver only decides when to recompute, so an instant jump marks the same entry a smooth scroll would.
- The active marker is drawn in the active link currentColor rather than a separate accent token, so the non-text ratio can never drop below the text ratio.
- A plain activation scrolls with window.scrollTo and writes the hash with history.replaceState; modified or non-primary activations are left to the browser, so open-in-new-tab and the JavaScript-disabled page keep native anchor navigation.
- Focus stays on the activated link; Safari reaches links with Option+Tab and the component does not add tabindex to consumer anchors.
What it does
An on-this-page index for documentation: a labelled navigation landmark that marks the section the reader is in, and still works without JavaScript.
Upstream behaviour
- Collects headings from the element referenced by for, or uses items
- Nests h3 under h2 up to max-level
- Marks the current section with aria-current while scrolling
- Click or Enter scrolls to the anchor (instant under reduced motion) and emits detent-toc-select
- Empty state when the target has no headings
- A light-DOM list of anchor links stays readable without JavaScript
Differences from upstream
- Native-only row: no upstream source file, no upstream route (docs/migration/native-only.md).
- On-this-page navigation; upstream has no equivalent (scroll-progress-nav is a progress meter).
- The static fallback list is authored by the site so the TOC is usable with JavaScript disabled (PC-1.6).
- TocItem is pinned as { id, label, level }; entries with a missing id are dropped and level clamps into 2-6, each with one development warning.
- The scroll-spy decides the current entry from the offset line; the IntersectionObserver only decides when to recompute, so an instant jump marks the same entry a smooth scroll would.
- The active marker is drawn in the active link currentColor rather than a separate accent token, so the non-text ratio can never drop below the text ratio.
- A plain activation scrolls with window.scrollTo and writes the hash with history.replaceState; modified or non-primary activations are left to the browser, so open-in-new-tab and the JavaScript-disabled page keep native anchor navigation.
- Focus stays on the activated link; Safari reaches links with Option+Tab and the component does not add tabindex to consumer anchors.