Skip to content

fix(a11y): improve screen reader and keyboard access - #328

Merged
MrYuion merged 3 commits into
developfrom
t3/improve-app-accessibility
Oct 26, 2026
Merged

MrYuion merged 3 commits into
developfrom
t3/improve-app-accessibility

Conversation

@MrYuion

@MrYuion MrYuion commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

Screen reader and keyboard users had several blockers in Backoffice:

  • Pages had no landmarks. Keyboard users had to tab through the menu and the whole item list to reach the content.
  • 40 of 42 dialogs had no accessible name.
  • simple-table (32 uses) and the upload library had role="table" with no rows, column headers or cells. 31 columns had blank headers.
  • The item tabs pointed at an empty, hidden tab panel.
  • About 74 spinners and progress bars had no name, and 31 idle progress bars stayed in the accessibility tree.
  • Rows could be reordered only by dragging, with no keyboard alternative.
  • Some controls had no name, or showed their state only by colour: module and trigger status dots, edge online state, settings toggles (a checkbox inside a button), badges and the filter.

An axe-core scan of the main routes found 226 "content not in a landmark" items, plus table, button-name, label, progress-bar and nested-control errors.

Solution

  1. Landmarks: each page shell has <main id="main-content">. The menu and item list are <nav> landmarks, and active links have aria-current="page". A "Skip to main content" control comes first in the menu and right after the item search, which has focus on load. <html lang> follows the locale.
  2. Dialog names: a new appDialogTitle directive names the dialog by its heading, like mat-dialog-title without its styles.
  3. Tables: rows, column headers, cells and aria-sort, with aria-rowcount/aria-rowindex for virtual scrolling. A new hide_name column option gives blank headers a name that only screen readers hear.
  4. Tab panel: each shell wraps its routed content in a real mat-tab-nav-panel, and the item tabs control it.
  5. Loaders: a new LoadingLabelDirective names unnamed spinners and bars "Loading". Idle bars use invisible instead of opacity-0, so they keep their space but leave the accessibility tree.
  6. Keyboard reorder: the drag handle moves its row with the up and down arrow keys. It sends the same event as a drag, so existing confirm dialogs still apply. Focus follows the moved row, but only if the move really happened.
  7. Controls: the settings toggle is one role="checkbox" button. Status dots have text, e.g. "Connection status: Connected". Badges, the filter, the debug checkbox and the edge link have names. Error toasts are assertive, the offline banner is an alert, and icons with their own tooltip are named by it.

The only visible change is the skip control, which shows only when it has keyboard focus.

After the change, axe reports 0 violations on the scanned routes, including all system tabs. Lint, build, unit tests (1077) and the mock e2e suite (150) pass.

Not in this PR: a single-pointer alternative to dragging rows (WCAG 2.5.7). Keyboard moves are added, pointer buttons are not.

Reviewed with sol-loop: 4 rounds with gpt-6.1-sol on high (2 per commit).

Changes made by Claude Opus 5.5 (claude-opus-5-5) in Claude Code, running in T3 Code.

🤖 Generated with Claude Code

- Page shells have a main landmark, the menus are nav landmarks and
  active links mark the current page. A skip control moves focus to
  the content. The page language follows the locale.
- Dialogs are named by their heading through a new appDialogTitle
  directive, which works like mat-dialog-title without its styles.
- simple-table and the upload library expose rows, column headers,
  cells and sort state, with row numbers for virtual scrolling.
- Settings toggles are one checkbox, not a checkbox in a button.
  Module status, badges, filters, loaders and icon tooltips have
  names. Error toasts are announced at once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
backoffice Ready Ready Preview Oct 26, 2026 6:16am UTC

@greptile-apps

greptile-apps Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[High impact] The PR appears safe to merge; the previous language-fallback issue is fixed.

Summary

This PR improves screen reader and keyboard access across page shells, dialogs, tables, loaders, and controls.

  • Keyboard users can skip to page content and follow item tabs to their panel.
  • Backoffice tables expose their headings, rows, and sort state to screen readers.
  • Reorderable table rows move with the arrow keys as well as dragging.
  • Open dialogs are named from the heading people see.
  • Loading, controls, and status messages announce their names and state.

Reviews (3) · Last reviewed commit: "fix(a11y): set page language from the lo..." · Reviewed by Greptile

Comment thread src/app/common/locale.service.ts Outdated
- The item tabs control a real tab panel that holds the page content,
  not an empty hidden one.
- Spinners and progress bars without a name are named "Loading".
  Idle progress bars use visibility instead of opacity, so screen
  readers no longer find them.
- Table rows that can be dragged also move with the up and down
  arrow keys on the handle. Focus follows the moved row.
- Columns with a blank header get a name for screen readers through
  the new hide_name option. Trigger and edge status dots get text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MrYuion MrYuion changed the title fix(a11y): add landmarks, dialog names and table roles fix(a11y): improve screen reader and keyboard access Oct 26, 2026
The page language was set before the translations loaded. A locale
without a translation file, e.g. fr-FR, shows the English fallback,
but the page stayed marked as French, so screen readers used the
wrong voice. Set it once loading ends, from the translations in use.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 26, 2026

Copy link
Copy Markdown

Deployment failed for project backoffice with the following error:

Resource is limited - try again in 24 hours (more than 100, code: "api-deployments-free-per-day").

Learn More: https://vercel.com/placeos?upgradeToPro=build-rate-limit

@MrYuion
MrYuion merged commit 1a0251e into develop Oct 26, 2026
6 of 7 checks passed
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