Need
Lesson pages have no table of contents: readers of long lessons cannot
see the structure or jump to a section, and there are no heading
anchors to deep-link to.
Design
See ADR-0018
Hybrid, per the discussion and wireframe review:
- Server: a post-sanitization pass in the renderer mints slugified
anchor ids on h2..h4 and returns the TOC entries alongside the
HTML.
Ids are minted by our code AFTER sanitization, so
author-supplied ids still die (the sanitizer strips id today, and
that protects the #main skip-link target).
The allowlist does not change, and spoofing stays structurally impossible.
- Template: a
details/summary TOC inside nav aria-label="Contents",
rendered when the lesson has at least 2 entries.
Sticky: a pinned sidebar on desktop (internal scroll for long TOCs)
and a slim pinned collapsed bar on mobile, so the TOC is
viewable at any scroll position. Works without JavaScript.
- Client:
lesson-toc.js auto-opens the panel on wide screens and
adds an IntersectionObserver scroll-spy that marks the current
section with aria-current (CP4: dynamic interface).
Acceptance criteria
Need
Lesson pages have no table of contents: readers of long lessons cannot
see the structure or jump to a section, and there are no heading
anchors to deep-link to.
Design
See ADR-0018
Hybrid, per the discussion and wireframe review:
anchor ids on
h2..h4and returns the TOC entries alongside theHTML.
Ids are minted by our code AFTER sanitization, so
author-supplied ids still die (the sanitizer strips
idtoday, andthat protects the
#mainskip-link target).The allowlist does not change, and spoofing stays structurally impossible.
details/summaryTOC insidenav aria-label="Contents",rendered when the lesson has at least 2 entries.
Sticky: a pinned sidebar on desktop (internal scroll for long TOCs)
and a slim pinned collapsed bar on mobile, so the TOC is
viewable at any scroll position. Works without JavaScript.
lesson-toc.jsauto-opens the panel on wide screens andadds an IntersectionObserver scroll-spy that marks the current
section with
aria-current(CP4: dynamic interface).Acceptance criteria
section and update the URL hash; single-heading lessons show none.
sidebar) and mobile (pinned collapsed bar), with no horizontal
page scroll.
(MockMvc pins the server-rendered nav and anchors).
accents and deduplicate repeated headings.
aria-currentacross TOC links while scrolling.full test suite and Checkstyle stay green.