Skip to content

feat(client): announce every InfoBar kind to a screen reader (UI/UX v3 P6c) - #94

Merged
mizu-jun merged 1 commit into
masterfrom
p6c-infobar-accessibility
Aug 29, 2026
Merged

feat(client): announce every InfoBar kind to a screen reader (UI/UX v3 P6c)#94
mizu-jun merged 1 commit into
masterfrom
p6c-infobar-accessibility

Conversation

@mizu-jun

Copy link
Copy Markdown
Owner

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_banner occurred zero
times
in accessibility.rs — the surface that reports "your shell could not
be 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 — one Role::Alert per queued bar, in stack order,
    labelled with the same text the bar draws. Error → Live::Assertive; offline
    and update → Live::Polite (the offline bar reports a condition, not an event).
  • Ids keyed by slotinfo_bar_node_id(slot) at 28..=30, so the id of "the
    error bar" does not move when the bar above it is dismissed, which is what a
    platform adapter caches. The match is exhaustive: a fourth InfoBarSlot cannot
    compile without a node. NodeIdKind::UpdateBanner / NodeId(10) are retired for
    NodeIdKind::InfoBar { slot }.
  • Bars past the cap are still announced+{count} more is a constraint of
    a two-bar stack, not of a screen reader.
  • infobar-more-count ×8 locales, drawn on the bottom bar via
    StackLayout::more_label, so the cap and the way the cap is reported live
    together. This is the phase's one new string (spec §3.5).
  • infobar::contiguous — the VecDeque flattening the vertex builder
    open-coded, now that the AccessKit path needs the same slice.

Gates

Gate Where
G-a11y every_info_bar_kind_is_announced — exhaustive over InfoBarKind, asserts Role::Alert + a non-empty label per slot; the_error_bar_is_announced_assertively; a_bar_past_the_drawn_cap_is_still_announced
G-hash tree_state_hash_detects_a_bar_appearing_and_changing (add / reword / remove) and tree_state_hash_ignores_the_offline_elapsed_count
G-i18n nexterm_i18n::tests::test_all_locales_have_same_keys_as_en, plus the_count_suffix_reports_the_bars_the_cap_dropped

Verification

  • cargo fmt --check — clean
  • cargo clippy --workspace --all-targets -- -D warnings — 0 warnings
  • cargo test --workspace — all green (1101 in nexterm-client-gpu)

Not covered, as in P4/P5/P6b: appearance. The +{count} more suffix on a small
window joins the on-device pass (spec §7).

Generated with Claude Code
via Happy

…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>
@github-actions

Copy link
Copy Markdown

Coverage report


Generated by cargo llvm-cov (workspace minus nexterm-client-gpu and nexterm-i18n).

@mizu-jun
mizu-jun merged commit 301f88e into master Aug 29, 2026
13 checks passed
@mizu-jun
mizu-jun deleted the p6c-infobar-accessibility branch August 29, 2026 13:22
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