# A11y audit — scanner-desktop-layout **Reviewer:** role-a11y-auditor **Convoy:** `scanner-desktop-layout` **Surface:** local UI diff vs `origin/main` (+ untracked workstation components) — audit group `audit-scanner-desktop-layout-local` **Date:** 2026-08-15 **Model:** cursor-grok-4.5-high (`model_tier=fast`) **Skill note:** `.cursor/skills/accessibility-audit/SKILL.md` was not present in-repo; audit follows `role-a11y-auditor.md`, prior report shape (`.convoys/scanner-mobile-checkout/audits/a11y-20260814.md`), convoy UX §4 A11y constraints, and WCAG 2.2 AA. ## Diff scope (UI) | Path | Status | | --- | --- | | `pages/scanner.js` | modified (workstation wiring) | | `components/scanner/ScannerCamera.js` | modified (`variant="workstation"`, Auto-detect badge) | | `components/scanner/ScannerResultPanel.js` | **new** | | `components/scanner/ScannerHistoryStrip.js` | **new** | | `components/scanner/ScannerTips.js` | **new** | | `test/components/ScannerHistoryStrip.test.js` | new (a11y-adjacent: tab `aria-label`s) | | `test/components/ScannerResultPanel.test.js` | new (condition name / foil switch) | | `test/components/ScannerTips.test.js` | new | | `test/components/ScannerCamera.test.js` | new | Non-UI (`lib/use-camera-scanner.js`, batch helper, queue hooks) excluded from findings except where they gate AT-visible status. Patterns referenced: `components/ui/Modal.js` (`useFocusTrap`, `role="dialog"`), `components/ui/Button.js` (ember `focus-visible` ring, visible label in name). ## Executive summary - Desktop workstation ships strong foundations: Tips via `` + trap, Auto-detect / Foil `role="switch"`, strip `tablist`/`tab`/`tabpanel` with badge counts in `aria-label`, condition `aria-label` includes card name, commit errors `role="alert"`, ember `focus-visible` rings, toast `role="status"`, mobile checkout `inert`/`trapActive` carry-forward. - **No severity ≥ 3 findings.** Several **sev-2** gaps vs promised UX §4 constraints: Camera fallback lacks `aria-describedby`; Auto-detect overlay badge has no `aria-live`; batch progress + Cancel live only inside the Queue `tabpanel` (often `hidden`); empty-inspector “Identifying…” is wired to disambiguation, not identify-in-flight; queue badge white-on-ember is ~4.44:1; `--color-*` semantic tokens appear undefined in `styles/`. - **Recommendation:** comment-only for merge gate (no sev ≥ 3); fix the §4 constraint fails (A1–A4) in this PR or an immediate a11y follow-up. **Counts:** 8 findings (sev ≥ 3: **0**, sev < 3: **8**). Constraint checklist: **8 pass / 3 partial / 3 fail**. ## Convoy constraint checklist (UX §4) | # | Constraint | Result | Notes | | --- | --- | --- | --- | | 1 | **1.4.3** Contrast text on glass | **Partial** | Body `--text-primary/secondary` on solid `--bg-*` ≥ 4.5:1 (sampled). Queue badge `#ffffff` on `--accent-ember` ≈ **4.44:1** (fails normal text). Glass composite not instrumented. | | 2 | **1.4.10** Reflow @ 768 / 200% | **Pass*** | `md:flex-row` → column below `md`; strip `overflow-x-auto` only (*static; no browser zoom run). | | 3 | **1.4.11** Focus not obscured (sticky) | **Pass** | Inspector rail is flex column sibling — not `position: sticky/fixed`. Batch banner sits in strip flow. | | 4 | **2.1.1** Keyboard all actions | **Partial** | Desk controls, tips, inspector, tabs, chips, Clear All, View All are `