{"id":"screen-reader-testing","name":"screen-reader-testing","summary":"VoiceOver、NVDA、JAWSなどのスクリーンリーダーでウェブアプリケーションをテストしてください。","body":"# Screen Reader Testing\n\nPractical guide to testing web applications with screen readers for comprehensive accessibility validation.\n\n## When to Use This Skill\n\n- Validating screen reader compatibility\n- Testing ARIA implementations\n- Debugging assistive technology issues\n- Verifying form accessibility\n- Testing dynamic content announcements\n- Ensuring navigation accessibility\n\n## Core Concepts\n\n### 1. Major Screen Readers\n\n| Screen Reader | Platform  | Browser        | Usage |\n| ------------- | --------- | -------------- | ----- |\n| **VoiceOver** | macOS/iOS | Safari         | ~15%  |\n| **NVDA**      | Windows   | Firefox/Chrome | ~31%  |\n| **JAWS**      | Windows   | Chrome/IE      | ~40%  |\n| **TalkBack**  | Android   | Chrome         | ~10%  |\n| **Narrator**  | Windows   | Edge           | ~4%   |\n\n### 2. Testing Priority\n\n```\nMinimum Coverage:\n1. NVDA + Firefox (Windows)\n2. VoiceOver + Safari (macOS)\n3. VoiceOver + Safari (iOS)\n\nComprehensive Coverage:\n+ JAWS + Chrome (Windows)\n+ TalkBack + Chrome (Android)\n+ Narrator + Edge (Windows)\n```\n\n### 3. Screen Reader Modes\n\n| Mode               | Purpose                | When Used         |\n| ------------------ | ---------------------- | ----------------- |\n| **Browse/Virtual** | Read content           | Default reading   |\n| **Focus/Forms**    | Interact with controls | Filling forms     |\n| **Application**    | Custom widgets         | ARIA applications |\n\n## VoiceOver (macOS)\n\n### Setup\n\n```\nEnable: System Preferences → Accessibility → VoiceOver\nToggle: Cmd + F5\nQuick Toggle: Triple-press Touch ID\n```\n\n### Essential Commands\n\n```\nNavigation:\nVO = Ctrl + Option (VoiceOver modifier)\n\nVO + Right Arrow   Next element\nVO + Left Arrow    Previous element\nVO + Shift + Down  Enter group\nVO + Shift + Up    Exit group\n\nReading:\nVO + A             Read all from cursor\nCtrl               Stop speaking\nVO + B             Read current paragraph\n\nInteraction:\nVO + Space         Activate element\nVO + Shift + M     Open menu\nTab                Next focusable element\nShift + Tab        Previous focusable element\n\nRotor (VO + U):\nNavigate by: Headings, Links, Forms, Landmarks\nLeft/Right Arrow   Change rotor category\nUp/Down Arrow      Navigate within category\nEnter              Go to item\n\nWeb Specific:\nVO + Cmd + H       Next heading\nVO + Cmd + J       Next form control\nVO + Cmd + L       Next link\nVO + Cmd + T       Next table\n```\n\n### Testing Checklist\n\n```markdown\n## VoiceOver Testing Checklist\n\n### Page Load\n\n- [ ] Page title announced\n- [ ] Main landmark found\n- [ ] Skip link works\n\n### Navigation\n\n- [ ] All headings discoverable via rotor\n- [ ] Heading levels logical (H1 → H2 → H3)\n- [ ] Landmarks properly labeled\n- [ ] Skip links functional\n\n### Links & Buttons\n\n- [ ] Link purpose clear\n- [ ] Button actions described\n- [ ] New window/tab announced\n\n### Forms\n\n- [ ] All labels read with inputs\n- [ ] Required fields announced\n- [ ] Error messages read\n- [ ] Instructions available\n- [ ] Focus moves to errors\n\n### Dynamic Content\n\n- [ ] Alerts announced immediately\n- [ ] Loading states communicated\n- [ ] Content updates announced\n- [ ] Modals trap focus correctly\n\n### Tables\n\n- [ ] Headers associated with cells\n- [ ] Table navigation works\n- [ ] Complex tables have captions\n```\n\n### Common Issues & Fixes\n\n```html\n<!-- Issue: Button not announcing purpose -->\n<button><svg>...</svg></button>\n\n<!-- Fix -->\n<button aria-label=\"Close dialog\"><svg aria-hidden=\"true\">...</svg></button>\n\n<!-- Issue: Dynamic content not announced -->\n<div id=\"results\">New results loaded</div>\n\n<!-- Fix -->\n<div id=\"results\" role=\"status\" aria-live=\"polite\">New results loaded</div>\n\n<!-- Issue: Form error not read -->\n<input type=\"email\" />\n<span class=\"error\">Invalid email</span>\n\n<!-- Fix -->\n<input type=\"email\" aria-invalid=\"true\" aria-describedby=\"email-error\" />\n<span id=\"email-error\" role=\"alert\">Invalid email</span>\n```\n\n## NVDA (Windows)\n\n### Setup\n\n```\nDownload: nvaccess.org\nStart: Ctrl + Alt + N\nStop: Insert + Q\n```\n\n### Essential Commands\n\n```\nNavigation:\nInsert = NVDA modifier\n\nDown Arrow         Next line\nUp Arrow           Previous line\nTab                Next focusable\nShift + Tab        Previous focusable\n\nReading:\nNVDA + Down Arrow  Say all\nCtrl               Stop speech\nNVDA + Up Arrow    Current line\n\nHeadings:\nH                  Next heading\nShift + H          Previous heading\n1-6                Heading level 1-6\n\nForms:\nF                  Next form field\nB                  Next button\nE                  Next edit field\nX                  Next checkbox\nC                  Next combo box\n\nLinks:\nK                  Next link\nU                  Next unvisited link\nV                  Next visited link\n\nLandmarks:\nD                  Next landmark\nShift + D          Previous landmark\n\nTables:\nT                  Next table\nCtrl + Alt + Arrows Navigate cells\n\nElements List (NVDA + F7):\nShows all links, headings, form fields, landmarks\n```\n\n### Browse vs Focus Mode\n\n```\nNVDA automatically switches modes:\n- Browse Mode: Arrow keys navigate content\n- Focus Mode: Arrow keys control interactive elements\n\nManual switch: NVDA + Space\n\nWatch for:\n- \"Browse mode\" announcement when navigating\n- \"Focus mode\" when entering form fields\n- Application role forces forms mode\n```\n\n### Testing Script\n\n```markdown\n## NVDA Test Script\n\n### Initial Load\n\n1. Navigate to page\n2. Let page finish loading\n3. Press Insert + Down to read all\n4. Note: Page title, main content identified?\n\n### Landmark Navigation\n\n1. Press D repeatedly\n2. Check: All main areas reachable?\n3. Check: Landmarks properly labeled?\n\n### Heading Navigation\n\n1. Press Insert + F7 → Headings\n2. Check: Logical heading structure?\n3. Press H to navigate headings\n4. Check: All sections discoverable?\n\n### Form Testing\n\n1. Press F to find first form field\n2. Check: Label read?\n3. Fill in invalid data\n4. Submit form\n5. Check: Errors announced?\n6. Check: Focus moved to error?\n\n### Interactive Elements\n\n1. Tab through all interactive elements\n2. Check: Each announces role and state\n3. Activate buttons with Enter/Space\n4. Check: Result announced?\n\n### Dynamic Content\n\n1. Trigger content update\n2. Check: Change announced?\n3. Open modal\n4. Check: Focus trapped?\n5. Close modal\n6. Check: Focus returns?\n```\n\n## JAWS (Windows)\n\n### Essential Commands\n\n```\nStart: Desktop shortcut or Ctrl + Alt + J\nVirtual Cursor: Auto-enabled in browsers\n\nNavigation:\nArrow keys         Navigate content\nTab                Next focusable\nInsert + Down      Read all\nCtrl               Stop speech\n\nQuick Keys:\nH                  Next heading\nT                  Next table\nF                  Next form field\nB                  Next button\nG                  Next graphic\nL                  Next list\n;                  Next landmark\n\nForms Mode:\nEnter              Enter forms mode\nNumpad +           Exit forms mode\nF5                 List form fields\n\nLists:\nInsert + F7        Link list\nInsert + F6        Heading list\nInsert + F5        Form field list\n\nTables:\nCtrl + Alt + Arrows Table navigation\n```\n\n## TalkBack (Android)\n\n### Setup\n\n```\nEnable: Settings → Accessibility → TalkBack\nToggle: Hold both volume buttons 3 seconds\n```\n\n### Gestures\n\n```\nExplore: Drag finger across screen\nNext: Swipe right\nPrevious: Swipe left\nActivate: Double tap\nScroll: Two finger swipe\n\nReading Controls (swipe up then right):\n- Headings\n- Links\n- Controls\n- Characters\n- Words\n- Lines\n- Paragraphs\n```\n\n## Common Test Scenarios\n\n### 1. Modal Dialog\n\n```html\n<!-- Accessible modal structure -->\n<div\n  role=\"dialog\"\n  aria-modal=\"true\"\n  aria-labelledby=\"dialog-title\"\n  aria-describedby=\"dialog-desc\"\n>\n  <h2 id=\"dialog-title\">Confirm Delete</h2>\n  <p id=\"dialog-desc\">This action cannot be undone.</p>\n  <button>Cancel</button>\n  <button>Delete</button>\n</div>\n```\n\n```javascript\n// Focus management\nfunction openModal(modal) {\n  // Store last focused element\n  lastFocus = document.activeElement;\n\n  // Move focus to modal\n  modal.querySelector(\"h2\").focus();\n\n  // Trap focus\n  modal.addEventListener(\"keydown\", trapFocus);\n}\n\nfunction closeModal(modal) {\n  // Return focus\n  lastFocus.focus();\n}\n\nfunction trapFocus(e) {\n  if (e.key === \"Tab\") {\n    const focusable = modal.querySelectorAll(\n      'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n    );\n    const first = focusable[0];\n    const last = focusable[focusable.length - 1];\n\n    if (e.shiftKey && document.activeElement === first) {\n      last.focus();\n      e.preventDefault();\n    } else if (!e.shiftKey && document.activeElement === last) {\n      first.focus();\n      e.preventDefault();\n    }\n  }\n\n  if (e.key === \"Escape\") {\n    closeModal(modal);\n  }\n}\n```\n\n### 2. Live Regions\n\n```html\n<!-- Status messages (polite) -->\n<div role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">\n  <!-- Content updates will be announced after current speech -->\n</div>\n\n<!-- Alerts (assertive) -->\n<div role=\"alert\" aria-live=\"assertive\">\n  <!-- Content updates interrupt current speech -->\n</div>\n\n<!-- Progress updates -->\n<div\n  role=\"progressbar\"\n  aria-valuenow=\"75\"\n  aria-valuemin=\"0\"\n  aria-valuemax=\"100\"\n  aria-label=\"Upload progress\"\n></div>\n\n<!-- Log (additions only) -->\n<div role=\"log\" aria-live=\"polite\" aria-relevant=\"additions\">\n  <!-- New messages announced, removals not -->\n</div>\n```\n\n### 3. Tab Interface\n\n```html\n<div role=\"tablist\" aria-label=\"Product information\">\n  <button role=\"tab\" id=\"tab-1\" aria-selected=\"true\" aria-controls=\"panel-1\">\n    Description\n  </button>\n  <button\n    role=\"tab\"\n    id=\"tab-2\"\n    aria-selected=\"false\"\n    aria-controls=\"panel-2\"\n    tabindex=\"-1\"\n  >\n    Reviews\n  </button>\n</div>\n\n<div role=\"tabpanel\" id=\"panel-1\" aria-labelledby=\"tab-1\">\n  Product description content...\n</div>\n\n<div role=\"tabpanel\" id=\"panel-2\" aria-labelledby=\"tab-2\" hidden>\n  Reviews content...\n</div>\n```\n\n```javascript\n// Tab keyboard navigation\ntablist.addEventListener(\"keydown\", (e) => {\n  const tabs = [...tablist.querySelectorAll('[role=\"tab\"]')];\n  const index = tabs.indexOf(document.activeElement);\n\n  let newIndex;\n  switch (e.key) {\n    case \"ArrowRight\":\n      newIndex = (index + 1) % tabs.length;\n      break;\n    case \"ArrowLeft\":\n      newIndex = (index - 1 + tabs.length) % tabs.length;\n      break;\n    case \"Home\":\n      newIndex = 0;\n      break;\n    case \"End\":\n      newIndex = tabs.length - 1;\n      break;\n    default:\n      return;\n  }\n\n  tabs[newIndex].focus();\n  activateTab(tabs[newIndex]);\n  e.preventDefault();\n});\n```\n\n## Debugging Tips\n\n```javascript\n// Log what screen reader sees\nfunction logAccessibleName(element) {\n  const computed = window.getComputedStyle(element);\n  console.log({\n    role: element.getAttribute(\"role\") || element.tagName,\n    name:\n      element.getAttribute(\"aria-label\") ||\n      element.getAttribute(\"aria-labelledby\") ||\n      element.textContent,\n    state: {\n      expanded: element.getAttribute(\"aria-expanded\"),\n      selected: element.getAttribute(\"aria-selected\"),\n      checked: element.getAttribute(\"aria-checked\"),\n      disabled: element.disabled,\n    },\n    visible: computed.display !== \"none\" && computed.visibility !== \"hidden\",\n  });\n}\n```\n\n## Best Practices\n\n### Do's\n\n- **Test with actual screen readers** - Not just simulators\n- **Use semantic HTML first** - ARIA is supplemental\n- **Test in browse and focus modes** - Different experiences\n- **Verify focus management** - Especially for SPAs\n- **Test keyboard only first** - Foundation for SR testing\n\n### Don'ts\n\n- **Don't assume one SR is enough** - Test multiple\n- **Don't ignore mobile** - Growing user base\n- **Don't test only happy path** - Test error states\n- **Don't skip dynamic content** - Most common issues\n- **Don't rely on visual testing** - Different experience","author":"@wshobson","ownerProfile":null,"authorContacts":null,"sourceUrl":"https://github.com/wshobson/agents/tree/main/plugins/accessibility-compliance/skills/screen-reader-testing","license":"MIT","category":"testing","lang":"en","tokens":2857,"stars":0,"calls30d":2,"claimed":false,"visibility":"public","origin":"crawler","version":"0.1.0","createdAt":"2026-08-22","updatedAt":"2026-08-22","files":[],"requires":{"mcp":[],"tools":[]},"safety":{"flags":[],"scannedAt":"2026-08-22","hasScripts":false,"networkEndpoints":[]}}