Skip to content

feat(webui): session list ARIA tree semantics + roving tabindex - #1238

Merged
edwin-zvs merged 1 commit into
mainfrom
session-list-a11y
Aug 8, 2026
Merged

feat(webui): session list ARIA tree semantics + roving tabindex#1238
edwin-zvs merged 1 commit into
mainfrom
session-list-a11y

Conversation

@edwin-zvs

Copy link
Copy Markdown
Contributor

Closes #1075.

The web UI session list had TUI-parity keyboard navigation but no accessibility semantics: rows were anonymous divs, selection was communicated by a CSS class only, and no row was ever in the tab order, so a screen reader announced nothing useful and a Tab-only user could never reach the list.

What changed (crates/daemon/assets/index.html)

  • role="tree" on the container (aria-label="sessions"). Chose tree over listbox because the list genuinely nests — groups contain sessions, sessions contain subagents/forks — and the lineage panel already established the tree/treeitem convention in this codebase.
  • Every row is a treeitem: sessions, services, the operator row, group headers, and the "N archived" rows.
    • Sessions and services carry aria-selected (mirrors the active row).
    • Group headers, archived rows, and sessions with children carry aria-expanded; leaves don't, so they never announce as collapsed empty branches.
    • All rows expose aria-level / aria-posinset / aria-setsize, computed by a small DFS walk over the already-flat orderedItems() output.
    • ARIA attrs are diffed with their own signature in the keyed reconcile, same pattern as class/inner, so unchanged rows aren't touched.
  • Real roving tabindex: exactly one row — the caret row if focus is in the list, else the selected row, else the first — holds tabindex="0"; all others -1. Tab from the top of the page reaches the list exactly once, then the existing arrow-key handling takes over. focusListRow promotes the row it focuses and demotes the rest.
  • toggleListFocus enters through the roving Tab stop. Its old query looked for .item.selected, a class no row ever carries, so C-x o always landed on the first session row; now it returns to the selected/last-caret row.

Unchanged: all key handling (Enter / Space / Shift+Arrow / group Left/Right, C-n/C-p), rendering, drag, click behavior.

Testing

  • New assertion block in web_smoke.rs (web_client_loads_and_websocket_connects) covering: tree/treeitem roles, aria-selected on the selected subagent, aria-expanded on expanded/collapsed groups and a parent session, level/posinset/setsize on nested rows, and the single-Tab-stop invariant.
  • No visual change, so no screen recording — the diff is invisible attributes.

🤖 Generated with Claude Code

…abindex

The session list had full keyboard navigation but no accessibility
semantics: rows were anonymous divs, selection was CSS-class-only, and
no row was ever in the tab order, so assistive tech saw nothing and
Tab-only users could not reach the list at all.

- The container is now role="tree" (matching the lineage panel's
  existing convention; groups nest sessions, sessions nest
  subagents/forks, so a tree fits better than a flat listbox).
- Every row — sessions, services, the operator, group headers, and
  archived-count rows — is a treeitem. Sessions/services carry
  aria-selected; group headers, archived rows, and sessions with
  children carry aria-expanded; all rows expose aria-level /
  aria-posinset / aria-setsize derived from the ordered items' depth.
- Roving tabindex: exactly one row (the caret row, else the selected
  row, else the first) holds tabindex="0", so Tab reaches the list
  exactly once and the existing arrow-key handling takes over.
- toggleListFocus now enters through that roving Tab stop; its old
  query looked for .item.selected, a class no row ever carries.

Existing key handling (Enter / Space / Shift+Arrow / group Left-Right)
is untouched.

Closes #1075
@edwin-zvs
edwin-zvs merged commit 1a8df79 into main Aug 8, 2026
1 check passed
@edwin-zvs
edwin-zvs deleted the session-list-a11y branch August 8, 2026 15:04
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.

web UI: session list has keyboard nav but no listbox/option accessibility semantics

1 participant