feat(client): announce every InfoBar kind to a screen reader (UI/UX v3 P6c) - #94
Merged
Conversation
…3 P6c)
Before this change the update banner was the only one of the three
top-of-screen messages with an AccessKit node. The offline bar had none —
it was in the tree hash, so it forced a rebuild that added nothing — and
the server error, the surface that reports "your shell could not be
launched" and "your config failed to load", occurred zero times in
accessibility.rs. A screen-reader user was never told.
One builder driven by `InfoBarKind` replaces the update-only one:
- `build_info_bar_nodes` emits one `Role::Alert` per queued bar in stack
order, labelled with the same text the bar draws so the two cannot
drift. An error is `Live::Assertive`; the offline and update bars are
`Live::Polite` — the offline bar reports a condition, not an event.
- Node ids are keyed by slot (`info_bar_node_id`), not by queue position,
so the id of "the error bar" does not move when the bar above it is
dismissed. The match is exhaustive, so a fourth slot cannot be added
without a node (G-a11y). `NodeIdKind::UpdateBanner` and NodeId(10)
are retired in favour of `NodeIdKind::InfoBar { slot }` at 28..=30.
- Bars past the drawn cap are announced in full: `+{count} more` is a
constraint of a two-bar stack, not of a screen reader.
The tree hash already covered slot + message from P6b; this adds the
gates it was missing — a bar appearing, being reworded or being removed
changes it, and the offline bar's elapsed seconds do not (G-hash).
Completes the phase's one new string: `infobar-more-count` in all 8
locales (G-i18n), drawn on the bottom bar via `StackLayout::more_label`
so the cap and the way the cap is reported cannot disagree. The
`VecDeque` flattening the vertex builder open-coded moves to
`infobar::contiguous`, now that the AccessKit path needs it too.
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
Coverage reportGenerated by |
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.
Why
P6's §1.2 measurement: of the three top-of-screen banners, only the update
notice ever had an AccessKit node. The offline bar had none (it was hashed, so
it forced a tree rebuild that added nothing), and
error_banneroccurred zerotimes in
accessibility.rs— the surface that reports "your shell could notbe launched" and "your config failed to load" was never announced.
The spec keeps P6c separate from the P6b migration precisely because this is the
most valuable change in the phase and should be reviewable without the migration
diff around it.
What
build_info_bar_nodes— oneRole::Alertper queued bar, in stack order,labelled with the same text the bar draws. Error →
Live::Assertive; offlineand update →
Live::Polite(the offline bar reports a condition, not an event).info_bar_node_id(slot)at 28..=30, so the id of "theerror bar" does not move when the bar above it is dismissed, which is what a
platform adapter caches. The match is exhaustive: a fourth
InfoBarSlotcannotcompile without a node.
NodeIdKind::UpdateBanner/ NodeId(10) are retired forNodeIdKind::InfoBar { slot }.+{count} moreis a constraint ofa two-bar stack, not of a screen reader.
infobar-more-count×8 locales, drawn on the bottom bar viaStackLayout::more_label, so the cap and the way the cap is reported livetogether. This is the phase's one new string (spec §3.5).
infobar::contiguous— theVecDequeflattening the vertex builderopen-coded, now that the AccessKit path needs the same slice.
Gates
every_info_bar_kind_is_announced— exhaustive overInfoBarKind, assertsRole::Alert+ a non-empty label per slot;the_error_bar_is_announced_assertively;a_bar_past_the_drawn_cap_is_still_announcedtree_state_hash_detects_a_bar_appearing_and_changing(add / reword / remove) andtree_state_hash_ignores_the_offline_elapsed_countnexterm_i18n::tests::test_all_locales_have_same_keys_as_en, plusthe_count_suffix_reports_the_bars_the_cap_droppedVerification
cargo fmt --check— cleancargo clippy --workspace --all-targets -- -D warnings— 0 warningscargo test --workspace— all green (1101 innexterm-client-gpu)Not covered, as in P4/P5/P6b: appearance. The
+{count} moresuffix on a smallwindow joins the on-device pass (spec §7).
Generated with Claude Code
via Happy