前端可访问性 是做什么的?
适用于 <ui-packages-dir> 中的组件和 <web-dir>/src/ 下的页面。目标是 WCAG 2.1 Level AA,这是一个底线而非目标。
解决项目约定
尖括号中的值来自项目的技术栈配置文件。按以下顺序解析它们:
- 读取仓库根目录下的
STACK-PROFILE.md并直接使用其中的值。 - 如果该文件不存在或缺少某个键,则从仓库中推断——从 Web 应用程序的依赖关系中推断
css-strategy和ui-framework。 - 如果仍然无法解析,则匹配仓库中已经存在的约定。
- 仅当仓库为空时,退回到本技能声明的默认值——然后将选定的值写入
STACK-PROFILE.md。
切勿在仓库已表达出一种约定时发明新的约定。
硬性规则
1. 语义化 HTML 优先
- 使用
<button>表示操作,<a>表示导航。绝不要使用<div>并添加点击处理程序。 - 标题级别向下递减,不得跳级。
- 每个表单字段都必须有
<label>,并通过htmlFor属性关联。 - 使用
<fieldset>和<legend>对表单控件进行分组。 - 每个页面都应有地标元素 ——
<nav>、<main>、<footer>、<aside>。
2. 仅在 HTML 无法满足语义时使用 ARIA
优先级顺序是:语义元素,然后是 ARIA 属性,最后是自定义行为。切勿使用 ARIA 覆盖元素已经携带的语义——在 <div> 上使用 role="button" 是糟糕地重新实现了 <button> 免费提供的功能。
错误的 ARIA 比没有更糟糕:错误的 aria-label 会静默地替换屏幕阅读器本应正确计算出的可访问名称。
3. 键盘操作是强制性的
- 所有可交互元素都必须可通过 Tab 键聚焦。
- Tab 顺序应遵循视觉顺序。
- 焦点必须始终可见。切勿在未替换轮廓线的情况下移除轮廓线。
Escape键关闭对话框和弹出窗口;Enter和Space键激活控件;箭头键在复合小部件内移动。- 自定义小部件要显式管理焦点。
4. 颜色和对比度
- 正常文本对比度 4.5:1,大文本(18px 或 14px 粗体)和重要的 UI 边界对比度 3:1。
- 颜色不能单独传达信息——必须配合文本、图标或图案使用。
- 需要同时检查两种主题,而不仅仅是默认主题。
5. 图像和媒体
- 每张图片都必须有
alt属性。内容图片需提供描述性文本,装饰性图片使用alt=""。 - 永远不要使用“图片”、“照片”或文件名作为替代文本。
- 复杂图片需要通过
aria-describedby或图注提供长描述。 - 视频需提供字幕,音频需提供文字转录。
6. 表单
- 每个输入框都必须有标签。
- 错误信息通过
aria-describedby关联,并使用role="alert"进行宣告。 - 无效字段需标记
aria-invalid。 - 必填字段使用
required属性——如果显示星号,则必须在某处用文本说明其含义。 - 提交失败时,焦点应移动到第一个无效字段。
7. 目标大小
WCAG 2.1 AA 没有规定最小目标大小(2.2 AA 设定为 24×24 CSS 像素;44×44 是 2.1 AAA 以及平台 HIG 约定)。对于触控表面上的主要操作,将 44×44 视为最低标准,并按如下方式应用:
- 主要操作、危险操作以及用户在手机上可能够到的任何元素:44×44。
- 密集的桌面界面元素——工具栏、表格行操作、紧凑的表单控件——可以缩小到 24×24,前提是这些目标不与其他目标相邻,并且其他地方存在等效的全尺寸控件。
- 间距很重要。一个 32px 的控件,每边有 6px 的清晰间距,满足 2.2 标准;一个 32px 的控件紧挨着相邻控件则不满足。
第三方组件库通常低于此标准——默认按钮高度为 32 或 36 CSS 像素很常见。不要手动编辑第三方文件来修复,也不要默认接受这种不匹配。在 <wrappers-pkg> 中提高默认值,以便每个消费者都继承合规的大小,同时保留较小的尺寸作为明确的选择,用于密集布局。这样,可访问的选择就是你无需思考就能得到的那个。
在 STACK-PROFILE.md 中记录项目采用了哪种解决方案。未声明的豁免会被每六个月作为一个错误重新发现一次。
8. 运动
- 尊重
prefers-reduced-motion。 - 自动播放的内容需要有暂停控制。
- 任何内容的闪烁频率不得超过每秒三次。
9. 测试是工作的一部分
- 仅使用键盘操作该功能。
- 使用屏幕阅读器测试——macOS 上的 VoiceOver,Windows 上的 NVDA。
- 在端到端测试套件中运行自动化扫描。
自动化工具约能捕获三分之一的实际问题。它们是底线,不是通过标准。
模式
完整的实现位于 references/component-patterns.md。每个模式保证以下内容:
- 按钮——一个语义化的
<button>,其焦点环仅对键盘用户显示(focus-visible),包含符合运动偏好的过渡动画,并提供ariaLabel属性用于纯图标按钮。 - 表单输入——一个带标签的输入框,其错误文本通过
aria-describedby程序化连接,使用role="alert"宣告,并标记aria-invalid;必填星号带有aria-hidden。 - 对话框——使用
showModal()方法实现的原生<dialog>,可免费获得焦点陷阱、Escape键处理以及 inert 背景;打开时焦点移入,关闭时焦点恢复到触发元素。 - 列表框——一个自定义选择组件,具有循环箭头键导航和正确的 ARIA 绑定(
aria-activedescendant、role="listbox");仅在原生的<select>不适用时才构建它。 - 跳过链接——允许键盘用户跳过导航,仅在聚焦时可见;目标元素拥有
tabIndex={-1}以确保焦点真正落在那里。
WCAG 快速参考
| 原则 | 要求 | 实践中 |
|---|---|---|
| 可感知 | 内容对每种感官都可用 | 替代文本、字幕、颜色加图标 |
| 可操作 | 一切都可以通过键盘到达 | Tab 顺序、Escape 关闭、焦点可见 |
| 可理解 | 可预测且措辞清晰 | 一致的导航、平实的语言 |
| 健壮 | 与辅助技术兼容 | 有效的 HTML、正确的 ARIA、语义元素 |
| 问题 | 修复 |
|---|---|
| 未标记的输入框 | 添加 <label htmlFor> |
| 对比度低 | 满足 4.5:1,或大文本 3:1 |
| 缺少键盘路径 | 使用真正的交互元素,管理焦点 |
| 缺少替代文本 | 提供描述,或装饰性图片使用 alt="" |
| 仅靠颜色传达意义 | 添加文本或图标 |
| 纯图标按钮 | 添加 aria-label |
| 闪烁内容 | 保证每秒少于三次闪烁 |
运动偏好
在纯 CSS 中处理减少运动。像 motion-safe: 这样的工具类变体仅在允许运动时应用,因此将其放在减少运动查询内会生成一条永远无法匹配的规则。
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
使用接近零而非 none 可以保持过渡结束事件触发,从而避免等待这些事件的组件挂起。
对于按组件的控制,将 motion-safe: 变体直接应用于动画工具属性本身——例如上面的按钮中使用的 motion-safe:transition-colors。
视觉隐藏内容
大多数工具框架已经提供了 sr-only 和 not-sr-only 类。直接使用它们,不要重新定义。如果你的框架没有,参考实现位于 references/component-patterns.md。
测试清单
键盘
- 每个交互元素均可通过 Tab 键聚焦
- Tab 顺序与视觉顺序匹配
- 焦点始终可见
- Escape 键关闭对话框和弹出窗口
- 箭头键在复合小部件中正常工作
- 对话框关闭时焦点返回到触发元素
屏幕阅读器
- 所有内容均可访问
- 无需周围上下文即可了解控件用途
- 标签与其字段一起宣告
- 错误通过
role="alert"宣告 - 存在地标
- 装饰性元素使用
aria-hidden隐藏
颜色
- 正常文本 4.5:1,大文本和 UI 边界 3:1
- 没有仅靠颜色传达信息的情况
- 两种主题均已验证
运动
- 遵循减少运动设置
- 无超过每秒三次的闪烁
- 自动播放内容可暂停
自动化
扫描在 Playwright 套件中运行——参见 frontend-testing。Axe-in-Playwright 代码片段位于 references/component-patterns.md。
参考文档
| 深入探讨 | 参考 |
|---|---|
| 可访问组件实现:按钮、表单输入、模态框、列表框、跳过链接 | references/component-patterns.md |
相关技能
frontend-architecture—— 组件分层和主题化frontend-testing—— 这些检查运行的地方frontend-component-docs—— 记录组件的可访问性契约