Skip to content

Make tier collapse headers keyboard accessible#13

Merged
JuliusScheuerer merged 1 commit into
mainfrom
worktree-keyboard-accessible-tiers
Mar 25, 2026
Merged

Make tier collapse headers keyboard accessible#13
JuliusScheuerer merged 1 commit into
mainfrom
worktree-keyboard-accessible-tiers

Conversation

@JuliusScheuerer
Copy link
Copy Markdown
Owner

Summary

  • Add role="button", tabindex="0", and aria-expanded to tier header divs in results.html
  • Add keydown event handler in review.js — Enter and Space toggle collapse/expand
  • Extract shared collapse logic into toggleTierCollapse() that updates aria-expanded for screen readers
  • Focus-visible styling already works via existing :focus-visible CSS rules

Test plan

  • make check passes (275 tests, 95.50% coverage)
  • Manual: tab through tier headers, verify Enter/Space toggles collapse

Add role="button", tabindex="0", and aria-expanded to tier headers.
Handle Enter and Space keydown events to toggle collapse. Extract
shared collapse logic into toggleTierCollapse() helper that updates
aria-expanded state for screen readers.
@JuliusScheuerer JuliusScheuerer merged commit 03a7f5c into main Mar 25, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 25, 2026

Greptile Summary

This PR improves keyboard accessibility for tier collapse/expand headers in results.html by adding role="button", tabindex="0", and aria-expanded attributes, and wires up an Enter/Space keydown handler in review.js. The shared toggleTierCollapse() refactor cleanly consolidates click and keyboard paths and correctly maintains aria-expanded state.

Key issue found:

  • onTierHeaderKeydown is missing the select-all-checkbox guard that onTierHeaderClick already has. Because keydown bubbles, pressing Space while the checkbox inside the header has focus calls e.preventDefault() (blocking the native checkbox toggle) and then collapses/expands the tier instead — making the select-all checkbox un-toggleable via keyboard.

Minor note:

  • Placing role="button" on a container that wraps an <input type="checkbox"> is an ARIA anti-pattern (interactive content inside an interactive role). The checkbox may be announced or behave inconsistently across assistive technologies. This is a structural constraint worth addressing as a follow-up refactor.

Confidence Score: 4/5

  • One-line fix required before merge: add the checkbox guard to onTierHeaderKeydown.
  • The overall approach is correct — aria-expanded, tabindex, and the toggleTierCollapse refactor are all sound. The single concrete bug (missing checkbox guard in the keydown handler) is easy to fix with one line and doesn't affect existing click or non-keyboard users. Everything else is a non-blocking accessibility best-practice note.
  • src/document_anonymizer/web/static/js/review.js — the onTierHeaderKeydown function needs the select-all-checkbox guard.

Important Files Changed

Filename Overview
src/document_anonymizer/web/static/js/review.js Adds keyboard (Enter/Space) collapse toggle and extracts toggleTierCollapse() — logic is clean, but onTierHeaderKeydown is missing the select-all-checkbox guard present in onTierHeaderClick, causing Space on the checkbox to suppress its toggle and collapse the tier instead.
src/document_anonymizer/web/templates/results.html Adds role="button", tabindex="0", and aria-expanded="true" to tier header divs. Initial aria-expanded value correctly matches the visible (non-hidden) default state. Introduces the ARIA anti-pattern of an interactive checkbox nested inside role="button".

Sequence Diagram

sequenceDiagram
    participant KB as Keyboard User
    participant HDR as tier-header div (role=button)
    participant CB as select-all-checkbox
    participant JS as review.js

    Note over HDR: tabindex="0", aria-expanded="true"

    KB->>HDR: Tab → focus header div
    KB->>HDR: Press Enter or Space
    HDR->>JS: keydown → onTierHeaderKeydown
    JS->>JS: toggleTierCollapse(header)
    JS->>HDR: setAttribute("aria-expanded", "false")
    JS-->>HDR: tier body hidden

    KB->>CB: Tab → focus checkbox (inside header)
    KB->>CB: Press Space
    CB-->>HDR: keydown bubbles up
    HDR->>JS: onTierHeaderKeydown fires
    Note over JS: ⚠️ No checkbox guard!<br/>e.preventDefault() blocks<br/>checkbox toggle
    JS->>JS: toggleTierCollapse(header)
    Note over CB: Checkbox state unchanged (bug)
Loading

Reviews (1): Last reviewed commit: "Make tier collapse headers keyboard acce..." | Re-trigger Greptile

Comment on lines +126 to +132
function onTierHeaderKeydown(e) {
// Enter or Space toggles collapse (standard button behavior)
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
toggleTierCollapse(e.currentTarget);
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Missing checkbox guard in onTierHeaderKeydown

onTierHeaderClick correctly bails out when the click target is the select-all-checkbox, but onTierHeaderKeydown has no equivalent guard. Because keydown bubbles, pressing Space while the checkbox has focus will:

  1. Call e.preventDefault() — suppressing the checkbox's native toggle behaviour, so the checkbox state never changes.
  2. Proceed to call toggleTierCollapse, collapsing or expanding the tier instead.

This makes the select-all checkbox completely un-toggleable with the keyboard, which is a regression for keyboard users.

Suggested change
function onTierHeaderKeydown(e) {
// Enter or Space toggles collapse (standard button behavior)
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
toggleTierCollapse(e.currentTarget);
}
}
function onTierHeaderKeydown(e) {
// Don't interfere with the checkbox's native Space behaviour
if (e.target.classList.contains("select-all-checkbox")) return;
// Enter or Space toggles collapse (standard button behavior)
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
toggleTierCollapse(e.currentTarget);
}
}

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