Vetrix Docs

Vetrix Accessibility — Manual Verification Procedures

Companion to plans/accessibility.md §6 ("Verification checklist per page"). The §6 checklist enumerates the what; this document is the how. It gives step-by-step walkthrough scripts that a reviewer can execute end-to-end on https://www.gitvetrix.test/ (or any deployed Vetrix environment) to confirm a release's assistive-technology behaviour before sign-off.

Every script is deliberately concrete: it names the keystrokes, the expected screen-reader utterance, and the pass/fail observation. No hand-waving. A pass means "the SR said the expected sentence and focus landed on the expected element"; a fail is filed as a ticket with the script step that broke.

This is a docs-only reference. It does not change application behaviour. When the application changes, update the relevant expected-utterance lines here in the same PR.


0. Preconditions — one-time environment setup

0.1 Host machines and browser matrix

The three combinations below are mandatory. The plan §6 calls for all three; never substitute one for another.

# Screen reader Browser Host OS Notes
1 NVDA 2024.x Firefox ESR Windows 10 / 11 Preferred on Windows; works best with Firefox + UIA.
2 VoiceOver Safari current macOS 14 or later Built-in. Use a non-US keyboard layout only if paired.
3 Orca 46+ Firefox ESR Ubuntu 22.04 / 24.04 Run under a GNOME session (Wayland or X11).

Chrome-based browsers with NVDA or VO are not an acceptable substitute — behaviour diverges, particularly around live regions, aria-current, and the Tabs pattern.

0.2 Accounts and fixture data

Sign in as three users before starting any walkthrough so you don't blow your flow on a login redirect mid-test:

  • vetrix_admin (admin) — /admin access.
  • vetrix_dev1 (developer) — owns vetrix_dev1/hello-php repo.
  • vetrix_reviewer (read-only on repo) — cannot merge, cannot admin.

Fixture repo requirements:

  • At least one open issue with ≥3 comments, labels, an assignee, and a workflow state other than "To do".
  • At least one MR in "Review requested" with an inline comment thread on a diff line.
  • At least one pipeline with a succeeded and a failed job (for log viewer).
  • A board with three columns and ≥5 cards per column (for keyboard DnD).
  • A user list in /admin/users with ≥20 users so pagination appears.

0.3 Common SR shortcuts used below

Action NVDA VoiceOver Orca
Start / stop SR Insert+Ctrl+N / Insert+Q Cmd+F5 Super+Alt+S
Next heading H VO+Cmd+H H (browse mode)
Previous heading Shift+H VO+Cmd+Shift+H Shift+H
List of landmarks NVDA+F7 → Landmarks VO+U → Landmarks Orca+L → Landmarks
Read current focus NVDA+Tab VO+F5 Orca+KP_Plus
Next form field F VO+Cmd+J F
Read the whole page NVDA+Down Arrow VO+A Orca+KP_Plus twice
Toggle browse/focus (virtual buffer) NVDA+Space (not applicable) Insert+Space

Keep a note-taker open. The pass criterion is "the SR said the expected sentence"; an approximate match is not a pass.


1. NVDA + Firefox ESR on Windows — walkthrough scripts

NVDA must be restarted between pages if the speech viewer starts truncating long output (rare but happens on the MR diff page). Run with the Speech Viewer open (NVDA menu → Tools → Speech Viewer) so you can capture exact utterances for tickets.

1.1 Login — /login

  1. Cold-load https://www.gitvetrix.test/login in a fresh Firefox profile.
    • Expected: Page title announced as "Sign in · Vetrix". NVDA reads the <h1> "Sign in to Vetrix".
  2. Press Tab once.
    • Expected: "Skip to main content, link". Pressing Enter moves focus to <main id="main-content">.
  3. Press Tab until focus reaches the first form field.
    • Expected: "Username, required, edit, blank". Not "Enter username" (placeholder-as-label is a fail).
  4. Type an invalid username, Tab to Password, type anything, press Enter.
    • Expected: NVDA announces the error alert once, e.g. "Alert — Incorrect username or password. Try again." The error region must have role="alert" (§3.2). The Username field regains focus.
  5. Fix the credentials, press Enter.
    • Expected: After redirect, NVDA reads the new <h1> ("Repositories" or your landing heading). Focus lands on <main> or <h1>, not on the <body> (§1.1 route-change focus).
  6. If MFA challenge is enabled: the 6-digit code input must be announced as "Enter 6-digit code, required, edit" and focus must move to it automatically (§4.2 /login).

Fail if: skip link missing, error is silent, focus jumps to the URL bar after login, or MFA input does not receive focus.

1.2 Repository browse — /vetrix_dev1/hello-php

  1. Navigate to the repo landing page. Press D to jump to the repo sub-nav landmark.
    • Expected: "Repository, navigation". Arrow-keys walk Code / Issues / Pull requests / Pipelines / Packages / Pages / Settings.
    • Expected: The active tab reports "current page" (from aria-current, not styling alone — §1.2).
  2. Press H repeatedly to walk headings.
    • Expected: one <h1> = "vetrix_dev1 / hello-php", followed by <h2>s for each section (README, About, Clone, Stats). No <h3> appears before an <h2>.
  3. Tab to the Clone disclosure.
    • Expected: "Clone, button, collapsed". Enter or Space expands it to "expanded"; the HTTPS/SSH panel is announced.
  4. Tab to the Copy HTTPS URL button inside the Clone panel.
  5. Switch to Code tab, then Tab into the file list.
    • If the tree renders as a table: each row announces <th scope="row"> file/directory name plus "(directory)" or "(file)" (§4.7).
    • If the tree is a WAI-ARIA tree: Right Arrow expands a collapsed <treeitem> and NVDA says "expanded, level 2" etc.
  6. Open a file (src/index.php). Press F to step through interactive buttons.
    • Expected: "Raw, link", "Blame, link", "History, link" — each has a unique accessible name (§2.15/§4.7).

1.3 Issue detail — /vetrix_dev1/hello-php/issues/1

  1. Land on the page. Press H.
    • Expected: <h1> reads the issue title (not the number, not the status — §4.9). Status badge is a sibling with a text value ("In Progress"), announced as part of reading order.
  2. Press R (next landmark region) or NVDA+F7 to list landmarks.
    • Expected: Regions include <main>, a <section aria-label="Issue sidebar">, and <section aria-label="Comments">.
  3. Tab through the sidebar metadata.
    • Expected: Each field announces as a pair: "Assignees: alice, bob", "Labels: bug, priority: high", "Milestone: v1.0", "Due date: 2026-05-01". Empty fields announce "Not set" as text, never silence (§4.9).
  4. Tab to the Watching toggle.
    • Expected: "Watching, toggle button, pressed" (or "not pressed"). Space flips it; SR announces the new state.
  5. Jump to the comment composer with E (edit field).
    • Expected: Composer is a labelled textarea ("Write a comment"). MarkdownEditor toolbar is exposed as a toolbar region with labelled buttons ("Bold", "Italic", etc. — §2.18).
  6. Submit an empty comment.
    • Expected: role="alert" announces "Comment body is required". Focus returns to the textarea.
  7. Click Move to In Progress (workflow transition). If a pre-condition fails, a polite live region announces the failure reason (§4.9). Do not accept inline red text as a substitute.

1.4 Merge request review — /vetrix_dev1/hello-php/pulls/1

  1. Press H to confirm the <h1> is the MR title.
  2. Press T (next tab stop) or Tab until you hit the Tabs group (Conversation / Commits / Files / Checks).
    • Expected: NVDA announces "Conversation, tab, selected, 1 of 4". Right Arrow moves to Commits and announces "Commits, tab, 2 of 4". Count badges inside tabs are part of the accessible name ("Files, 4 of 4, 12 changed" — §2.8).
  3. Switch to Files. Tab into the DiffViewer.
    • Expected: The "+" / "-" glyph in each diff line is read as text, not skipped (§2.16). A line-number is either aria-hidden or part of a proper table.
  4. Tab to a "Add comment on line N" button.
    • Expected: "Add comment on line 42 of src/index.php, button" (§4.8). Pressing Enter opens an inline thread composer with focus trapped until you submit or dismiss.
  5. Submit a review.
    • Expected: The approve/request-changes/comment radio group is wrapped in <fieldset> with legend "Review decision" (§4.8).
  6. Confirm the merge button.
    • Expected: ConfirmModal title = "Merge pull request", button label = "Merge pull request #1 into develop" (§3.4). Escape closes and returns focus to the Merge trigger.
  7. Toggle Mark as viewed on one file.
    • Expected: "Mark src/index.php as viewed, checkbox, checked".

1.5 Board — /vetrix_dev1/hello-php/boards/1

This is the single most important SR test — the kanban board is a 🔴 blocker risk zone (§4.9).

  1. Press R to list regions.
    • Expected: each column is <section aria-label="To do, 5 cards"> etc. — never an unlabelled div stack.
  2. Tab to the first card in "To do".
    • Expected: NVDA reads "ISSUE-123 Fix login redirect, priority high, assigned to alice, 1 of 5, listitem". The card is a single focus stop.
  3. With focus on the card, open its actions menu (Enter or a visible "Card menu" button).
    • Expected: Menu contains "Move to next column", "Move to previous column", "Move to top of column", "Move to bottom of column", and "Open". No DnD-required step should be keyboard-exclusive (§4.9).
  4. Activate "Move to next column".
    • Expected: a polite live region announces "Moved ISSUE-123 to In progress". The column region labels update their counts ("To do, 4 cards"; "In progress, 6 cards"). Focus stays on the moved card.
  5. Let the test board receive a WebSocket update (another user moves a card).
    • Expected: a batched polite utterance fires no more than once per ~5 seconds: "3 cards moved in the last 5 seconds" (§3.5). Each individual move does not announce.

1.6 Pipeline log — /vetrix_dev1/hello-php/pipelines/1

  1. Navigate in. Press H to confirm <h1> = pipeline name + run number.
  2. Tab into the stage/job graph region.
    • Expected: A text-list fallback exists (<ul>) reading "{stage}: {job} — {status}" for each node (§4.10). If only an SVG graph is present, that is a fail.
  3. Open the log panel for a running job. NVDA with default verbosity must not announce every new log line (sensory overload — §3.5).
    • Expected: A polite live region announces only the terminal state ("Build succeeded" or "Build failed, exit code 1").
  4. Tab to the Pause autoscroll toggle.
    • Expected: "Pause autoscroll, toggle button, not pressed". Space pauses; the log stops jumping but continues rendering.
  5. Tab to Cancel.
    • Expected: Activating Cancel opens <ConfirmModal> labelled "Cancel pipeline run", confirm button = "Cancel run #1"; Escape returns focus (§3.4 / §4.10).

1.7 Admin user list — /admin/users

  1. Press D for the Admin sub-nav landmark; NVDA says "Admin, navigation, Users, current page" (§4.14).
  2. Press T to jump to the user table.
    • Expected: <caption> (visually hidden) reads e.g. "All users, 137 rows". Every column header has scope="col"; each username cell is <th scope="row"> (§2.9).
  3. Arrow keys inside the table navigate cells (NVDA table-nav: Ctrl+Alt +Arrows). Column header announces on every cross-column move.
  4. Tab to a row-action button.
    • Expected: "Impersonate user alice@example.com, button"; never plain "Impersonate" (§2.9 row actions).
  5. Activate Impersonate.
    • Expected: ConfirmModal, title "Impersonate user", body explains the 4-hour session and audit-log entry, confirm button labelled "Impersonate alice@example.com". On accept, a role="status" banner ("You are impersonating alice — 4 h remaining, Stop impersonating") is announced once (§1.2).
  6. Filter search.
    • Expected: The search input is type="search". After debounce, a polite live region announces "42 results" once (§3.1).

2. VoiceOver + Safari on macOS — walkthrough scripts

Before starting:

  • System Settings → Accessibility → VoiceOver → Open VoiceOver Utility → Verbosity → Text: set "When text is selected" to "Speak text". Set punctuation to "All" for the duration of the review.
  • System Settings → Accessibility → VoiceOver → Commanders → Quick Nav: enable Quick Nav (toggle with Left+Right Arrow held together) so single-key navigation (H, F, R) matches NVDA usage.
  • Safari: Develop → Enable JavaScript from Apple Events and Safari → Settings → Advanced → Press Tab to highlight each item on a webpage on — Safari defaults to skipping non-link controls otherwise.

2.1 Login, repo browse, issue detail, MR review, board, pipeline log, admin

Re-run the seven scripts from §1 against VoiceOver with the following substitutions:

NVDA step VoiceOver equivalent
NVDA+F7 → Landmarks VO+U → Landmarks menu
H / Shift+H H / Shift+H (with Quick Nav) or VO+Cmd+H / VO+Cmd+Shift+H
NVDA+Down Arrow (read all) VO+A
Read current focus VO+F5
Enter forms mode VoiceOver doesn't have a forms mode; interactors always active
Ctrl+Alt+Arrow (table nav) VO+Cmd+Arrow
Toggle browse / focus (VoiceOver is always live)

Points where Safari/VO behaviour must specifically be observed (NVDA passing doesn't imply VO passes):

  • Skip-to-content link (§1.1). VO+F5 after first Tab must report "Skip to main content" — Safari sometimes skips this if display:none is used instead of clip: rect(…) + position:absolute. If VO says nothing, that is a fail even if NVDA passed.
  • role="alert" firing. Safari + VO is historically stricter — a fresh DOM insertion of a node with role="alert" is announced; a pre-existing node whose text changed is not. The login error and the empty-comment error must therefore be inserted, not merely filled. If VO is silent on error but NVDA announced it, file a ticket.
  • Tab pattern (§2.8). VO announces "Conversation, tab 1 of 4, selected" only if aria-selected is present; if the implementation instead uses aria-current on a link-styled tab, VO will say "current page" which is wrong for tab semantics (§2.8).
  • Live-region chattiness. VO with polite regions will still queue multiple messages; if a burst of SSE log lines slips past the §3.5 debounce, VO will read them all. This is a common regression — test it.
  • Combobox (§2.4). In Safari, Headless UI's Combobox must expose aria-expanded on the input; without it VO doesn't announce the listbox opening. Test the assignees picker on /issues/new and confirm VO says "expanded, 8 items" when you open the menu.
  • <progress> on milestones (§4.9). Safari narrates <progress> with its value only; VO users rely on the visible text equivalent ("8 of 12 issues closed, 67%"). If the text equivalent is absent, VO will read only "67%" with no subject.
  • PDF-like content (docs/): not applicable — Vetrix does not render PDFs inline today. Skip.

2.2 Reduced motion, forced color scheme (macOS)

System Settings → Accessibility → Display → Reduce motion on. Re-visit:

  • /pipelines/[id] — the line-anchor jump must not scroll smoothly; reduce-motion must snap to the line (§2.15 — scrollIntoView({ behavior: "smooth" }) is a fail).
  • /boards/[id] — card move animations must be ≤0.01s; the DnD reorder must not slide when reduced-motion is on.

System Settings → General → Appearance → Dark. Re-visit all seven pages above and confirm dark-mode tokens resolve — no invisible text, no blocks of black-on-black (§1.3).

2.3 Voice Control spot-check (macOS)

Voice Control is a macOS feature orthogonal to VO — it turns visible labels into activation phrases. Any control whose accessible name ≠ visible label is a Voice Control fail (WCAG 2.5.3 "Label in Name").

Procedure:

  1. System Settings → Accessibility → Voice Control → on. Say "Show numbers" to overlay activation numbers on every control; say "Hide numbers" to clear.
  2. On /login, say "Click Sign in".
    • Expected: the Sign in button activates. If you have to fall back to "Click 3" (the number overlay), the accessible name drifted from the visible label — file a ticket.
  3. On /vetrix_dev1/hello-php/issues/1, say "Click Move to In Progress".
    • Expected: the workflow transition activates. If VO announced "Move issue to In Progress, button" but Voice Control requires "Click Move issue to In Progress" to dispatch, that's a Label-in-Name violation. Fix by visibly displaying the full accessible name or shortening aria-label to match.
  4. On /admin/users, say "Click Impersonate alice at example dot com".
    • Expected: the impersonate action opens for Alice. If the button's visible label is just "Impersonate" and its aria-label is "Impersonate user alice@example.com", Voice Control will fail this step — §2.9 requires the row-subject to be part of the visible composition (e.g. the button sits adjacent to the username cell) or the accessible name to begin with the visible text.

Run the Voice Control spot-check on:

  • /login → "Click Sign in"
  • /vetrix_dev1/hello-php → "Click Clone"
  • /vetrix_dev1/hello-php/issues/new → "Click Submit issue"
  • /vetrix_dev1/hello-php/pulls/1 → "Click Merge pull request"
  • /admin/users → "Click Impersonate"

A failure on any of the five is a 🟠 ticket against the specific page.


3. Orca + Firefox ESR on Ubuntu — walkthrough scripts

Orca is the only Linux target; Firefox is the only supported browser. Chromium with Orca is out of scope.

3.1 Orca preflight

  1. Settings → Accessibility → Screen reader on, or Super+Alt+S.
  2. Orca preferences (Orca+Space):
    • General → Keyboard layout: set to match your physical layout (Laptop keyboard layouts use Caps Lock as the Orca modifier; Desktop uses KP_Insert).
    • Voice → Verbosity: Verbose during verification.
    • Web → "Present layout tables" off (Vetrix should not ship layout tables; toggling this off surfaces any accidental <table> used for layout).
    • Web → "Automatically start speaking a page when it is loaded" on.

3.2 Login, repo browse, issue detail, MR review, board, pipeline log, admin

Re-run §1's seven scripts. Orca-specific notes:

  • Browse mode is on by default on web content; press Orca+A to toggle focus mode for rich widgets (MarkdownEditor, Combobox listboxes). Vetrix must operate in browse mode for reading and focus mode only when necessary — if /boards/[id] requires focus mode to interact with cards, that's a UX regression, file it (§4.9).
  • Landmarks list: Orca+L opens the Navigation list; select "Landmarks". Each page from §1 must expose banner, main, a nav per nav region, and contentinfo on the footer.
  • Skip-link behaviour: Orca + Firefox sometimes fails to announce a pre-existing skip link; it must actually receive focus on first Tab so that focus mode kicks in and Orca re-reads. The script above (§1.1 step 2) therefore holds.
  • Live region flake: Orca's polite-queue is empirically the slowest of the three SRs. After a toast, wait ~2 s before interacting — if Orca hasn't announced by then, the aria-live region is wrong (§1.5).
  • Table column header re-announcement: when you move to a cell in a different column inside <table>, Orca announces the column header name only if <th scope="col"> is set. Missing scope is a silent failure in NVDA (it announces anyway via header discovery) but a loud failure in Orca (silence). Use Orca as the authoritative test for §2.9 scope correctness.
  • Combobox (§2.4): Orca announces "expanded" on the trigger only if aria-expanded is on the same element that receives focus. Headless UI satisfies this; custom wrappers sometimes do not. Test on /issues/new and /pulls/new reviewer pickers.
  • <progress>: Orca announces both the visible text and the progressbar value, so a correct milestone row reads "Milestone v1.0, 8 of 12 issues closed, 67%, progress bar, 67%".

3.3 Orca + reduced motion

GNOME offers Settings → Accessibility → Reduced animation — toggle it on and re-run the reduced-motion subset from §2.2.

3.4 Orca voice-control note

Orca is not a voice-control tool. Skip §2.3 on Linux — there is no supported Linux voice-control product to verify Label-in-Name against. macOS Voice Control covers the Label-in-Name obligation for the matrix.


4. 400% zoom + 200% text zoom (WCAG 1.4.10 / 1.4.12) — procedure

Run on Firefox with default window sizing 1280×1024. If you use a larger monitor, resize the Firefox window to 1280×1024 first so reflow behaviour matches the spec's reference viewport.

4.1 Page zoom to 400%

  1. Open each page from the test matrix (§1.1–§1.7).
  2. Ctrl++ (Cmd+Plus on macOS) five times to reach 400%. Firefox shows "400%" in the URL bar's zoom indicator.
  3. Observe the page for:
    • No horizontal scrollbar on the document (reflow per 1.4.10). Vertical scroll is expected and fine.
    • No clipped content. The full TopNav actions must still be reachable (collapsed into a menu if necessary, as long as the menu is keyboard-operable).
    • All interactive elements still visible and activatable. Buttons don't overflow their row; inputs don't become unreadably narrow.
  4. Perform one representative action per page at 400%:
    • /login — sign in.
    • /vetrix_dev1/hello-php — open the Clone disclosure and copy the URL.
    • /issues/1 — post a comment.
    • /pulls/1 — switch tabs and open an inline comment thread.
    • /boards/1 — move a card to the next column.
    • /pipelines/1 — toggle pause-autoscroll.
    • /admin/users — filter the list and impersonate.

Fail: any of the above causes a horizontal scrollbar to appear on the document root, or any control needed for the action is clipped off-screen.

4.2 Text-only zoom to 200%

Firefox: View → Zoom → Zoom Text Only checkbox on, then Ctrl++ until the zoom indicator reports 200%.

  1. Revisit each page from the matrix.
  2. Observe for:
    • No text truncation with in visible labels (WCAG 1.4.12 text spacing and 1.4.4 resize text). If a label becomes Comp…, that is a fail — widen or allow wrapping.
    • No loss of functionality. Buttons still clickable; dropdowns still open within the viewport.
    • Line-height preserved. At 200% text zoom with UA default line-height, nothing should overlap.
  3. At 200% text-only zoom, confirm specific widgets:
    • Badges (§2.7) wrap their text without the background losing shape.
    • Tabs (§2.8) with count suffixes ("Files, 12 changed") don't truncate the count.
    • Tables (§2.9) scroll horizontally inside their wrapper but the document itself does not.

Fail: any clipped label, any overlapping line, any lost-to-viewport control.

4.3 Zoom combined with reduced motion

Toggle prefers-reduced-motion: reduce via Firefox DevTools (Accessibility panel → "Simulate", or about:configui.prefersReducedMotion = 1) and re-run §4.1 once. Zoom and reduced motion must not interact badly (e.g. reflow must not rely on animation to stabilise the layout).


5. Chrome DevTools colour-blindness emulation — checklist per status badge

WCAG 1.4.1 forbids colour as the sole indicator. Vetrix has a lot of status badges; every one must pass under every vision-deficiency filter. Chrome DevTools is the canonical tool (Firefox DevTools lacks the matrix).

5.1 Procedure

  1. Chrome: open DevTools → (top-right of DevTools) → More tools → Rendering.
  2. In the Rendering panel, find Emulate vision deficiencies. Available modes:
    • No emulation (baseline)
    • Blurred vision
    • Reduced contrast
    • Protanopia (red-blind)
    • Deuteranopia (green-blind)
    • Tritanopia (blue-blind)
    • Achromatopsia (total colour blindness)
  3. For each status surface listed in §5.2, switch through all seven modes in order and verify the non-colour signal at each step.

5.2 Surfaces and the non-colour signal that must be present

Every cell must pass under every filter. "Pass" = the status is legible without reference to hue.

Surface Plan ref Non-colour signal required
IssueStatusBadge (Open / In Progress / Done) §2.7 Text label + glyph prefix; distinct backgrounds in grayscale.
PriorityBadge (Low / Med / High / Critical) §2.7 Arrow glyph and text ("High"). The arrow alone fails.
WorkflowStateBadge §2.7 Category word ("To do" / "In progress" / "Done") spelled in text next to the badge.
IssueTypeBadge (Bug / Deficiency / Task) §2.7 Type icon + text label.
Pipeline job status (Running / Success / Failed) §4.10 Glyph (↻ / ✓ / ✗) + text; the "running" spinner must also expose role="status" with "Running" text.
Pipeline log level (info / warn / error) §3.5 Level word in the line prefix ([WARN], [ERROR]) — never colour-only.
Deployment status (Succeeded / Failed / Rolled back) §4.10 Text label in cell; green/red border plus glyph.
Security severity (Critical / High / Med / Low) §4.10 Text label + glyph (double-up-arrow / up-arrow / dash / down-arrow).
MR check status (Pending / Passed / Failed) §4.8 Glyph + text; red/green on row stripe must not be the only carrier.
Board card priority hue stripe §4.9 Priority text in the card body — never infer priority from stripe colour alone.
Environment state (Active / Stopped / Locked) §4.10 Text label + glyph; the green pulse on Active must not be the only signal.
<progress> on milestones (§4.9) §4.9 Textual fraction ("8 of 12 issues closed") as a sibling of the bar.
Admin status pill (Active / Suspended / Deleted) §4.14 Text label + a glyph or a distinct outline.
/status service health (Operational / Degraded / Down) §4.2 Text + glyph (✓ / ⚠ / ✗); also expose in accessible name.

5.3 Reporting

For every failure, capture:

  1. Page URL.
  2. Filter mode that surfaced the failure.
  3. Screenshot with the filter active.
  4. The element's DOM snapshot (right-click → Copy element) for the ticket.

File a sub-ticket with a clear title (e.g. "PriorityBadge fails deuteranopia — arrow-only differentiator").


6. Keyboard-only walkthrough (baseline for all three SR matrices)

Before starting any screen-reader script above, do a dry keyboard pass of the seven target pages (§1.1–§1.7) with the SR off. This catches keyboard traps that would otherwise be blamed on the SR.

6.1 Procedure per page

  1. Load the page. Click once in the URL bar and then Tab back to the document.
  2. Tab through every interactive element in order.
    • Pass: a visible focus ring (from --vetrix-focus-ring, §1.3) is present on every stop; the tab order matches the visual order.
    • Fail: a stop has no visible ring (violates WCAG 2.4.7); tab order jumps disorientingly (e.g. from a sidebar link to a deeply nested button on the other side of the viewport).
  3. At every stop, confirm you can activate with the expected key:
    • Buttons: Enter and Space.
    • Links: Enter only (per spec; Space must not also activate, or screen magnifier users get unexpected navigation).
    • Toggles / switches: Space.
    • Combobox: Down Arrow opens, Enter selects, Escape closes.
    • Tabs: Left/Right/Home/End navigates, Tab moves out of the tablist.
  4. Shift+Tab back through. Order must reverse exactly.
  5. Escape from every modal, dropdown, combobox. Focus must return to the originating trigger — not the body, not the top of the page (§2.6).

6.2 Specific trap checks

  • /boards/[id] — after moving a card with the menu (§1.5), Esc must not move focus away from the card.
  • /pulls/[number] — opening an inline comment thread must trap Tab inside the composer until submit/cancel; Esc cancels.
  • /pipelines/[id] — pressing Tab while the log is scrolling must be possible (the autoscroll must not race the focus).
  • /admin/users — pagination Next button must remain focused after the table re-renders; if focus falls back to <body>, that is a §1.1 focus- management fail (§2.10 must also announce page change).

7. Reporting and ticket hygiene

A failure in any of §1–§6 is a defect. File with:

  • Title: {page} {SR/browser} {brief symptom} — e.g. /pulls/[n] VoiceOver Safari — review radio group missing fieldset legend.
  • Body: the exact step from this document that failed (quote the step number and the expected utterance), the actual utterance captured from the SR's speech viewer, screenshots at 400% / under the relevant vision filter if applicable.
  • Severity: use plans/accessibility.md §0 legend (🔴 Blocker / 🟠 High / 🟡 Medium / 🟢 Polish).
  • AC reference: cite the plans/accessibility.md section whose requirement the failure violates (e.g. §2.9, §4.9).

Do not file a ticket per SR — if NVDA, VO, and Orca all fail the same way, one ticket with a matrix ("NVDA+FF fail, VO+Safari fail, Orca+FF fail") is sufficient.


8. Pre-release sign-off checklist

Per release, the a11y gatekeeper runs:

  • §1 NVDA + Firefox: all seven pages green.
  • §2 VoiceOver + Safari: all seven pages green (including §2.2 reduced motion + dark).
  • §2.3 Voice Control spot-check: five actions green.
  • §3 Orca + Firefox: all seven pages green.
  • §4 400% zoom: all seven pages reflow without horizontal scroll.
  • §4.2 200% text-only zoom: no truncation on any page.
  • §5 Chrome DevTools vision filters: every status surface readable under all seven modes.
  • §6 Keyboard-only pass: no trap, visible focus on every stop.

A red item blocks the release until fixed or a documented waiver is signed by the a11y gatekeeper. The waiver names the section, the regression, and the ticket tracking the fix.