Screen Reader Testing

作者 wshobson46891e7e60da無授權條款收錄於 2026年10月8日更新於 2026年10月8日

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.

AI 產生的概覽

指導使用 VoiceOver、NVDA、JAWS 和 TalkBack 等螢幕閱讀器測試網頁應用程式。

功能
提供使用螢幕閱讀器驗證網頁無障礙性的實用參考,涵蓋主要螢幕閱讀器與瀏覽器的組合、測試優先順序以及鍵盤指令。內容包含導覽、表單、動態內容、表格和模態視窗的檢查清單與測試指令碼,並提供常見元件的 ARIA 與焦點管理程式碼範例。此外也列出常見無障礙問題及其修正方式和除錯技巧。
適用情境
適用於驗證螢幕閱讀器相容性、測試 ARIA 實作、除錯輔助技術問題,或檢查表單、導覽和動態內容的無障礙性。
執行需求
未隨附指令碼或工具,僅為純說明性參考。實際測試需要相應的螢幕閱讀器和平台,例如 macOS 或 iOS 上的 VoiceOver、Windows 上的 NVDA 或 JAWS,以及 Android 上的 TalkBack。

Screen Reader Testing

Practical guide to testing web applications with screen readers for comprehensive accessibility validation.

When to Use This Skill

  • Validating screen reader compatibility
  • Testing ARIA implementations
  • Debugging assistive technology issues
  • Verifying form accessibility
  • Testing dynamic content announcements
  • Ensuring navigation accessibility

Core Concepts

1. Major Screen Readers

Screen ReaderPlatformBrowserUsage
VoiceOvermacOS/iOSSafari~15%
NVDAWindowsFirefox/Chrome~31%
JAWSWindowsChrome/IE~40%
TalkBackAndroidChrome~10%
NarratorWindowsEdge~4%

2. Testing Priority

Minimum Coverage:1. NVDA + Firefox (Windows)2. VoiceOver + Safari (macOS)3. VoiceOver + Safari (iOS)
Comprehensive Coverage:+ JAWS + Chrome (Windows)+ TalkBack + Chrome (Android)+ Narrator + Edge (Windows)

3. Screen Reader Modes

ModePurposeWhen Used
Browse/VirtualRead contentDefault reading
Focus/FormsInteract with controlsFilling forms
ApplicationCustom widgetsARIA applications

VoiceOver (macOS)

Setup

Enable: System Preferences → Accessibility → VoiceOverToggle: Cmd + F5Quick Toggle: Triple-press Touch ID

Essential Commands

Navigation:VO = Ctrl + Option (VoiceOver modifier)
VO + Right Arrow   Next elementVO + Left Arrow    Previous elementVO + Shift + Down  Enter groupVO + Shift + Up    Exit group
Reading:VO + A             Read all from cursorCtrl               Stop speakingVO + B             Read current paragraph
Interaction:VO + Space         Activate elementVO + Shift + M     Open menuTab                Next focusable elementShift + Tab        Previous focusable element
Rotor (VO + U):Navigate by: Headings, Links, Forms, LandmarksLeft/Right Arrow   Change rotor categoryUp/Down Arrow      Navigate within categoryEnter              Go to item
Web Specific:VO + Cmd + H       Next headingVO + Cmd + J       Next form controlVO + Cmd + L       Next linkVO + Cmd + T       Next table

Testing Checklist

markdown
## VoiceOver Testing Checklist
### Page Load
- [ ] Page title announced- [ ] Main landmark found- [ ] Skip link works
### Navigation
- [ ] All headings discoverable via rotor- [ ] Heading levels logical (H1 → H2 → H3)- [ ] Landmarks properly labeled- [ ] Skip links functional
### Links & Buttons
- [ ] Link purpose clear- [ ] Button actions described- [ ] New window/tab announced
### Forms
- [ ] All labels read with inputs- [ ] Required fields announced- [ ] Error messages read- [ ] Instructions available- [ ] Focus moves to errors
### Dynamic Content
- [ ] Alerts announced immediately- [ ] Loading states communicated- [ ] Content updates announced- [ ] Modals trap focus correctly
### Tables
- [ ] Headers associated with cells- [ ] Table navigation works- [ ] Complex tables have captions

Common Issues & Fixes

html
<!-- Issue: Button not announcing purpose --><button><svg>...</svg></button>
<!-- Fix --><button aria-label="Close dialog"><svg aria-hidden="true">...</svg></button>
<!-- Issue: Dynamic content not announced --><div id="results">New results loaded</div>
<!-- Fix --><div id="results" role="status" aria-live="polite">New results loaded</div>
<!-- Issue: Form error not read --><input type="email" /><span class="error">Invalid email</span>
<!-- Fix --><input type="email" aria-invalid="true" aria-describedby="email-error" /><span id="email-error" role="alert">Invalid email</span>

NVDA (Windows)

Setup

Download: nvaccess.orgStart: Ctrl + Alt + NStop: Insert + Q

Essential Commands

Navigation:Insert = NVDA modifier
Down Arrow         Next lineUp Arrow           Previous lineTab                Next focusableShift + Tab        Previous focusable
Reading:NVDA + Down Arrow  Say allCtrl               Stop speechNVDA + Up Arrow    Current line
Headings:H                  Next headingShift + H          Previous heading1-6                Heading level 1-6
Forms:F                  Next form fieldB                  Next buttonE                  Next edit fieldX                  Next checkboxC                  Next combo box
Links:K                  Next linkU                  Next unvisited linkV                  Next visited link
Landmarks:D                  Next landmarkShift + D          Previous landmark
Tables:T                  Next tableCtrl + Alt + Arrows Navigate cells
Elements List (NVDA + F7):Shows all links, headings, form fields, landmarks

Browse vs Focus Mode

NVDA automatically switches modes:- Browse Mode: Arrow keys navigate content- Focus Mode: Arrow keys control interactive elements
Manual switch: NVDA + Space
Watch for:- "Browse mode" announcement when navigating- "Focus mode" when entering form fields- Application role forces forms mode

Testing Script

markdown
## NVDA Test Script
### Initial Load
1. Navigate to page2. Let page finish loading3. Press Insert + Down to read all4. Note: Page title, main content identified?
### Landmark Navigation
1. Press D repeatedly2. Check: All main areas reachable?3. Check: Landmarks properly labeled?
### Heading Navigation
1. Press Insert + F7 → Headings2. Check: Logical heading structure?3. Press H to navigate headings4. Check: All sections discoverable?
### Form Testing
1. Press F to find first form field2. Check: Label read?3. Fill in invalid data4. Submit form5. Check: Errors announced?6. Check: Focus moved to error?
### Interactive Elements
1. Tab through all interactive elements2. Check: Each announces role and state3. Activate buttons with Enter/Space4. Check: Result announced?
### Dynamic Content
1. Trigger content update2. Check: Change announced?3. Open modal4. Check: Focus trapped?5. Close modal6. Check: Focus returns?

JAWS (Windows)

Essential Commands

Start: Desktop shortcut or Ctrl + Alt + JVirtual Cursor: Auto-enabled in browsers
Navigation:Arrow keys         Navigate contentTab                Next focusableInsert + Down      Read allCtrl               Stop speech
Quick Keys:H                  Next headingT                  Next tableF                  Next form fieldB                  Next buttonG                  Next graphicL                  Next list;                  Next landmark
Forms Mode:Enter              Enter forms modeNumpad +           Exit forms modeF5                 List form fields
Lists:Insert + F7        Link listInsert + F6        Heading listInsert + F5        Form field list
Tables:Ctrl + Alt + Arrows Table navigation

TalkBack (Android)

Setup

Enable: Settings → Accessibility → TalkBackToggle: Hold both volume buttons 3 seconds

Gestures

Explore: Drag finger across screenNext: Swipe rightPrevious: Swipe leftActivate: Double tapScroll: Two finger swipe
Reading Controls (swipe up then right):- Headings- Links- Controls- Characters- Words- Lines- Paragraphs

Common Test Scenarios

1. Modal Dialog

html
<!-- Accessible modal structure --><div  role="dialog"  aria-modal="true"  aria-labelledby="dialog-title"  aria-describedby="dialog-desc">  <h2 id="dialog-title">Confirm Delete</h2>  <p id="dialog-desc">This action cannot be undone.</p>  <button>Cancel</button>  <button>Delete</button></div>
javascript
// Focus managementfunction openModal(modal) {  // Store last focused element  lastFocus = document.activeElement;
  // Move focus to modal  modal.querySelector("h2").focus();
  // Trap focus  modal.addEventListener("keydown", trapFocus);}
function closeModal(modal) {  // Return focus  lastFocus.focus();}
function trapFocus(e) {  if (e.key === "Tab") {    const focusable = modal.querySelectorAll(      'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])',    );    const first = focusable[0];    const last = focusable[focusable.length - 1];
    if (e.shiftKey && document.activeElement === first) {      last.focus();      e.preventDefault();    } else if (!e.shiftKey && document.activeElement === last) {      first.focus();      e.preventDefault();    }  }
  if (e.key === "Escape") {    closeModal(modal);  }}

2. Live Regions

html
<!-- Status messages (polite) --><div role="status" aria-live="polite" aria-atomic="true">  <!-- Content updates will be announced after current speech --></div>
<!-- Alerts (assertive) --><div role="alert" aria-live="assertive">  <!-- Content updates interrupt current speech --></div>
<!-- Progress updates --><div  role="progressbar"  aria-valuenow="75"  aria-valuemin="0"  aria-valuemax="100"  aria-label="Upload progress"></div>
<!-- Log (additions only) --><div role="log" aria-live="polite" aria-relevant="additions">  <!-- New messages announced, removals not --></div>

3. Tab Interface

html
<div role="tablist" aria-label="Product information">  <button role="tab" id="tab-1" aria-selected="true" aria-controls="panel-1">    Description  </button>  <button    role="tab"    id="tab-2"    aria-selected="false"    aria-controls="panel-2"    tabindex="-1"  >    Reviews  </button></div>
<div role="tabpanel" id="panel-1" aria-labelledby="tab-1">  Product description content...</div>
<div role="tabpanel" id="panel-2" aria-labelledby="tab-2" hidden>  Reviews content...</div>
javascript
// Tab keyboard navigationtablist.addEventListener("keydown", (e) => {  const tabs = [...tablist.querySelectorAll('[role="tab"]')];  const index = tabs.indexOf(document.activeElement);
  let newIndex;  switch (e.key) {    case "ArrowRight":      newIndex = (index + 1) % tabs.length;      break;    case "ArrowLeft":      newIndex = (index - 1 + tabs.length) % tabs.length;      break;    case "Home":      newIndex = 0;      break;    case "End":      newIndex = tabs.length - 1;      break;    default:      return;  }
  tabs[newIndex].focus();  activateTab(tabs[newIndex]);  e.preventDefault();});

Debugging Tips

javascript
// Log what screen reader seesfunction logAccessibleName(element) {  const computed = window.getComputedStyle(element);  console.log({    role: element.getAttribute("role") || element.tagName,    name:      element.getAttribute("aria-label") ||      element.getAttribute("aria-labelledby") ||      element.textContent,    state: {      expanded: element.getAttribute("aria-expanded"),      selected: element.getAttribute("aria-selected"),      checked: element.getAttribute("aria-checked"),      disabled: element.disabled,    },    visible: computed.display !== "none" && computed.visibility !== "hidden",  });}

Best Practices

Do's

  • Test with actual screen readers - Not just simulators
  • Use semantic HTML first - ARIA is supplemental
  • Test in browse and focus modes - Different experiences
  • Verify focus management - Especially for SPAs
  • Test keyboard only first - Foundation for SR testing

Don'ts

  • Don't assume one SR is enough - Test multiple
  • Don't ignore mobile - Growing user base
  • Don't test only happy path - Test error states
  • Don't skip dynamic content - Most common issues
  • Don't rely on visual testing - Different experience

來源與署名

來源:wshobson/agents位於plugins/accessibility-compliance/skills/screen-reader-testing提交46891e7

授權條款: 無授權條款

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架