Detent web components
By Shane Logsdon

Navigation

Docs TOC

Implemented

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

On this page

Overview

Sample section body for the preview. The trail above collects this heading.

Install

Sample section body for the preview.

Prerequisites

A nested heading, so the third level renders indented under “Install”.

API

Sample section body for the preview.

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

  • active recorded state; no setter
  • empty recorded state; no setter
  • reduced motion recorded 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

  • for
  • label
  • max-level
  • offset

Properties

  • for
  • label
  • maxLevel
  • offset
  • items
  • activeId

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.ts
  • src/components/docs-toc/styles.css
  • src/components/docs-toc/define.ts
  • docs/components/docs-toc.md
  • site/examples/docs-toc.ts
  • tests/browser/docs-toc.spec.ts
  • evidence/components/docs-toc.md

API

Boolean attributes follow HTML presence semantics: present means true, absent means false, and disabled="false" still means disabled.

Attributes

Attributes
AttributePropertyTypeDefaultNotes
forforstring (element id)'main'Kebab-case attribute; camelCase property.
labellabelstring'On this page'Kebab-case attribute; camelCase property.
max-levelmaxLevelnumber (2-6)3Kebab-case attribute; camelCase property.
offsetoffsetnumber (px)96Kebab-case attribute; camelCase property.

Properties

Properties
PropertyTypeDefaultNotes
forstring (element id)'main'Property-only: assign from script; not accepted as an attribute.
labelstring'On this page'Property-only: assign from script; not accepted as an attribute.
maxLevelnumber (2-6)3Property-only: assign from script; not accepted as an attribute.
offsetnumber (px)96Property-only: assign from script; not accepted as an attribute.
itemsTocItem[][]Property-only: assign from script; not accepted as an attribute.
activeIdstring | undefinedundefinedProperty-only: assign from script; not accepted as an attribute.

Events

Events
EventUpstream propPayload typeNotes
detent-toc-select(native)(id) => voidComposed, 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.
Previous: Theme Switcher Next: Page Nav

Docs

Components

Project