[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M70FWH3Z2GH9MKVSZ2FV37B7":3,"skill-same:01M70FWH3Z2GH9MKVSZ2FV37B7":71},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":42,"linkOnly":43,"markdownContent":44,"markdownBody":45,"frontMatter":46,"refName":47,"commitSha":48,"skillDirectory":49,"forksCount":50,"repositoryHtmlUrl":51,"repositoryHomepageUrl":52,"ownerHtmlUrl":53,"ownerBlogUrl":54,"ownerDescription":55,"updatedAt":56,"install":57,"resources":70},"01M70FWH3Z2GH9MKVSZ2FV37B7","screen-reader-testing",null,"Test web applications with screen readers including VoiceOver, NVDA, and JAWS. Use when validating screen reader compatibility, debugging accessibility issues, or ensuring assistive technology support.","shengdabai\u002FTony-Claude-Code-Skills","shengdabai","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F116417419?v=4","01M70F6D2WY5PQV5A92BKYJQEW","Tony Sheng","01M70F6D3GAAAYZ5H4CC1G8VXM",3,"MIT","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002F_collections\u002Fplugins\u002Faccessibility-compliance\u002Fskills\u002Fscreen-reader-testing\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41],"testing","screen","reader","screen reader testing","accessibility","web","test","with","use","technology","applications","compatibility","test web","support","voiceover","when","debugging","issues","2026-11-10 10:12:30",false,"---\nname: screen-reader-testing\ndescription: Test web applications with screen readers including VoiceOver, NVDA, and JAWS. Use when validating screen reader compatibility, debugging accessibility issues, or ensuring assistive technology support.\n---\n\n# 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\u002FiOS | Safari | ~15% |\n| **NVDA** | Windows | Firefox\u002FChrome | ~31% |\n| **JAWS** | Windows | Chrome\u002FIE | ~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\u002FVirtual** | Read content | Default reading |\n| **Focus\u002FForms** | 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\u002FRight Arrow   Change rotor category\nUp\u002FDown 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- [ ] Page title announced\n- [ ] Main landmark found\n- [ ] Skip link works\n\n### Navigation\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- [ ] Link purpose clear\n- [ ] Button actions described\n- [ ] New window\u002Ftab announced\n\n### Forms\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- [ ] Alerts announced immediately\n- [ ] Loading states communicated\n- [ ] Content updates announced\n- [ ] Modals trap focus correctly\n\n### Tables\n- [ ] Headers associated with cells\n- [ ] Table navigation works\n- [ ] Complex tables have captions\n```\n\n### Common Issues & Fixes\n\n```html\n\u003C!-- Issue: Button not announcing purpose -->\n\u003Cbutton>\u003Csvg>...\u003C\u002Fsvg>\u003C\u002Fbutton>\n\n\u003C!-- Fix -->\n\u003Cbutton aria-label=\"Close dialog\">\u003Csvg aria-hidden=\"true\">...\u003C\u002Fsvg>\u003C\u002Fbutton>\n\n\u003C!-- Issue: Dynamic content not announced -->\n\u003Cdiv id=\"results\">New results loaded\u003C\u002Fdiv>\n\n\u003C!-- Fix -->\n\u003Cdiv id=\"results\" role=\"status\" aria-live=\"polite\">New results loaded\u003C\u002Fdiv>\n\n\u003C!-- Issue: Form error not read -->\n\u003Cinput type=\"email\">\n\u003Cspan class=\"error\">Invalid email\u003C\u002Fspan>\n\n\u003C!-- Fix -->\n\u003Cinput type=\"email\" aria-invalid=\"true\" aria-describedby=\"email-error\">\n\u003Cspan id=\"email-error\" role=\"alert\">Invalid email\u003C\u002Fspan>\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\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\n1. Press D repeatedly\n2. Check: All main areas reachable?\n3. Check: Landmarks properly labeled?\n\n### Heading Navigation\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\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\n1. Tab through all interactive elements\n2. Check: Each announces role and state\n3. Activate buttons with Enter\u002FSpace\n4. Check: Result announced?\n\n### Dynamic Content\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\u003C!-- Accessible modal structure -->\n\u003Cdiv role=\"dialog\"\n     aria-modal=\"true\"\n     aria-labelledby=\"dialog-title\"\n     aria-describedby=\"dialog-desc\">\n  \u003Ch2 id=\"dialog-title\">Confirm Delete\u003C\u002Fh2>\n  \u003Cp id=\"dialog-desc\">This action cannot be undone.\u003C\u002Fp>\n  \u003Cbutton>Cancel\u003C\u002Fbutton>\n  \u003Cbutton>Delete\u003C\u002Fbutton>\n\u003C\u002Fdiv>\n```\n\n```javascript\n\u002F\u002F Focus management\nfunction openModal(modal) {\n  \u002F\u002F Store last focused element\n  lastFocus = document.activeElement;\n\n  \u002F\u002F Move focus to modal\n  modal.querySelector('h2').focus();\n\n  \u002F\u002F Trap focus\n  modal.addEventListener('keydown', trapFocus);\n}\n\nfunction closeModal(modal) {\n  \u002F\u002F 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\u003C!-- Status messages (polite) -->\n\u003Cdiv role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">\n  \u003C!-- Content updates will be announced after current speech -->\n\u003C\u002Fdiv>\n\n\u003C!-- Alerts (assertive) -->\n\u003Cdiv role=\"alert\" aria-live=\"assertive\">\n  \u003C!-- Content updates interrupt current speech -->\n\u003C\u002Fdiv>\n\n\u003C!-- Progress updates -->\n\u003Cdiv role=\"progressbar\"\n     aria-valuenow=\"75\"\n     aria-valuemin=\"0\"\n     aria-valuemax=\"100\"\n     aria-label=\"Upload progress\">\n\u003C\u002Fdiv>\n\n\u003C!-- Log (additions only) -->\n\u003Cdiv role=\"log\" aria-live=\"polite\" aria-relevant=\"additions\">\n  \u003C!-- New messages announced, removals not -->\n\u003C\u002Fdiv>\n```\n\n### 3. Tab Interface\n\n```html\n\u003Cdiv role=\"tablist\" aria-label=\"Product information\">\n  \u003Cbutton role=\"tab\"\n          id=\"tab-1\"\n          aria-selected=\"true\"\n          aria-controls=\"panel-1\">\n    Description\n  \u003C\u002Fbutton>\n  \u003Cbutton role=\"tab\"\n          id=\"tab-2\"\n          aria-selected=\"false\"\n          aria-controls=\"panel-2\"\n          tabindex=\"-1\">\n    Reviews\n  \u003C\u002Fbutton>\n\u003C\u002Fdiv>\n\n\u003Cdiv role=\"tabpanel\"\n     id=\"panel-1\"\n     aria-labelledby=\"tab-1\">\n  Product description content...\n\u003C\u002Fdiv>\n\n\u003Cdiv role=\"tabpanel\"\n     id=\"panel-2\"\n     aria-labelledby=\"tab-2\"\n     hidden>\n  Reviews content...\n\u003C\u002Fdiv>\n```\n\n```javascript\n\u002F\u002F 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\u002F\u002F 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: 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- **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- **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\n\n## Resources\n\n- [VoiceOver User Guide](https:\u002F\u002Fsupport.apple.com\u002Fguide\u002Fvoiceover\u002Fwelcome\u002Fmac)\n- [NVDA User Guide](https:\u002F\u002Fwww.nvaccess.org\u002Ffiles\u002Fnvda\u002Fdocumentation\u002FuserGuide.html)\n- [JAWS Documentation](https:\u002F\u002Fsupport.freedomscientific.com\u002FProducts\u002FBlindness\u002FJAWS)\n- [WebAIM Screen Reader Survey](https:\u002F\u002Fwebaim.org\u002Fprojects\u002Fscreenreadersurvey\u002F)\n","# 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\u002FiOS | Safari | ~15% |\n| **NVDA** | Windows | Firefox\u002FChrome | ~31% |\n| **JAWS** | Windows | Chrome\u002FIE | ~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\u002FVirtual** | Read content | Default reading |\n| **Focus\u002FForms** | 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\u002FRight Arrow   Change rotor category\nUp\u002FDown 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- [ ] Page title announced\n- [ ] Main landmark found\n- [ ] Skip link works\n\n### Navigation\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- [ ] Link purpose clear\n- [ ] Button actions described\n- [ ] New window\u002Ftab announced\n\n### Forms\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- [ ] Alerts announced immediately\n- [ ] Loading states communicated\n- [ ] Content updates announced\n- [ ] Modals trap focus correctly\n\n### Tables\n- [ ] Headers associated with cells\n- [ ] Table navigation works\n- [ ] Complex tables have captions\n```\n\n### Common Issues & Fixes\n\n```html\n\u003C!-- Issue: Button not announcing purpose -->\n\u003Cbutton>\u003Csvg>...\u003C\u002Fsvg>\u003C\u002Fbutton>\n\n\u003C!-- Fix -->\n\u003Cbutton aria-label=\"Close dialog\">\u003Csvg aria-hidden=\"true\">...\u003C\u002Fsvg>\u003C\u002Fbutton>\n\n\u003C!-- Issue: Dynamic content not announced -->\n\u003Cdiv id=\"results\">New results loaded\u003C\u002Fdiv>\n\n\u003C!-- Fix -->\n\u003Cdiv id=\"results\" role=\"status\" aria-live=\"polite\">New results loaded\u003C\u002Fdiv>\n\n\u003C!-- Issue: Form error not read -->\n\u003Cinput type=\"email\">\n\u003Cspan class=\"error\">Invalid email\u003C\u002Fspan>\n\n\u003C!-- Fix -->\n\u003Cinput type=\"email\" aria-invalid=\"true\" aria-describedby=\"email-error\">\n\u003Cspan id=\"email-error\" role=\"alert\">Invalid email\u003C\u002Fspan>\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\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\n1. Press D repeatedly\n2. Check: All main areas reachable?\n3. Check: Landmarks properly labeled?\n\n### Heading Navigation\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\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\n1. Tab through all interactive elements\n2. Check: Each announces role and state\n3. Activate buttons with Enter\u002FSpace\n4. Check: Result announced?\n\n### Dynamic Content\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\u003C!-- Accessible modal structure -->\n\u003Cdiv role=\"dialog\"\n     aria-modal=\"true\"\n     aria-labelledby=\"dialog-title\"\n     aria-describedby=\"dialog-desc\">\n  \u003Ch2 id=\"dialog-title\">Confirm Delete\u003C\u002Fh2>\n  \u003Cp id=\"dialog-desc\">This action cannot be undone.\u003C\u002Fp>\n  \u003Cbutton>Cancel\u003C\u002Fbutton>\n  \u003Cbutton>Delete\u003C\u002Fbutton>\n\u003C\u002Fdiv>\n```\n\n```javascript\n\u002F\u002F Focus management\nfunction openModal(modal) {\n  \u002F\u002F Store last focused element\n  lastFocus = document.activeElement;\n\n  \u002F\u002F Move focus to modal\n  modal.querySelector('h2').focus();\n\n  \u002F\u002F Trap focus\n  modal.addEventListener('keydown', trapFocus);\n}\n\nfunction closeModal(modal) {\n  \u002F\u002F 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\u003C!-- Status messages (polite) -->\n\u003Cdiv role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">\n  \u003C!-- Content updates will be announced after current speech -->\n\u003C\u002Fdiv>\n\n\u003C!-- Alerts (assertive) -->\n\u003Cdiv role=\"alert\" aria-live=\"assertive\">\n  \u003C!-- Content updates interrupt current speech -->\n\u003C\u002Fdiv>\n\n\u003C!-- Progress updates -->\n\u003Cdiv role=\"progressbar\"\n     aria-valuenow=\"75\"\n     aria-valuemin=\"0\"\n     aria-valuemax=\"100\"\n     aria-label=\"Upload progress\">\n\u003C\u002Fdiv>\n\n\u003C!-- Log (additions only) -->\n\u003Cdiv role=\"log\" aria-live=\"polite\" aria-relevant=\"additions\">\n  \u003C!-- New messages announced, removals not -->\n\u003C\u002Fdiv>\n```\n\n### 3. Tab Interface\n\n```html\n\u003Cdiv role=\"tablist\" aria-label=\"Product information\">\n  \u003Cbutton role=\"tab\"\n          id=\"tab-1\"\n          aria-selected=\"true\"\n          aria-controls=\"panel-1\">\n    Description\n  \u003C\u002Fbutton>\n  \u003Cbutton role=\"tab\"\n          id=\"tab-2\"\n          aria-selected=\"false\"\n          aria-controls=\"panel-2\"\n          tabindex=\"-1\">\n    Reviews\n  \u003C\u002Fbutton>\n\u003C\u002Fdiv>\n\n\u003Cdiv role=\"tabpanel\"\n     id=\"panel-1\"\n     aria-labelledby=\"tab-1\">\n  Product description content...\n\u003C\u002Fdiv>\n\n\u003Cdiv role=\"tabpanel\"\n     id=\"panel-2\"\n     aria-labelledby=\"tab-2\"\n     hidden>\n  Reviews content...\n\u003C\u002Fdiv>\n```\n\n```javascript\n\u002F\u002F 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\u002F\u002F 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: 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- **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- **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\n\n## Resources\n\n- [VoiceOver User Guide](https:\u002F\u002Fsupport.apple.com\u002Fguide\u002Fvoiceover\u002Fwelcome\u002Fmac)\n- [NVDA User Guide](https:\u002F\u002Fwww.nvaccess.org\u002Ffiles\u002Fnvda\u002Fdocumentation\u002FuserGuide.html)\n- [JAWS Documentation](https:\u002F\u002Fsupport.freedomscientific.com\u002FProducts\u002FBlindness\u002FJAWS)\n- [WebAIM Screen Reader Survey](https:\u002F\u002Fwebaim.org\u002Fprojects\u002Fscreenreadersurvey\u002F)\n",{"name":5,"description":7},"main","7850740bd837adb32f0a109ab2af82482196c6f2","skills\u002F_collections\u002Fplugins\u002Faccessibility-compliance\u002Fskills\u002Fscreen-reader-testing",1,"https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills","https:\u002F\u002Fzturnsgo.com","https:\u002F\u002Fgithub.com\u002Fshengdabai","https:\u002F\u002Fzturnsgo.com\u002F","AI × education creator — producer, not consumer. Taught 6,000+ learners; shipping learning tools & daily essays with Claude Code, practicing @xiaolai's way 🥋","2026-11-01 21:28:23",{"source":8,"skillName":5,"commands":58,"prompt":68,"zipAvailable":69},[59,62,65],{"tool":60,"command":61},"npx","npx skills add shengdabai\u002FTony-Claude-Code-Skills --skill screen-reader-testing",{"tool":63,"command":64},"bunx","bunx skills add shengdabai\u002FTony-Claude-Code-Skills --skill screen-reader-testing",{"tool":66,"command":67},"pnpm","pnpm dlx skills add shengdabai\u002FTony-Claude-Code-Skills --skill screen-reader-testing","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002F_collections\u002Fplugins\u002Faccessibility-compliance\u002Fskills\u002Fscreen-reader-testing\u002FSKILL.md",true,[],[72,89,123,148,170,190],{"skillNo":73,"name":74,"nameZh":6,"description":75,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":76,"categories":77,"keywords":81,"publishedAt":42},"01M70P17DAD4C1C47NPH6A96EM","xiaolai-methodology","以李笑来(Li Xiaolai)的思考方式、方法论与表达风格,为用户做全方位 1-on-1 教练指导,覆盖创业、赚钱、投资、学习、自学、读书写作、用 AI、以及人生心态与自我管理。当用户提到 \"李笑来\"、\"笑来\"、\"笑来老师\"、\"扮演李笑来\"、\"找笑来聊聊\"、\"笑来怎么看\"、\"xiaolai\"、\"把时间当作朋友\"、\"通往财富自由之路\"、\"定投改变命运\"、\"韭菜的自我修养\"、\"自学是门手艺\"、\"让时间陪你慢慢变富\"、\"七年就是一辈子\"、\"元认知\"、\"活在未来\"、\"复利\u002F长期主义\"、\"践行\"、\"痴迷改进\"、\"个人商业模式\u002F出售时间\"、\"睡后收入\"、\"定投\"、\"财富自由\"、\"操作系统升级\"、\"最少必要知识\"、\"北极星提示词\"、\"用 AI 该怎么学\u002F思考\" 等任一触发词,或当用户带着关于创业\u002F赚钱\u002F投资\u002F学习\u002F职业\u002F重大人生决策的困惑、希望被人用笑来式的「逻辑 + 大白话 + 类比 + 重新定义 + 践行导向」来点醒和指导时,自动进入李笑来教练模式。","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fxiaolai-methodology\u002FSKILL.md",[78],{"categoryNo":79,"categoryName":80,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000018","写作与文案",[82,83,84,85,86,87,88],"methodology","xiaolai","xiaolai methodology","朋友","投资","writing","收入",{"skillNo":90,"name":91,"nameZh":6,"description":92,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":93,"categories":94,"keywords":103,"publishedAt":42},"01M70P13NB9YP7ACJR1S861CDS","zread","Produce and consume a wiki-style knowledge base for a code repository via the `zread` CLI and its on-disk output under `.\u002F.zread\u002Fwiki\u002F`. Use this skill whenever the user wants to understand, onboard onto, explore, summarize, map, or get an overview of an unfamiliar codebase; asks for architecture docs, a project wiki, a repo walkthrough, module\u002Fpackage explanations, or \"what does this repo do\"; wants to generate, regenerate, resume, browse, or serve code documentation locally; or mentions zread \u002F zread.ai directly. Also use it proactively before diving into a large unknown repo — if `.\u002F.zread\u002Fwiki\u002Fcurrent` exists, read the generated pages instead of crawling source file-by-file; if it doesn't, consider offering to run `zread generate`. The trigger is the intent (understand a codebase through generated docs), not the literal word \"zread\".","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fzread\u002FSKILL.md",[95,98],{"categoryNo":96,"categoryName":97,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":99,"categoryName":100,"taxonomyType":101,"taxonomyTypeDesc":102},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[91,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122],"knowledge","wiki","explore","walkthrough","project","get","knowledge base","run","generate","intent","cli","map","documentation","project wiki","disk","architecture","code","unknown","generated",{"skillNo":124,"name":125,"nameZh":6,"description":126,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":127,"categories":128,"keywords":133,"publishedAt":42},"01M70P10S4SWMZQ8BJAFBFDRW3","youtube","Launch the YouTube Creator Agent to auto-generate videos.\nUse when: user types \u002Fyoutube, wants to create YouTube videos,\ngenerate video content, or upload to YouTube.\nKeywords: youtube, video, 视频, 创作视频, 发布视频","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fyoutube\u002FSKILL.md",[129,130],{"categoryNo":96,"categoryName":97,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":131,"categoryName":132,"taxonomyType":101,"taxonomyTypeDesc":102},"01JBSEEDCAT000000000000036","内容创作者",[125,134,135,136,137,138,139,140,112,141,142,143,39,32,144,145,146,147],"user","content","视频","creator","generate video","videos","launch","video content","create","upload","auto","types","Video","创作",{"skillNo":149,"name":150,"nameZh":6,"description":151,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":152,"categories":153,"keywords":155,"publishedAt":42},"01M70P0YE95D8BACZCGV8BK90K","youtube-script-sync","Sync, update, or optimize Tony's GetNote knowledge base `Youtube视频逐字稿` by turning each parent recording or first-person idea note into an idempotent child note containing a directly recordable YouTube long-video or Shorts script. Use when the user says “更新\u002F同步\u002F优化 Youtube视频逐字稿知识库”, or says “更新\u002F同步\u002F优化该知识库” while this knowledge base is the active context.","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fyoutube-script-sync\u002FSKILL.md",[154],{"categoryNo":96,"categoryName":97,"taxonomyType":21,"taxonomyTypeDesc":22},[156,157,125,158,159,146,160,161,162,163,164,165,166,167,110,168,39,32,104,169],"sync","script","youtube script sync","recording","shorts","first","base","person","update","active","child","optimize","idempotent","long",{"skillNo":171,"name":172,"nameZh":6,"description":173,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":174,"categories":175,"keywords":180,"publishedAt":42},"01M70P0WMV5XT0EEC0N445PW07","writing-skills","How to write SKILL.md files that trigger reliably and teach effectively. Use when creating, improving, or reviewing Claude Code skills.","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fxiaolai\u002Fnlpm\u002Fskills\u002Fnlpm\u002Fwriting-skills\u002FSKILL.md",[176,177,178,179],{"categoryNo":79,"categoryName":80,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":96,"categoryName":97,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":99,"categoryName":100,"taxonomyType":101,"taxonomyTypeDesc":102},{"categoryNo":131,"categoryName":132,"taxonomyType":101,"taxonomyTypeDesc":102},[181,87,182,183,184,185,120,39,32,186,187,188,189],"writing skills","how","teach","write skill","write","claude code skills","files","how to","trigger",{"skillNo":191,"name":192,"nameZh":6,"description":193,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":194,"categories":195,"keywords":198,"publishedAt":42},"01M70P0SJFR4YVJQBGMHKGSKXT","writing-rules","How to write .claude\u002Frules\u002F files that Claude actually follows. Use when creating, improving, or reviewing project rules.","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fxiaolai\u002Fnlpm\u002Fskills\u002Fnlpm\u002Fwriting-rules\u002FSKILL.md",[196,197],{"categoryNo":79,"categoryName":80,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":131,"categoryName":132,"taxonomyType":101,"taxonomyTypeDesc":102},[199,87,200,108,201,187,39,182,185,32,188],"Rules","writing rules","project rules"]