Generated banner for Screen Reader Testing
Generated logo for Screen Reader Testing

Screen Reader Testing

Seth HobsonCommunityCreated by Seth Hobson

  • Skill
  • Guidelines
  • Software development
  • MIT
  • No issues found

From the author

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.
— the description by Seth Hobson

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a structured approach to validating web accessibility by testing directly with assistive technology. It covers the essential screen readers used by the majority of users, outlines testing priorities, and provides practical patterns for common UI components like modals, live regions, and tab interfaces. The guide emphasizes the importance of testing with real hardware and software rather than relying solely on simulators or automated tools, ensuring that dynamic content, focus management, and semantic structures are accessible to all users.

What it does

  • Defines a testing hierarchy for major screen readers including NVDA, JAWS, and VoiceOver.
  • Provides keyboard command references for navigating content, forms, and landmarks.
  • Offers code patterns for accessible modals, live regions, and tab interfaces.
  • Includes a debugging utility to log the accessible name, role, and state of DOM elements.
  • Explains the distinction between browse and focus modes in screen reader software.
  • Supplies a structured test script for systematic accessibility verification.

When to use it

  • When validating the accessibility of a new web feature or component.
  • When debugging issues where assistive technology fails to announce content correctly.
  • When ensuring that dynamic updates in a single-page application are properly communicated.
  • When performing a comprehensive accessibility audit of a web application.
  • When verifying that keyboard navigation and focus management are implemented correctly.

Advantages

  • Promotes testing with actual screen readers for authentic user experience validation.
  • Reduces accessibility bugs by providing clear implementation patterns for common UI elements.
  • Standardizes the testing process across different platforms and browsers.
  • Helps developers understand how screen readers interpret semantic HTML and ARIA attributes.
  • Provides actionable debugging techniques to inspect the accessibility tree.

Usage examples

“How do I test if my modal dialog is accessible to screen reader users?”
The skill provides a specific modal structure using role='dialog' and aria-modal='true', along with JavaScript examples for trapping focus and managing focus return, which you can use to verify your implementation.
“What are the essential NVDA commands for navigating a web page?”
The skill lists key NVDA commands including 'H' for headings, 'F' for form fields, 'K' for links, and 'Insert + Down' to read all content, along with instructions for switching between browse and focus modes.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/wshobson/screen-reader-testing/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip screen-reader-testing.zip -d .agents/skills

Documentation by Seth Hobson

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

Before testing on macOS, read VoiceOver setup, commands, and checklists.

For the required iOS Safari coverage, read VoiceOver touch setup and gestures.

Before testing with NVDA, read NVDA setup, modes, commands, and test scripts.

Before testing with JAWS or TalkBack, read platform setup and commands.

Common Test Scenarios

1. Modal Dialog
<!-- 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>
// Focus management
function 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
<!-- 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
<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>
// Tab keyboard navigation
tablist.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

// Log what screen reader sees
function 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

Files

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
Seth Hobson
Publisher
Seth Hobson (Community)
Source
wshobson/agents · plugins/accessibility-compliance/skills/screen-reader-testing · commit 4bde316
Licence
MIT License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by Seth Hobson or Seth Hobson.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download screen-reader-testing.zip