Skip to content

feat(a11y): expose the outline panel as an ARIA tree#210

Merged
mtskf merged 3 commits into
mainfrom
feat/a11y-outline-aria-tree
Jul 12, 2026
Merged

feat(a11y): expose the outline panel as an ARIA tree#210
mtskf merged 3 commits into
mainfrom
feat/a11y-outline-aria-tree

Conversation

@mtskf

@mtskf mtskf commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Summary

Expose the document outline as an ARIA tree so screen readers convey the heading hierarchy, the current position, and each section's collapsed/expanded state instead of announcing a flat list of buttons. Semantics/quality gap from the 2026-07-12 editor-surface a11y audit (L2, severity Low); the panel was already fully operable.

Changes

  • ul.quoll-outline-listrole="tree" + aria-label="Document outline".
  • Each row <li>role="treeitem" with aria-level (1-based off the render depth, so a skipped heading level e.g. h1→h3 collapses to contiguous tree nesting), aria-expanded on parent rows only, and aria-selected tracking the active heading.
  • aria-expanded moved off the twistie onto the treeitem (single source of truth); the twistie stays a keyboard-operable disclosure control and keeps its action label.
  • Empty-state row marked role="none" so it never masquerades as a treeitem inside the tree.
  • Flat aria-level model chosen (the DOM is a flat list, not nested role="group") — the spec-sanctioned pattern for a flat tree.
  • Version bump 0.1.45 → 0.1.46 + CHANGELOG entry.

Related

  • TODO: a11y L2 — expose the outline panel as an ARIA tree (from the 2026-07-12 editor-surface audit).
  • Follow-up logged as L2b: roving-tabindex keyboard tree (arrow-key navigation, single tab stop per row) — out of scope here per the Done-when.

Test Plan

  • pnpm compile green.
  • pnpm test:unit green (3343 tests) — 5 new tests pin the tree role, aria-level from depth (incl. skipped levels), parent-only aria-expanded, aria-selected tracking selection, and the empty-state role="none"; 3 existing collapse tests updated to assert aria-expanded on the treeitem row.
  • pnpm lint clean on the changed files.

mtskf added 3 commits July 13, 2026 04:48
The document outline was a flat <ul>/<li>/<button> list with no tree
semantics, so screen readers announced a plain button list — heading
hierarchy, current position, and collapsed-subtree state were not
conveyed. Add role=tree on the list and role=treeitem on each row, with
aria-level from the render depth (skipped heading levels collapse to
contiguous nesting), aria-expanded on parent rows, and aria-selected
tracking the active heading. Expand state moves off the twistie onto the
treeitem as the single source of truth; the twistie stays a
keyboard-operable disclosure control. Bump 0.1.45 -> 0.1.46.
Adds three regression-guard tests for the outline panel's ARIA tree
semantics: a nested row's own collapse state survives an unrelated
ancestor's re-expand; aria-expanded is removed (not left stale) when an
edit turns a parent heading into a leaf; and the empty-state role=none
transition fires live when the last heading is deleted, not just at
mount time.
@mtskf mtskf merged commit 7842219 into main Jul 12, 2026
1 check passed
@mtskf mtskf deleted the feat/a11y-outline-aria-tree branch July 12, 2026 19:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant