# Screen Reader Verification Matrix

This document defines the screen reader pairing matrix for taga11y verification. Every public feature must be verified against at least one pairing from this matrix before merge, and at least one pairing from each row before release.

## Matrix

| Pairing | Platform | Browser | Role | Setup |
|---------|----------|---------|------|-------|
| NVDA + Firefox | Windows 10/11 | Firefox ESR | Primary baseline. Most widely used free SR; combobox pattern is well-supported and predictable. | Free download. Install NVDA, set Firefox as default browser. |
| JAWS + Chrome | Windows 10/11 | Chrome stable | Enterprise baseline. Most common in corporate environments; behavior diverges from NVDA on some ARIA patterns. | JAWS 2024+ (demo mode available for verification). Chrome stable. |
| VoiceOver + Safari (macOS) | macOS Sonoma+ | Safari 17+ | Apple desktop baseline. Native SR; behaves differently from VoiceOver iOS on combobox patterns. | Built into macOS. Enable via `Cmd+F5`. |
| VoiceOver + Safari (iOS) | iOS 17+ | Safari (iOS) | Apple mobile baseline. Touch + rotor gestures; combobox behavior differs from desktop VoiceOver. | Built into iOS devices. Enable via Settings → Accessibility → VoiceOver. |
| TalkBack + Chrome | Android 13+ | Chrome stable | Android baseline. Touch + linear/angular gestures; different interaction model from iOS. | Built into Android. Enable via Settings → Accessibility → TalkBack. |
| Narrator + Edge | Windows 11 | Edge stable | Microsoft baseline. Sanity check only; Narrator is less commonly used than NVDA/JAWS on Windows. | Built into Windows. Enable via `Win+Ctrl+Enter`. |

## Verification Scope per Pairing

Each pairing tests the following feature areas:

### Combobox Interaction
- [ ] Input receives focus and announces the label (via `<label for>`)
- [ ] Input focus with N selected tags announces the field name, then a polite-region announcement of the `a11y.tagsSummary` summary including all labels; with zero selected tags only the field name is announced (no summary message fires)
- [ ] `ArrowDown` opens the dropdown and announces the first suggestion
- [ ] Arrow key navigation highlights suggestions; `aria-activedescendant` changes are announced
- [ ] `Enter` selects the highlighted suggestion; announcement "Tag added: <label>"
- [ ] `Escape` closes the dropdown without clearing input
- [ ] `Tab` moves focus out of the component (no trapping)
- [ ] `Shift+Tab` moves focus into the component from the next element

### Dropdown Behavior
- [ ] Listbox is hidden when closed (not announced by SR)
- [ ] Dropdown opens and announces "N results available" or "No results"
- [ ] Loading state announces "Loading suggestions…"
- [ ] Suggestions are announced with their labels and `aria-selected` state

### Chip Region
- [ ] Chip region is announced as "Selected tags" list
- [ ] Each chip announces its label and remove button
- [ ] Remove button click announces "Tag removed: <label>"
- [ ] Focus returns to combobox input after chip removal

### Error Handling
- [ ] Error region (`role="alert"`) is announced assertively on rejection
- [ ] Duplicate tag: "Already added: <label>"
- [ ] Whitelist violation: "Not in the list"
- [ ] Max tags: "Maximum tags reached"
- [ ] Error region auto-dismisses after 2 seconds and is no longer announced

### Form Integration
- [ ] Hidden input is not announced (type="hidden")
- [ ] Original input retains its `id` attribute
- [ ] Page label association works via `<label for>`

## Known Divergences

| Divergence | NVDA | JAWS | VO macOS | VO iOS | TalkBack | Narrator |
|-----------|------|------|----------|--------|----------|----------|
| `aria-activedescendant` announcement on navigation | Announces option label on each arrow | Announces option label; may repeat combobox label | Announces option label; rotor may interfere | Announces option label; rotor navigation differs | Announces option label; linear navigation differs | Announces option label |
| `role="alert"` on error region | Announced immediately on content change | Announced immediately; may interrupt polite queue | Announced immediately | Announced immediately; may require rotor | Announced immediately | Announced immediately |
| Empty dropdown announcement | Announces "No results" | May not announce empty state consistently | Announces "No results" | Announces "No results" | Announces "No results" | May not announce empty state |
| Chip remove button activation | Announces "Remove <label>, button" | Announces "Remove <label>, button" | Announces "Remove <label>" | Announces "Remove <label>" via rotor | Announces "Remove <label>, button" | Announces "Remove <label>, button" |
| `aria-live="polite"` queueing | Queues announcements; may delay | Queues; may merge rapid announcements | Queues; may delay on slow devices | Queues; aggressive merging | Queues; may delay | Queues; may delay |
| `prefers-reduced-motion` respect | Respects CSS; no spinner animation | Respects CSS | Respects CSS | Respects CSS (motion already limited) | Respects CSS | Respects CSS |

## Verification Procedure

1. Open the relevant example page in the target browser.
2. Enable the screen reader.
3. Navigate to the tagging input using Tab.
4. Verify each item in the Verification Scope above.
5. Record results in the table below.

## Results Log

| Feature | NVDA+Ff | JAWS+Ch | VO+Sa(Mac) | VO+Sa(iOS) | TB+Ch(An) | Narr+Ed |
|---------|---------|---------|------------|------------|-----------|---------|
| Combobox focus | | | | | | |
| Combobox focus with tags | | | | | | |
| ArrowDown opens | | | | | | |
| Arrow nav | | | | | | |
| Enter selects | | | | | | |
| Escape closes | | | | | | |
| Tab order | | | | | | |
| Dropdown announce | | | | | | |
| Loading announce | | | | | | |
| Chip announce | | | | | | |
| Remove announce | | | | | | |
| Error announce | | | | | | |
| Empty announce | | | | | | |
