a11y: dynamic aria-selected + roving tabindex (phase 3a canary intake)#1156
Merged
Conversation
Layered on phase 2 (PR #1111). Completes the listbox correctness story by making `aria-selected` and the tab order respond to selection changes after initial render. ReactiveListWidget — base class additions: - New virtual `protected isItemIdSelected(id): boolean`. Default matches `selectedId`; subclasses override to use their own state. Drives both aria-selected and the roving tabindex. - New Lit `updated()` override walks `.list-item` wrappers after every render and syncs aria-selected + tabindex via the new `syncListSelection()` helper. The visual `.active` class was already reactive via Lit (subclasses re-render their inner template); this hook keeps the ARIA state on the static EntityScroller-managed outer wrapper in sync without re-rendering the wrapper. - Initial `getRenderFunction`: tabindex now depends on `isItemIdSelected` (selected → 0, others → -1) rather than the blanket `tabindex=0` from phase 2. - Fallback: if no item is currently selected, the first item gets tabindex=0 so the list remains a single Tab stop. - Arrow-key navigation in `onListKeydown` updates roving tabindex as focus moves — newly-focused item gets tabindex=0, all others -1. Keeps the list a single tab stop after the user has navigated. RoomListWidget: - Overrides `isItemIdSelected`: `id === this.currentRoomId`. When the active room changes, the @reactive currentRoomId triggers a Lit update → updated() → syncListSelection() walks the DOM and the new room becomes aria-selected="true" with tabindex=0, old room drops to "false" / -1. UserListWidget: - Overrides `isItemIdSelected`: `id === this._selectedUserId`. Same reactive pattern. Out of scope (further phase 3 follow-ups, not blockers): - Color-contrast audit across themes - <div onclick> → <button> migration - axe-core lint gate in CI - Focus restoration when a selected item is removed/filtered out `npm run build:ts` is green. Stacked on PR #1111; once that merges, this PR's diff against main reduces to just the phase-3a changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Refs #1127
Refs #1099
Supersedes the canary-intake portion of #1112.
Proof