feat(a11y): <main>-landmark en visueel-verborgen <h1> in HTML-export (#1250) #1259

Merged
brenno merged 3 commits from fix/1250-html-export-landmarks into main 2026-08-05 09:12:06 +00:00
Owner

Wat

#1250: de HTML-export had geen documentstructuur — geen <main>, geen document-<h1>, dus voor een schermlezer was het één platte rij <section>s zonder ingang. WCAG 2.1 SC 1.3.1 (Info en relaties) en 2.4.1 (Blokken omzeilen) op AA-niveau.

Wijziging

  • De dia's zitten nu in een <main>-landmark.
  • Eén visueel-verborgen <h1 class="ocideck-sr-only"> met de decktitel vóór de eerste <section>. De titel staat al in <title>; dit is het structuur-anker dat de schermlezer bij binnenkomst aankondigt.
  • ocideck-sr-only-CSS in de structural stylesheet.
  • De titel wordt als tekstinhoud ontsnapt (_htmlText, niet _htmlAttr).
  • docs/ACCESSIBILITY.md bijgewerkt: landmark-structuur staat er nu; kleurcontrast en focus-order blijven nog niet gecontroleerd.

Visueel verandert niets — de export opent nog direct op de eerste dia. Het optionele per-dia aria-label uit het voorstel bewust níet meegenomen (scope: één anker-titel + landmark, niet per-dia navigatie).

Refactor

Om build() onder de method-length-ratchet (132) te houden is het CSP-<meta>-blok met zijn verklarende commentaar extraheren naar static _cspMeta(nonce). Zuivere verplaatsing; de CSP zelf is ongewijzigd.

Bewaker-blik

Raakt een publieke belofte (de export als toegankelijke route). De wijziging versterkt de belofte: het .md-formaat, de opslag, de afhankelijkheden en het uitgaand verkeer veranderen niet — alleen de HTML-wrapper eromheen wordt toegankelijker. Geen nieuwe partij om te vertrouwen, geen vendor-lock-in. Geen botsing; soevereiniteit en uitwisselbaarheid blijven onaangeroord.

Relatie met #1249

#1250 noemde dat dit samen met #1249 (de lang-kwestie) bekeken moest worden. #1249 is al opgelost (htmlLang-parameter + Deck.language); deze PR gaat alleen over de documentstructuur.

Testplan

  • make check groen (formaat, analyse, conventies, method-length, privacy-projectiegrens, volledige testsuite, coverage, per-file coverage)
  • Nieuwe tests: <main> rond secties met h1 ervoor; fallback op fallbackTitle; ontsnapping van &/< in de titel
  • Bestaande export-tests blijven groen

Closes #1250

Generated with Devin

## Wat #1250: de HTML-export had geen documentstructuur — geen `<main>`, geen document-`<h1>`, dus voor een schermlezer was het één platte rij `<section>`s zonder ingang. WCAG 2.1 SC 1.3.1 (Info en relaties) en 2.4.1 (Blokken omzeilen) op AA-niveau. ## Wijziging - De dia's zitten nu in een `<main>`-landmark. - Eén visueel-verborgen `<h1 class="ocideck-sr-only">` met de decktitel vóór de eerste `<section>`. De titel staat al in `<title>`; dit is het structuur-anker dat de schermlezer bij binnenkomst aankondigt. - `ocideck-sr-only`-CSS in de structural stylesheet. - De titel wordt als tekstinhoud ontsnapt (`_htmlText`, niet `_htmlAttr`). - `docs/ACCESSIBILITY.md` bijgewerkt: landmark-structuur staat er nu; kleurcontrast en focus-order blijven nog niet gecontroleerd. Visueel verandert niets — de export opent nog direct op de eerste dia. Het optionele per-dia `aria-label` uit het voorstel bewust níet meegenomen (scope: één anker-titel + landmark, niet per-dia navigatie). ## Refactor Om `build()` onder de method-length-ratchet (132) te houden is het CSP-`<meta>`-blok met zijn verklarende commentaar extraheren naar `static _cspMeta(nonce)`. Zuivere verplaatsing; de CSP zelf is ongewijzigd. ## Bewaker-blik Raakt een publieke belofte (de export als toegankelijke route). De wijziging versterkt de belofte: het `.md`-formaat, de opslag, de afhankelijkheden en het uitgaand verkeer veranderen niet — alleen de HTML-wrapper eromheen wordt toegankelijker. Geen nieuwe partij om te vertrouwen, geen vendor-lock-in. Geen botsing; soevereiniteit en uitwisselbaarheid blijven onaangeroord. ## Relatie met #1249 #1250 noemde dat dit samen met #1249 (de `lang`-kwestie) bekeken moest worden. #1249 is al opgelost (`htmlLang`-parameter + `Deck.language`); deze PR gaat alleen over de documentstructuur. ## Testplan - [x] `make check` groen (formaat, analyse, conventies, method-length, privacy-projectiegrens, volledige testsuite, coverage, per-file coverage) - [x] Nieuwe tests: `<main>` rond secties met h1 ervoor; fallback op `fallbackTitle`; ontsnapping van `&`/`<` in de titel - [x] Bestaande export-tests blijven groen Closes #1250 Generated with [Devin](https://devin.ai)
De HTML-export is de aanbevolen route voor wie leest in plaats van kijkt,
maar voor een schermlezer was het document één platte rij <section>s
zonder ingang — geen landmark, geen documenttitel. Dat is het gat dat
docs/ACCESSIBILITY.md noemde als "nobody has checked its landmark
structure".

Nu zitten de dia's in een <main>-landmark met een visueel-verborgen <h1>
die de decktitel draagt (ocideck-sr-only). Een schermlezer kondigt bij
binnenkomst het document aan en kan naar de inhoud springen — WCAG 2.1 SC
1.3.1 (Info en relaties) en 2.4.1 (Blokken omzeilen). De titel staat al in
<title>; dit is het structuur-anker voor de schermlezer. Visueel verandert
niets: de export opent nog direct op de eerste dia.

De titel wordt als tekstinhoud ontsnapt (_htmlText, niet _htmlAttr), zodat
een decktitel met & of < geen ruwe markup in de h1 wordt.

Om build() onder de method-length-ratchet (132) te houden is het
CSP-<meta>-blok met zijn verklarende commentaar extraheren naar een
static _cspMeta(nonce). Dat is een zuivere verplaatsing; de CSP zelf is
ongewijzigd.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Drie tests die de structuur bewaken: <main> rond de secties met de h1 ervoor,
fallback op fallbackTitle wanneer de decktitel leeg is, en ontsnapping van de
titel als tekstinhoud (&, <). Bewijst dat de secties binnen <main> staan en ná
de h1, en dat de sr-only-regel meegaat in de structural CSS.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
docs(a11y): HTML-export heeft landmarks en een documenttitel (#1250)
All checks were successful
scans / scans (pull_request) Successful in 2m31s
static-gate / static-gate (pull_request) Successful in 4m17s
bfe4d1b1fc
Werkte de "nobody has checked its landmark structure"-zin bij: de
landmark-structuur staat er nu (<main> + visueel-verborgen <h1>),
WCAG 1.3.1/2.4.1. Kleurcontrast en focus-order blijven nog niet gecontroleerd.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
brenno merged commit 4f563726bd into main 2026-08-05 09:12:06 +00:00
Sign in to join this conversation.
No description provided.