mcpField
S
screen-reader-testing

screen-reader-testing

github.com/wshobson测试与质量41.9k10.8k 次安装2 周前更新
代码仓库

关于 screen-reader-testing

这是一项用于通过屏幕阅读器验证 Web 无障碍性的实用技能,面向开发者和测试人员,涵盖平台覆盖、交互模式及常见组件场景。

主要功能

  1. 01主流屏幕阅读器覆盖建议
  2. 02浏览、焦点和应用模式说明
  3. 03对话框、实时区域与标签页场景
  4. 04可访问名称调试建议
  5. 05键盘与焦点测试实践

适用场景

  1. 01检查不同屏幕阅读器中的 ARIA 行为
  2. 02使用辅助技术测试表单和导航
  3. 03排查动态内容播报问题

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/wshobson/agents --skill screen-reader-testing

安装后在 Claude Code 中输入/screen-reader-testing确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
jaws-talkback.md
nvda.md
voiceover-ios.md
voiceover.md

详细介绍

屏幕阅读器测试

使用屏幕阅读器测试 Web 应用的实用指南,助你全面验证无障碍性。

何时使用此技能

  • 验证屏幕阅读器兼容性
  • 测试 ARIA 实现
  • 调试辅助技术问题
  • 验证表单的无障碍性
  • 测试动态内容播报
  • 确保导航无障碍

核心概念

1. 主流屏幕阅读器
屏幕阅读器平台浏览器使用率
VoiceOvermacOS/iOSSafari~15%
NVDAWindowsFirefox/Chrome~31%
JAWSWindowsChrome/IE~40%
TalkBackAndroidChrome~10%
NarratorWindowsEdge~4%
2. 测试优先级
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. 屏幕阅读器模式
模式用途使用场景
浏览/虚拟阅读内容默认阅读模式
焦点/表单与控件交互填写表单
应用程序自定义小组件ARIA 应用程序

在 macOS 上测试前,请阅读 VoiceOver 设置、命令和检查清单。

如需覆盖 iOS Safari,请阅读 VoiceOver 触控设置和手势。

使用 NVDA 测试前,请阅读 NVDA 设置、模式、命令和测试脚本。

使用 JAWS 或 TalkBack 测试前,请阅读 平台设置和命令。

常见测试场景

1. 模态对话框

<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. 实时区域

<div role="status" aria-live="polite" aria-atomic="true">
  
</div>


<div role="alert" aria-live="assertive">
  
</div>


<div
  role="progressbar"
  aria-valuenow="75"
  aria-valuemin="0"
  aria-valuemax="100"
  aria-label="Upload progress"
></div>


<div role="log" aria-live="polite" aria-relevant="additions">
  
</div>
3. 标签页界面
<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();
});

调试技巧

// 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",
  });
}

最佳实践

应做事项
  • 使用实际的屏幕阅读器进行测试 - 不要只用模拟器
  • 优先使用语义化 HTML - ARIA 是补充手段
  • 在浏览和焦点模式下测试 - 两种模式的体验不同
  • 验证焦点管理 - 尤其是针对 SPA
  • 先只用键盘进行测试 - 这是屏幕阅读器测试的基础
不应做事项
  • 不要以为一种屏幕阅读器就够了 - 应测试多种屏幕阅读器
  • 不要忽视移动设备 - 用户群体正在不断扩大
  • 不要只测试顺利流程 - 也要测试错误状态
  • 不要跳过动态内容 - 这是最常见的问题
  • 不要依赖视觉测试 - 两者的体验不同

评论

支持 Markdown