Skip to content

a11y(backend): keyboardable matrix, qualified actions, live toasts (#2343 #2344 #2351) - #2360

Merged
fl0rianr merged 1 commit into
kpoin/ui-testingfrom
feat/gui3-backend-a11y
Jun 23, 2026
Merged

fl0rianr merged 1 commit into
kpoin/ui-testingfrom
feat/gui3-backend-a11y

Conversation

@kpoineal

Copy link
Copy Markdown
Collaborator

Summary

Three NVDA/keyboard accessibility fixes in BackendManager.tsx (Group C of the gui3 a11y series).


#2343 — Backend matrix cells are now keyboard-operable

Problem: Clickable <div> cells in the capability matrix had no keyboard focus, no ARIA role, and no selected state — mouse-only.

Fix: Applied the overlay-button pattern (same as recipe-card__overlay-btn in PresetManager):

  • .cell--selectable gains position: relative.
  • A <button class="cell__select-btn" aria-pressed aria-label> with position: absolute; inset: 0; z-index: 0 covers the full cell.
  • aria-pressed reflects selection state ("true"/"false").
  • aria-label includes the human-readable recipe label and backend identifier (e.g., "llama.cpp (vulkan), Available").
  • Action buttons (.cell__actions) get position: relative; z-index: 1 so they remain clickable above the overlay.
  • The global :focus-visible ring (2px accent outline) renders on the overlay button when keyboard-focused.

Closes #2343


#2344 — Backend action buttons now have qualified accessible names

Problem: Install, Update, Uninstall buttons had generic labels ("Install" / "Update" / "Uninstall") that collided across multiple backends when announced by NVDA.

Fix: Added aria-label to each action button containing both the recipe label and backend identifier:

  • aria-label="Install llama.cpp (vulkan)"
  • aria-label="Update llama.cpp (vulkan)"
  • aria-label="Uninstall llama.cpp (cpu)"
  • aria-label="Setup guide for ryzenai (hybrid)"

Visible button text is unchanged; aria-label overrides the accessible name for AT.

Closes #2344


#2351 — Toast and preset notices are now live regions

Problem: backends__toast and context-rail__notice were conditionally mounted ({msg && <div>…</div>}). Mounting a node with content does not trigger NVDA live region announcements — the element must already exist in the DOM before content changes.

Fix: Two always-present <div role="status" aria-live="polite" aria-atomic="true" class="sr-only"> elements added:

  • data-backends-toast-live — mirrors toastMsg (installing/updating/uninstalling progress and completion).
  • data-backends-preset-notice-live — mirrors presetNotice (preset assignment confirmations and incompatibility notices).

Visual toast and notice remain conditionally rendered (no visual change). Only the sr-only live regions are always in the DOM.

Closes #2351


Tests

8 new tests added to tests/a11y.spec.ts (A35–A42):

  • A35–A38: matrix cell button existence, toggle, focusability, label qualification
  • A39–A40: Install/Uninstall button qualified aria-label
  • A41–A42: toast and preset-notice live region attributes

Result: 58 passed / 7 skipped / 0 failed (baseline was 50 passed / 7 skipped / 0 failed)


A human reviews before merge. No behavior or visual changes for mouse/pointer users.

@kpoineal

Copy link
Copy Markdown
Collaborator Author

Internal review: LGTM (advisory — human approval required)

Authored by Mattingly; reviewed by Lovell.

  • BackendManager.tsx ~line 739 — overlay button (a11y: Backend matrix cells are mouse-first #2343): cell__select-btn at position: absolute; inset: 0; z-index: 0 with action buttons at z-index: 1 is the established pattern (mirrors recipe-card__overlay-btn in PresetManager). aria-pressed for selection state is correct; aria-label includes recipe label + backend identifier + badge text — sufficient for NVDA disambiguation. position: relative added to .cell--selectable in styles.css. ✅
  • Action button labels (a11y: Backend action buttons are generic #2344): Install/Update/Uninstall/Setup guide for () — qualified, consistent format. Visible text unchanged. ✅
  • Live regions (a11y: Backend/preset toasts and notices are not consistently live regions #2351): Always-present role="status" aria-live="polite" aria-atomic="true" .sr-only pattern is correct — conditional mounting does not trigger NVDA, content mutation on already-mounted element does. Two separate regions (toast, preset notice) in different DOM positions; simultaneous updates are an edge case the pattern handles acceptably. ✅
  • Tests A35–A42: A36 (toggle aria-pressed true→false→true) is the critical behavioral assertion. A41/A42 verify presence and attributes of both live regions. Solid coverage.
  • Minor: if a future recipe type has no entry in RECIPE_LABELS, the raw recipe ID surfaces in the aria-label — acceptable fallback, not a bug.

…e-region toasts

Closes #2343 #2344 #2351

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@kpoineal

Copy link
Copy Markdown
Collaborator Author

Rebased onto merged base a8718a7. Conflicts resolved in ests/a11y.spec.ts (branch's new tests renumbered to A51–A58) and ACCESSIBILITY.md (union merge). Final suite: 69 passed / 7 skipped / 0 failed. — Mattingly

@kpoineal
kpoineal force-pushed the feat/gui3-backend-a11y branch from 5ea8eeb to 72279f2 Compare June 23, 2026 12:58

@fl0rianr fl0rianr left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@fl0rianr
fl0rianr merged commit e0d1c92 into kpoin/ui-testing Jun 23, 2026
55 of 57 checks passed
@fl0rianr
fl0rianr deleted the feat/gui3-backend-a11y branch June 23, 2026 14:42
fl0rianr pushed a commit that referenced this pull request Jul 17, 2026
…e-region toasts (#2360)

Closes #2343 #2344 #2351

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants