USWDS 无障碍开发规范:面向仓库贡献者的 WCAG 2.1 AA 实操指南
USWDS 无障碍开发规范:面向仓库贡献者的 WCAG 2.1 AA 实操指南
导读
本文围绕 USWDS(U.S. Web Design System)仓库内的无障碍开发规范文档(.agents/skills/uswds-accessibility/SKILL.md)展开,系统梳理了面向仓库贡献者(而非下游应用消费方)的无障碍标准、不可妥协原则、高危组件清单、验证闭环与常见反模式。读完本文,你将掌握:如何在修改交互组件(modal、combo-box、date-picker、accordion 等)时守住 WCAG 2.1 AA 底线,如何运行并解读仓库内置的自动化无障碍测试(npm run test:a11y),以及如何用手动辅助技术测试矩阵完成对焦点管理、键盘行为与屏幕阅读器播报的最终验收。文中所有结论均可在当前仓库的源码与测试用例中找到对应证据。
适用对象说明:本文规范针对在 USWDS 仓库内工作(构建组件)的编码 Agent 与开发者。若你的团队只是在应用中消费 USWDS,无障碍验收逻辑并不相同,需要另行参考 designsystem.digital.gov 的公开文档。
1. 标准与底线:WCAG 2.1 AA 与法律下限
1.1 质量线:WCAG 2.1 AA 起步
规范文档给出的核心标准非常明确:
- 最低门槛(Bar):WCAG 2.1 AA。
- 若组件本已追求更高目标(WCAG 2.2 / AAA),或修复成本很低,应优先采用更高标准。
- 法律下限(Legal floor):Section 508 采纳的是 WCAG 2.0 AA;《美国残疾人法案》(ADA)采纳的是 WCAG 2.1 AA。因此仅满足 508 并不等于达到 USWDS 的质量线——质量线始终是 WCAG 2.1 AA 起步。
这里的关键区别在于:法律合规 ≠ 产品无障碍质量。符合 508 只是不违法的起点,USWDS 的组件质量标准高于法律底线。
1.2 自动化测试的定位
仓库在 package.json 中定义了无障碍自动化测试脚本:
"test:a11y": "storybook build -o _site && concurrently -k -s first -n \"SB,TEST\" \"http-server _site --port 6006 --silent\" \"wait-on --timeout 30000 http://127.0.0.1:6006 && test-storybook --url http://127.0.0.1:6006\""
从脚本可见其技术链路:Storybook 构建 + 静态服务器 + Playwright 驱动的 test-storybook(集成 Axe)。规范文档明确:npm run test:a11y 的失败是阻塞性的(blocking),而非建议性(advisory)。但与此同时:
通过 Axe 是必要不充分条件。
即 Axe 全绿不代表无障碍验收完成——焦点行为、键盘操作、屏幕阅读器播报这类需要人工判断的维度,自动化扫描覆盖不到(详见第 5 节验证闭环)。test:ci 脚本(npm run lint && gulp test && npm run test:dist && npm run test:a11y && npm run build:html)也将 test:a11y 纳入了 CI 全链路。
1.3 术语表:先统一语言
规范文档定义了四组关键术语,在仓库评审与 PR 交流中应统一使用:
- WCAG:Web Content Accessibility Guidelines,成功标准引用格式为 SC X.X.X(例如 SC 2.4.7)。USWDS 目标为 WCAG 2.1 AA 起步。
- 辅助技术(Assistive technology):帮助用户与内容交互的软硬件(屏幕阅读器、放大镜、语音控制、开关访问等),不等于自动化无障碍扫描。
- 手动辅助技术测试(Manual assistive technology testing):用真实工具与浏览器进行人工验证(纯键盘、屏幕阅读器组合、缩放、强制颜色模式)。交互行为变更时必须执行;自动化
npm run test:a11y不能替代它。 - 屏幕阅读器(Screen reader):验证矩阵中使用的辅助技术类型(VoiceOver、NVDA、TalkBack)。规范中"screen reader"默认指具名的浏览器 + 屏幕阅读器组合,除非变更纯属视觉范畴。
2. 不可妥协的七条原则(Non-negotiables)
规范文档从 USWDS 无障碍原则中提炼出**不得回退(do not regress)**的七条底线,任何修改都必须守住:
- 键盘焦点顺序:焦点遵循逻辑、可预测的顺序,且焦点指示器始终可见(WCAG 2.4.7;不得遮挡焦点,SC 2.4.11)。
- 禁止仅靠视觉传达含义:含义不得仅由视觉样式(颜色、形状、位置)传达;文本与 UI 组件对比度满足 WCAG 最低要求——普通文本 4.5:1、大文本与 UI 组件/图形 3:1(SC 1.4.3、1.4.11)。
- 状态变更播报:页面与组件状态变化需在用户需要时向辅助技术播报(状态消息,WCAG 4.1.3)。
- 非文本内容的文本替代:非文本内容必须有合适的文本等价物。
- 关键信息不依赖 hover:关键信息不能仅靠悬停呈现。
- 触控与纯键盘双支持:触控与纯键盘交互都必须完整支持;可行时触控目标最小为 24×24 CSS px(WCAG 2.2 SC 2.5.8)。
- 尊重设备与浏览器无障碍设置(WCAG 1.4.4、1.4.10、1.4.12;涉及动画时 SC 2.2.2)——布局与功能必须在用户自定义设置下依然可用,而非假设默认浏览器/OS 配置。具体拆解为:
- 文本缩放与回流(reflow):400% 缩放 / 320px 宽度下不得丢失内容或功能;优先使用
rem/em而非固定px来设定文本与间距。 - 减弱动态效果(Reduced motion):尊重
prefers-reduced-motion;不得仅靠动画传达含义;确需保留的关键动画必须显式标注。 - 强制颜色 / 高对比度(Forced colors):测试纯图标控件与 CSS 生成指示器;当系统颜色接管时,不得仅依赖
background-color表达状态。 - 指针 / 输入假设:同时支持键盘与触控;不得要求仅 hover 交互(见第 5 条)或精细指针精度。
- 禁止在组件 CSS 中:禁用缩放、覆盖用户文本间距、重置 OS 字号或对比度设置。
- 文本缩放与回流(reflow):400% 缩放 / 320px 宽度下不得丢失内容或功能;优先使用
2.1 仓库中的原则落地示例:文本替代
以 Banner 组件为例,usa-banner.twig 中的锁形图标通过 role="img"、aria-labelledby 与 <title>/<desc> 提供了完整文本替代;而装饰性国旗小图直接使用 aria-hidden="true" + alt="" 标记为纯装饰。这正是原则 4"非文本内容文本替代"在模板层的实际体现——装饰性内容隐藏、信息性内容命名。
3. 适用范围防火墙(Scope firewall)
USWDS 交付无障碍组件,但这不意味着使用它的站点自动满足 Section 508。规范明确两条红线:
- 不得把组件级修复当作关闭下游项目应用级无障碍 issue 的依据。
- 不得仅凭"使用了 USWDS"或"本仓库 Axe 通过"就宣称整个产品"508 合规"或"符合 WCAG"。
这条防火墙与本文第 8 节的反模式相互呼应:产品级合规声明需要应用层面的整体验收,组件库层面的通过与否不能替代。
4. 何时适用本规范:变更触碰面清单
规范文档列出的变更类型,只要触碰以下任一领域,就必须按本文流程执行:
- 交互行为、焦点或键盘处理
aria-*、role、名称、描述、live region- 显示/隐藏模式(
hidden、aria-hidden、data-modal-hidden、disclosure) - 仅供屏幕阅读器的文本(
.usa-sr-only及同类) - 颜色对比度、焦点可见性或触控目标尺寸
- a11y 测试、Storybook a11y stories,或 PR 中的手动辅助技术测试说明
反之,若变更与本规范无关(tokens、纯 Sass 颜色、不影响屏幕阅读器的文档 typo),应明说并停止,不要强行套用无障碍评审。
5. 高危组件清单(High-risk components)
规范文档给出了一张"需格外小心"的组件表,每个条目都对应仓库中的真实源码。修改这些组件前,优先阅读现有组件 JS 与测试。
| 组件区域 | 需要警惕的点 | 仓库源码佐证 |
|---|---|---|
| Modal | 焦点陷阱、初始焦点、Escape、焦点恢复到打开者;非 modal 元素的 aria-hidden/data-modal-hidden 必须在关闭时清除——即使打开者节点已被移除 |
usa-modal/src/index.js、modal.spec.js |
| Combo box | Listbox/popup 语义、过滤播报、指针与键盘行为对齐 | usa-combo-box/src/index.js 中 aria-expanded、aria-activedescendant、role="listbox" |
| Date picker / date range | Dialog 或 grid 键盘模型、禁用日期、焦点返回 | usa-date-picker/src/index.js 中 aria-hidden 与 Escape 处理 |
| Accordion | aria-expanded、button/heading 模式、多选 vs 单选 |
usa-accordion/src/index.js 中 EXPANDED 常量与 data-allow-multiple 开关 |
| Header / nav / language selector | 菜单、焦点陷阱复用、移动端开关 | usa-header/src/index.js 中 aria-hidden 与 FocusTrap |
| File input、character count、validation | live region、错误关联(aria-describedby)、状态文本 |
usa-character-count/src/index.js、usa-validation/src/index.js |
| Table(可排序) | 列表头按钮语义(aria-sort)、排序状态播报、排序控件键盘可操作性 |
usa-table/src/index.js |
| Tooltip | hover 和 focus/关闭支持;绝不可作为关键信息的唯一来源;不得造成键盘陷阱 | usa-tooltip/src/index.js |
| Banner | "如何确认这是官方站点"展开/收起的 disclosure 语义 | usa-banner/src/usa-banner.twig 中 aria-expanded/aria-controls 按钮 |
| Pagination | 当前页指示(aria-current="page")、每链接的可访问名称 |
usa-pagination/src/includes/_pagination-button.twig |
| Radio / checkbox 组、fieldset | fieldset/legend 分组、组级错误关联 |
各表单组件的字段集模板 |
5.1 高频共享工具:底层实现速览
上述组件频繁依赖 packages/uswds-core/src/js/utils/ 下的五个共享工具:
- focus-trap.js:通过
FOCUSABLE选择器(a[href]、非 disabled 的input/select/textarea/button、iframe、[tabindex="0"]、[contenteditable]等)计算首/末可聚焦节点,实现 Tab 循环(正向到末位回绕到首位、反向同理),并支持注入Escape等附加按键绑定与autoFocus选项。 - behavior.js:事件委托框架——把
{ 事件: { 选择器: 回调 } }规范化为addEventListener参数;on()/off()控制组件初始化与销毁,是focus-trap中update(isActive)开关背后的机制。 - keymap、active-element、toggle:分别负责按键映射、活动元素查询与布尔属性切换。
以 Modal 为例,index.js 中 modal.focusTrap = FocusTrap(targetModal, { Escape: onMenuClose }) 并调用 modal.focusTrap.update(safeActive),即把 focus-trap 的 Tab 回环与 Escape 关闭绑定到 modal 的开关生命周期上。而对应的 modal.spec.js 用 Mocha + jsdom 逐一断言了"打开时 aria-hidden="true" 写入非 modal 兄弟节点、初始焦点落在 footer 按钮、关闭后 aria-hidden 移除、焦点恢复给打开者"等行为——这些测试正是规范第 5 节"关注点"的自动化镜像。
6. 验证闭环(Verification loop)
规范明确:不得在完成下述相关步骤(或给出明确延后理由)之前宣布无障碍修复完成。整个闭环分四步:
6.1 第一步:优先复用既有模式,而非新造 ARIA
- 与仓库内兄弟组件及 APG(Accessible Rich Internet Applications Authoring Practices)对齐的模式保持一致。
- 避免"ARIA 汤"(ARIA soup):当可见标签或原生语义已能为控件命名时,不要再加
aria-label去"堵住"检查工具。
6.2 第二步:仓库内单元测试(Mocha + jsdom)
行为变更时必须新增或更新 Mocha + jsdom 测试,参照 usa-accordion/src/test/ 或组件既有 specs。测试必须覆盖:焦点返回、Escape、打开/关闭属性清理、键盘路径(当这些正是 bug 所在时)。
单测运行示例(仓库规范原样给出):
npx mocha --require jsdom-global/register packages/<pkg>/src/test/<name>.spec.js
6.3 第三步:自动化无障碍扫描
- 完整套件:
npm run test:a11y(依赖 Storybook 构建与 Playwright 浏览器,速度慢)。当 markup/roles 变更时,或宣称 CI 无障碍绿灯前运行。 - 在 PR 中说明:本地跑了什么、CI 将跑什么。
6.4 第四步:手动辅助技术测试(交互行为变更时必做)
自动化扫描不是焦点、键盘、对比度或播报变更的签字依据。实质性交互修复的最低手动测试矩阵为:
- 纯键盘:Tab、Shift+Tab、Enter/Space、Escape、方向键(适用处)
- 一组屏幕阅读器组合:VoiceOver + Safari,或 NVDA + Firefox(或 Chrome)
- 缩放/回流:400% 缩放 / 320px 视口宽度——确认无内容或功能丢失(WCAG 1.4.10、1.4.4)
- 强制颜色模式:若变更新增或改动纯图标控件、CSS 生成(
::before/::after)内容,需在 forced-colors / Windows 高对比度模式抽查 - 触控/手势:若变更影响触控或手势行为,需在桌面组合之外追加移动端屏幕阅读器一轮(VoiceOver + iOS Safari,或 TalkBack + Chrome)
PR 中必须写明:
- 你验证了什么
- 使用的浏览器 + 辅助技术
- 哪些问题是
develop分支上已有的,哪些是本次引入的 - 局限 / 不在范围内
代码评审(uswds-code-review gate 16b)应路由手动辅助技术测试,而不是仅凭 diff 凭空判定 pass/fail——这正是本 skill 与代码评审技能的分工:无障碍判断归本技能,代码评审遇到辅助技术行为问题转交本技能处理。
7. 反模式清单(Anti-patterns):禁止行为
规范文档列出以下禁止行为,均可直接作为 PR 自检项:
- 用
aria-hidden隐藏内容来"骗过"Axe,却没有对应的取消隐藏路径 - modal/dialog 关闭后页面残留
aria-hidden(尤其当打开者节点已被移除时) - 用
div+ 点击处理器替换原生<button>/<a>/<input> - 仅靠
title承担关键可访问名称 - 用 polite live region 播报每次击键,而一个状态变更播报就足够(保持 WCAG 4.1.3 状态消息本意)
- 设置正数
tabindex(tabindex > 0)来强造自定义顺序,而非修正 DOM/源码顺序 - 抑制默认焦点轮廓却没有合规的自定义指示器(WCAG 2.4.7 / 2.4.11)
- 仅依赖 hover 的 tooltip 或菜单,没有 focus 触发的等价交互
- 上线新的纯图标控件或 CSS 生成内容指示器,却未检查 forced-colors / Windows 高对比度模式
- 仅凭组件使用或 Axe 通过就断言产品级 508 合规
8. 输出期望:评审意见的四个显式要素
应用本技能时,输出必须明确以下四要素,缺一不可:
- 风险(Risk):涉及哪条不可妥协原则或哪个高危组件区域
- 变更(Change):markup / 行为 / 测试发生了什么变化
- 证据(Evidence):跑过的测试、手动辅助技术测试矩阵(含缩放/回流、forced-colors、移动端屏幕阅读器测试,如适用),或延后理由
- 路由(Routing):若仍需真实辅助技术环境验证,应写明"
Needs accessibility specialist assistive technology verification"并列出矩阵——不得仅凭代码就软性宣称"LGTM for assistive technology behavior"。
9. 与仓库其他约定的关系
- 根 Agent 指南:仓库环境与常规设置见根目录 AGENTS.md。
- 代码评审技能:PR 校准与非无障碍门禁见
.agents/skills/uswds-code-review/;本技能与之互补,承接其 gate 16 中的无障碍判断职责。 - 上游上下文:本规范源于 USWDS 无障碍实践演进(对应上游 issue #6749),公开的用户侧无障碍说明以 designsystem.digital.gov 文档为准(当前仓库只读,不提供外链展开)。
结语
USWDS 的无障碍开发规范给出了一个可执行的分层模型:WCAG 2.1 AA 质量线 → 七条不可妥协原则 → 高危组件专项清单 → 四步验证闭环(模式复用 → Mocha 单测 → Axe 自动化 → 手动矩阵)→ 反模式禁令。对仓库贡献者而言,核心心法是两条:其一,自动化测试(npm run test:a11y)是必要不充分条件,焦点/键盘/播报类结论必须落到真实辅助技术矩阵;其二,组件级无障碍 ≠ 产品级合规,评审意见必须区分"已有问题"与"本次引入",并如实标注需要专业无障碍专家验证的部分。