Skip to content

fix(web): role=button + aria-label on Timeline rows (#700)#809

Merged
memtomem merged 1 commit intomainfrom
fix/web-a11y-timeline-row-role
May 5, 2026
Merged

fix(web): role=button + aria-label on Timeline rows (#700)#809
memtomem merged 1 commit intomainfrom
fix/web-a11y-timeline-row-role

Conversation

@memtomem
Copy link
Copy Markdown
Owner

@memtomem memtomem commented May 5, 2026

Summary

Timeline rows are constructed in two views — chunks (.timeline-item) and files (.timeline-file-item / .tl-file-chunk-item). Each view's rows are fully click-activated (chunks-view toggles inline expansion, files-view outer toggles a chunk list, files-view inner navigates to source) but they were rendered as bare <div> with no role, no tabindex, no accessible name, and no keyboard handler at all — keyboard users couldn't even activate them.

Second of three split PRs against #700 (Timeline entries target). Independent of the search PR (#808).

Changes (timeline.js)

  • renderChunkView.timeline-item row gets role="button", tabindex="0", aria-expanded="false", aria-label (filename + time), and a keydown handler mirroring the existing click toggle.
  • renderFileView.timeline-file-item (outer expand row) and each .tl-file-chunk-item (inner navigate row) get the same a11y attributes; the inner row stops keydown propagation so the outer expand handler does not also fire on Enter.
  • aria-expanded is initialized to false on first render, then updated on every toggle, so screen readers announce the collapsed state on first focus.

tests/test_web_a11y.py — new TestTimelineRowA11y class, six pin assertions (role / aria-label / keydown × 2 views). Mutation-validated: removing role= from the chunks-view row fails its dedicated test.

Why these patterns

  • All three row types are fully clickable → role="button" (matches home-source-item precedent at app.js:1703).
  • aria-label uses data already on the row — no new server data, no i18n keys (filename / time / chunk_type are not localized prose).
  • Keyboard handlers were genuinely missing, not just a11y polish. Without them role="button" would be a lie. Click + keydown form a parity pair throughout.

Out of scope

Test plan

  • uv run pytest packages/memtomem/tests/test_web_a11y.py — 6 pass.
  • Mutation check — removing role= from renderChunkView .timeline-item fails the relevant test.
  • uv run ruff check packages/memtomem/src packages/memtomem/tests clean.
  • Manual: in Timeline tab, focus a .timeline-item with Tab, press Enter — confirm expand. Same for .timeline-file-item outer row and .tl-file-chunk-item inner row.

Merge interaction with #808

Both PRs touch tests/test_web_a11y.py: #808 creates the file with TestSearchResultItemA11y; this PR creates it with the same fixtures + TestTimelineRowA11y. Whichever lands first, the other will need a trivial rebase to keep both classes side-by-side.

Refs #700, umbrella #702.

🤖 Generated with Claude Code

Timeline rows are constructed in two views: chunks (.timeline-item) and
files (.timeline-file-item / .tl-file-chunk-item). Each view's rows are
fully click-activated — the chunks-view item toggles inline expansion,
the files-view outer row toggles a chunk list, and the files-view inner
chunk row navigates to source — but they were rendered as bare <div>
with no role, no tabindex, no accessible name, and no keyboard handler.
Screen readers announced them as anonymous "group" landmarks and
keyboard users could not activate them at all.

Add role="button", tabindex="0", aria-label (filename + time / file +
count + last-time / chunk-type + time), and Enter/Space keydown
handlers that mirror the existing click handlers. Initialize
aria-expanded="false" on the two expandable row types so screen readers
announce the collapsed state on first focus.

Inner navigate row (.tl-file-chunk-item) stops keydown propagation so
the parent files-view row's expand handler does not also fire.

Pin the contract with TestTimelineRowA11y in test_web_a11y.py — six
source-scan assertions covering both views. Mutation-validated: removing
role= from the chunks-view row fails its specific test.

Refs #700, umbrella #702.

Co-Authored-By: Claude <[email protected]>
@memtomem memtomem force-pushed the fix/web-a11y-timeline-row-role branch from 6ab8ea6 to 2d5935f Compare May 5, 2026 13:44
@memtomem memtomem merged commit 5c478f9 into main May 5, 2026
11 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators May 5, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants