mcpField
W
wcag-design

wcag-design

github.com/alexsnchz设计与 UI051 次安装34 周前更新
代码仓库

关于 wcag-design

WCAG-Design 是供前端团队创建无障碍界面的设计技能,介绍 WCAG 2.2 AA 原则与成功标准,涵盖感知、操作、理解和健壮性。

主要功能

  1. 01WCAG 2.2 AA 指南
  2. 02POUR 四项原则
  3. 03成功标准参考
  4. 04内容与控件的无障碍指导

适用场景

  1. 01按 WCAG 标准检查界面
  2. 02规划键盘可操作的导航
  3. 03检查对比度、文本替代和重排
  4. 04改进表单标签与错误处理

安装

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

$npx skills add https://github.com/alexsnchz/skills --skill wcag-design

安装后在 Claude Code 中输入/wcag-design确认已加载。

附带文件README.md

详细介绍

WCAG-Design 技能

此技能指导创建符合 WCAG 2.2 AA 标准的无障碍、视觉独特的前端界面。无障碍与设计质量并不矛盾——最出色的无障碍界面同样设计精美。


第 1 部分 — WCAG 基础知识

什么是 WCAG?

**Web Content Accessibility Guidelines(网页内容无障碍指南,WCAG)**是全球通用的网页无障碍标准,由 W3C 通过 Web Accessibility Initiative(网页无障碍倡议,WAI)发布。WCAG 规定了如何让残障人士也能访问网页内容,其中包括盲人、低视力人士、聋人、听力受损人士、行动不便人士、光敏人士、言语障碍人士以及有认知或学习障碍的人士。

版本历史
版本年份说明
WCAG 1.01999首个 W3C 无障碍建议标准
WCAG 2.02008进行了重大重构,加入可测试的成功标准
WCAG 2.12018+17 项新标准,涵盖移动设备、低视力和认知障碍
WCAG 2.22023+9 项新标准,移除 4.1.1 Parsing;当前标准
WCAG 3.0~2028草案;采用通俗语言和以结果为导向的模型

向后兼容性:符合 WCAG 2.2 的内容也符合 2.1 和 2.0。


第 2 部分 — POUR 四项原则

每项 WCAG 成功标准都对应以下四项原则之一:

1. 可感知(P)

所有信息和 UI 组件都必须以用户能够感知的方式呈现。

  • 为非文本内容提供文本替代
  • 为媒体提供字幕和音频描述
  • 内容呈现方式应可调整
  • 确保足够的色彩对比度和可读性
2. 可操作(O)

所有 UI 组件和导航都必须可操作。

  • 完全支持键盘操作
  • 不设置键盘陷阱
  • 为交互提供充足的时间
  • 不包含可能诱发癫痫的内容
  • 支持多种输入方式(键盘、触控、语音)
  • 提供无障碍焦点管理
3. 可理解(U)

用户必须能够理解内容以及界面的工作方式。

  • 文本应易读且易于理解
  • 行为应可预测且一致
  • 提供输入辅助和错误恢复机制
  • 使用清晰的语言和定义
4. 健壮(R)

内容必须能够与当前及未来的技术可靠地协同工作,尤其是辅助技术。

  • 标记有效且格式规范
  • 与浏览器和辅助技术兼容
  • 语义结构恰当

第 3 部分 — 符合性等级

等级标准数量说明
A32最低级别——基本无障碍,满足基础要求
AA+24(共 56 项)推荐级别——涵盖大多数法律要求;解决常见障碍
AAA+31(共 87 项)增强级别——最高级别;并非总是必需

目标:WCAG 2.2 AA——这是大多数无障碍法律要求的级别(ADA、EN 301 549、Section 508、《安大略省残障人士无障碍法》等)。


第 4 部分 — WCAG 2.2 完整成功标准参考

原则 1:可感知
指南 1.1 — 文本替代
标准等级规则
1.1.1 非文本内容A所有非文本内容均有文本替代(图像、图标、图表、按钮)
指南 1.2 — 基于时间的媒体
标准等级规则
1.2.1 纯音频和纯视频(预录制)A提供文字稿或音频描述
1.2.2 字幕(预录制)A为多媒体中的所有预录制音频提供字幕
1.2.3 音频描述或媒体替代内容(预录制)A提供音频描述或完整的文本替代内容
1.2.4 字幕(实时)AA为所有实时多媒体提供字幕
1.2.5 音频描述(预录制)AA为所有预录制视频提供音频描述
1.2.6 手语(预录制)AAA为预录制音频提供手语翻译
1.2.7 扩展音频描述(预录制)AAA暂停不足时提供扩展音频描述
1.2.8 媒体替代内容(预录制)AAA为所有预录制媒体提供完整的文本替代内容
1.2.9 纯音频(实时)AAA为所有实时纯音频内容提供替代内容
指南 1.3 — 可适应
标准等级规则
1.3.1 信息和关系A必须能够通过编程确定通过呈现方式传达的结构和关系
1.3.2 有意义的顺序A阅读和导航顺序应合乎逻辑
1.3.3 感官特征A指示不能仅依赖颜色、大小、形状、视觉位置或声音
1.3.4 方向AA内容不受纵向或横向屏幕方向限制
1.3.5 确定输入目的AA输入字段的用途可通过编程识别(自动完成)
1.3.6 确定用途AAA按钮、链接和控件的用途可通过编程确定
指南 1.4 — 可辨识
标准等级规则
1.4.1 颜色的使用A颜色不能是传达信息的唯一视觉方式
1.4.2 音频控制A可暂停、停止自动播放的音频,或控制其音量
1.4.3 对比度(最低)AA普通文本为 4.5:1;大号文本为 3:1
1.4.4 调整文本大小AA文本可放大至 200%,且不损失内容
1.4.5 文本图像AA尽可能使用实际文本,而非文本图像
1.4.6 对比度(增强)AAA普通文本为 7:1;大号文本为 4.5:1
1.4.7 低背景音或无背景音AAA纯音频内容应能清晰地从背景音中听辨
1.4.8 视觉呈现AAA限制文本宽度、可调整行距,且不使用两端对齐
1.4.9 文本图像(无例外)AAA除徽标外,不使用文本图像
1.4.10 重排AA内容在宽度为 320px 的屏幕上无需水平滚动即可呈现
1.4.11 非文本对比度AAUI 组件和图形元素的对比度至少为 3:1
1.4.12 文本间距AA行高 ≥1.5×、字间距 ≥0.12em、词间距 ≥0.16em 时,内容不应丢失
1.4.13 悬停或聚焦时的内容AA可悬停或可聚焦的内容可关闭、可悬停且持续显示

原则 2:可操作
指南 2.1 — 键盘可访问
标准等级规则
2.1.1 键盘A所有功能均可通过键盘操作
2.1.2 无键盘陷阱A用户可以使用键盘离开任何组件
2.1.3 键盘(无例外)AAA所有功能均可通过键盘操作——无一例外
2.1.4 字符键快捷键A可禁用或重新映射单字符快捷键
指南 2.2 — 时间充足
标准等级规则
2.2.1 可调整时限A可关闭、调整或延长时限(≥默认时限的 10 倍)
2.2.2 暂停、停止、隐藏A可暂停、停止或隐藏自动移动、闪烁或滚动的内容
2.2.3 无时限AAA除实时事件外,不设置时间限制
2.2.4 中断AAA用户可以推迟或屏蔽所有中断
2.2.5 重新进行身份验证AAA超时后重新进行身份验证时不会丢失数据
2.2.6 超时AAA在会话因不活动而超时前向用户发出警告
指南 2.3 — 癫痫发作和生理反应
标准等级规则
2.3.1 闪烁不超过三次或低于阈值A内容闪烁频率不得超过每秒 3 次(或低于阈值)
2.3.2 三次闪烁AAA所有内容的闪烁频率不得超过每秒 3 次
2.3.3 交互触发的动画AAA可禁用由交互触发的运动动画
指南 2.4 — 可导航
标准等级规则
2.4.1 绕过区块A提供跳过重复区块的机制(跳过导航链接)
2.4.2 页面标题A网页具有描述性标题
2.4.3 焦点顺序A焦点顺序合乎逻辑且有意义
2.4.4 链接用途(结合上下文)A可从链接文本或上下文中明确了解链接用途
2.4.5 多种方式AA提供多种查找页面的方式(搜索、站点地图、导航)
2.4.6 标题和标签AA标题和标签描述主题或用途
2.4.7 焦点可见AA键盘焦点指示器清晰可见
2.4.8 位置AAA用户可以了解自己在一组页面中的位置
2.4.9 链接用途(仅链接文本)AAA仅从链接文本即可明确了解链接用途
2.4.10 区段标题AAA区段标题用于组织内容
2.4.11 焦点未被遮挡(最低要求) ⭐ 2.2 新增AA焦点元素未被粘性或固定内容完全遮挡
2.4.12 焦点未被遮挡(增强要求) ⭐ 2.2 新增AAA焦点元素完全可见
2.4.13 焦点外观 ⭐ 2.2 新增AAA焦点指示器:轮廓线 ≥2px,对比度变化 ≥3:1
指南 2.5 — 输入方式
标准等级规则
2.5.1 指针手势A基于路径的手势提供单指替代方式
2.5.2 指针取消A指针操作在“抬起”事件时激活,而非“按下”时
2.5.3 名称中的标签A无障碍名称包含可见标签文本
2.5.4 运动触发A运动触发的功能提供 UI 替代方式,且可禁用
2.5.5 目标尺寸(增强要求)AAA触控目标尺寸 ≥44×44 CSS 像素
2.5.6 并发输入机制AAA不限制输入方式
2.5.7 拖动操作 ⭐ 2.2 新增AA仅支持拖动的功能提供单指替代方式
2.5.8 目标尺寸(最低要求) ⭐ 2.2 新增AA触控目标尺寸 ≥24×24 CSS 像素,或具有足够间距

原则 3:易于理解
指南 3.1 — 易读
标准等级规则
3.1.1 页面语言A通过程序识别默认的人类语言(lang 属性)
3.1.2 内容部分的语言AA标记内容中的语言变化
3.1.3 不常见词语AAA为行话、习语和技术术语提供定义
3.1.4 缩写AAA首次使用时展开缩写
3.1.5 阅读难度AAA提供适合初中低年级阅读水平的文本,或提供简化版本
3.1.6 发音AAA为有歧义的词语提供发音说明
指南 3.2 — 可预测
标准等级规则
3.2.1 获得焦点时A组件获得焦点时,上下文不发生变化
3.2.2 输入时A更改设置不会自动提交表单
3.2.3 一致的导航AA各页面上的导航机制顺序保持相对一致
3.2.4 一致的标识AA功能相同的组件使用一致的标识
3.2.5 按请求更改AAA仅在用户请求时更改上下文
3.2.6 一致的帮助 ⭐ 2.2 新增A各页面上的帮助机制位于相同位置
指南 3.3 — 输入辅助
标准等级规则
3.3.1 错误识别A以文本形式识别并描述错误
3.3.2 标签或说明A为所有输入提供标签或说明
3.3.3 错误建议AA在已知情况下,为输入错误提供建议
3.3.4 错误预防(法律、财务、数据)AA关键提交操作可撤销、检查或确认
3.3.5 帮助AAA提供上下文相关的帮助
3.3.6 错误预防(所有)AAA所有输入均可撤销、检查或确认
3.3.7 冗余输入 ⭐ 2.2 新增A同一会话中无需再次输入之前已输入的信息
3.3.8 无障碍身份验证(最低要求) ⭐ 2.2 新增AA身份验证无需认知功能测试或记忆要求
3.3.9 无障碍身份验证(增强) ⭐ 2.2 新增AAA为认知功能测试提供增强的替代方案

原则 4:稳健
指南 4.1 — 兼容
标准等级规则
4.1.1 解析A在 WCAG 2.2 中已移除 — 因 HTML5 错误修正机制而过时
4.1.2 名称、角色、值A所有组件都有无障碍名称、角色和状态/值
4.1.3 状态消息AA向屏幕阅读器播报状态消息,且不更改焦点

第 5 部分 — 实现参考

5.1 颜色对比度规则
场景AA 最低要求AAA 增强要求
普通文本(<18px 或 <14px 粗体)4.5:17:1
大号文本(≥18px 或 ≥14px 粗体)3:14.5:1
UI 组件和图形3:13:1
占位符文本4.5:1—
禁用元素豁免豁免
装饰性元素豁免豁免

深色模式说明: 深色主题仍必须满足对比度要求。避免使用纯黑色 #000000 (会造成光晕效应/眼疲劳);改用 #0f0f0f 或 #121212 等柔和的黑色。 深色背景上的高饱和度颜色可能会产生视觉抖动 — 请降低饱和度。

工具: WebAIM Contrast Checker、Colour Contrast Analyser、axe DevTools、Chrome DevTools。


5.2 语义化 HTML — 优先使用原生元素

始终优先使用语义化 HTML,而不是 ARIA。ARIA 是补充手段,不能替代语义化 HTML。


<header>…</header>
<nav aria-label="Main navigation">…</nav>
<main>
  <article>…</article>
  <aside>…</aside>
</main>
<footer>…</footer>


<div class="header">…</div>
<div class="nav">…</div>

语义化元素 → 地标映射:

元素ARIA 地标角色
<header>banner
<nav>navigation
<main>main
<aside>complementary
<footer>contentinfo
<section aria-label="...">region
<form>form

标题层级规则: 切勿跳过层级。<h1> → <h2> → <h3>(不要使用 h1 → h3)。 可以使用多个 <h1> 元素,但第一个应是页面/章节标题。


5.3 WAI-ARIA 速查参考

仅在语义化 HTML 不足时使用 ARIA。ARIA 首要规则:如果可以使用内置语义的原生 HTML 元素或属性,就不要使用 ARIA。

地标角色
<div role="banner">…</div>        
<div role="navigation">…</div>    
<div role="main">…</div>          
<div role="complementary">…</div> 
<div role="contentinfo">…</div>   
<div role="search">…</div>        
<div role="region" aria-label="…">…</div> 
小部件角色(交互组件)
role="button"      
role="checkbox"    
role="radio"       
role="combobox"    
role="listbox"     
role="option"      
role="menu"        
role="menuitem"    
role="tab"         
role="tablist"     
role="tabpanel"    
role="dialog"      
role="alertdialog" 
role="slider"      
role="progressbar" 
role="tooltip"     
role="tree"        
role="treeitem"    
role="grid"        
role="gridcell"    
实时区域角色(动态内容)
role="alert"       
role="status"      
role="log"         
role="timer"       


aria-live="polite"    
aria-live="assertive" 
aria-live="off"       
aria-atomic="true"    
aria-relevant="additions text" 
必要的 ARIA 属性

aria-label="Close dialog"
aria-labelledby="heading-id"
aria-describedby="description-id help-text-id"


aria-expanded="true|false"       
aria-selected="true|false"       
aria-checked="true|false|mixed"  
aria-pressed="true|false|mixed"  
aria-disabled="true"             
aria-hidden="true"               
aria-invalid="true|false|grammar|spelling" 
aria-busy="true|false"           


aria-controls="panel-id"         
aria-owns="list-id"              
aria-haspopup="true|menu|listbox|tree|grid|dialog" 
aria-autocomplete="none|inline|list|both"  


aria-valuemin="0"
aria-valuemax="100"
aria-valuenow="50"
aria-valuetext="50 percent"      


aria-current="page|step|location|date|time|true|false"

5.4 键盘导航实现

每个交互组件都必须完全支持键盘操作。

焦点管理规则
/* NEVER do this without a custom replacement */
*:focus { outline: none; }

/* ✅ Custom focus indicator — meets WCAG 2.4.7 and 2.4.13 */
:focus-visible {
  outline: 2px solid #005fcc;
  outline-offset: 2px;
  border-radius: 2px;
}

/* Different styles for mouse vs keyboard */
:focus:not(:focus-visible) { outline: none; }
:focus-visible { outline: 2px solid #005fcc; outline-offset: 2px; }
Tab 顺序


<div role="button" tabindex="0">Custom Button</div>


<button tabindex="3">Bad</button>


<div tabindex="-1" id="modal-content">…</div>
跳过导航链接

<a href="#main-content" class="skip-link">Skip to main content</a>

<style>
.skip-link {
  position: absolute;
  top: -100%;
  left: 0;
  padding: 0.5rem 1rem;
  background: #000;
  color: #fff;
  z-index: 9999;
  transition: top 0.1s;
}
.skip-link:focus { top: 0; }
</style>

<main id="main-content">…</main>
按组件划分的键盘操作模式
组件按键操作
所有组件Tab将焦点向前移动
所有组件Shift+Tab将焦点向后移动
按钮Enter / Space激活
链接Enter跳转到链接
复选框Space切换状态
单选按钮组方向键选择选项
选择框/下拉菜单方向键浏览;按 Enter 进行选择
对话框Escape关闭
对话框Tab仅在对话框内循环切换
菜单方向键浏览菜单项
菜单Escape关闭
菜单Enter / Space激活菜单项
滑块方向键增大/减小
滑块Home / End最小值 / 最大值
标签页方向键切换标签页
树形控件方向键展开/折叠/导航
模态框的焦点陷阱
// Trap focus within modal while open
function trapFocus(modal) {
  const focusable = modal.querySelectorAll(
    'a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])'
  );
  const firstEl = focusable[0];
  const lastEl = focusable[focusable.length - 1];

  modal.addEventListener('keydown', (e) => {
    if (e.key !== 'Tab') return;
    if (e.shiftKey) {
      if (document.activeElement === firstEl) {
        e.preventDefault(); lastEl.focus();
      }
    } else {
      if (document.activeElement === lastEl) {
        e.preventDefault(); firstEl.focus();
      }
    }
  });
}

5.5 无障碍组件模式
模态框 / 对话框
<dialog
  role="dialog"
  aria-modal="true"
  aria-labelledby="modal-title"
  aria-describedby="modal-desc"
  id="my-modal"
>
  <h2 id="modal-title">Confirm Action</h2>
  <p id="modal-desc">Are you sure you want to delete this item?</p>
  <button type="button" autofocus>Cancel</button>
  <button type="button">Delete</button>
</dialog>
// HTML5 <dialog> — built-in focus management + Escape handling
const modal = document.getElementById('my-modal');
const trigger = document.getElementById('open-btn');

trigger.addEventListener('click', () => modal.showModal());
modal.addEventListener('close', () => trigger.focus()); // Restore focus
无障碍按钮(自定义)

<button type="button" aria-pressed="false" id="dark-toggle">
  <svg aria-hidden="true" focusable="false">…</svg>
  Dark Mode
</button>


<button type="button" aria-label="Close dialog">
  <svg aria-hidden="true" focusable="false">…</svg>
</button>
手风琴
<div class="accordion">
  <h3>
    <button
      type="button"
      aria-expanded="false"
      aria-controls="panel-1"
      id="btn-1"
    >Section Title</button>
  </h3>
  <div id="panel-1" role="region" aria-labelledby="btn-1" hidden>
    Panel content here
  </div>
</div>
标签页
<div role="tablist" aria-label="Settings sections">
  <button role="tab" aria-selected="true" aria-controls="panel-a" id="tab-a" tabindex="0">General</button>
  <button role="tab" aria-selected="false" aria-controls="panel-b" id="tab-b" tabindex="-1">Privacy</button>
</div>

<div role="tabpanel" id="panel-a" aria-labelledby="tab-a">…</div>
<div role="tabpanel" id="panel-b" aria-labelledby="tab-b" hidden>…</div>
组合框 / 自动补全
<label for="city-input">City</label>
<input
  type="text"
  id="city-input"
  role="combobox"
  aria-expanded="false"
  aria-haspopup="listbox"
  aria-autocomplete="list"
  aria-controls="city-list"
/>
<ul id="city-list" role="listbox" aria-label="Cities">
  <li role="option" id="opt-1">New York</li>
  <li role="option" id="opt-2">Los Angeles</li>
</ul>
提醒 / 状态消息

<div role="status" aria-live="polite" aria-atomic="true">
  Form saved successfully.
</div>


<div role="alert" aria-live="assertive" aria-atomic="true">
  Error: Please enter a valid email address.
</div>

5.6 表单 — 无障碍模式

<div class="field">
  <label for="email">
    Email address
    <span aria-hidden="true" class="required">*</span>
    <span class="sr-only">(required)</span>
  </label>
  <input
    type="email"
    id="email"
    name="email"
    autocomplete="email"
    aria-required="true"
    aria-describedby="email-hint email-error"
    aria-invalid="false"
  />
  <p id="email-hint" class="hint">We'll never share your email.</p>
  <p id="email-error" class="error" role="alert" hidden>
    Please enter a valid email address.
  </p>
</div>


<fieldset>
  <legend>Notification preferences</legend>
  <label><input type="radio" name="notif" value="email"> Email</label>
  <label><input type="radio" name="notif" value="sms"> SMS</label>
  <label><input type="radio" name="notif" value="none"> None</label>
</fieldset>

表单验证规则:

  • 在 blur 或 submit 时进行验证,而不是每次击键时都验证
  • 使用 aria-invalid="true" 标记无效字段
  • 通过 aria-describedby 将错误消息关联到字段
  • 对动态插入的错误消息使用 role="alert"
  • 切勿仅使用占位符文本作为标签
  • 为常见字段提供 autocomplete 属性

5.7 图片和替代文本

<img src="chart.png" alt="Sales by region 2024: North 35%, South 28%, East 22%, West 15%">


<img src="divider.png" alt="">


<button aria-label="Search">
  <img src="magnifier.svg" alt="">
</button>


<figure>
  <img src="architecture.png" alt="System architecture diagram" aria-describedby="arch-desc">
  <figcaption id="arch-desc">
    Three-tier architecture: client tier connects to API gateway,
    which routes to microservices cluster, backed by PostgreSQL and Redis.
  </figcaption>
</figure>


<div class="hero-bg" role="img" aria-label="Abstract blue wave">…</div>

替代文本指南:

  • 保持简洁(通常不超过 125 个字符;复杂内容最多 250 个字符)
  • 不要以“图片”或“图像”开头
  • 描述用途和内容,而不只是外观
  • 对于图表:包含关键数据点
  • 对于徽标:仅使用公司名称

5.8 排版无障碍性
/* ✅ Accessible base typography */
html {
  font-size: 100%; /* Don't fix to px — respects user browser setting */
}

body {
  font-size: 1rem;      /* = 16px by default */
  line-height: 1.5;     /* Readable line spacing */
  max-width: 75ch;      /* Limit line length for readability */
  font-family: /* Choose a readable, designed typeface */;
}

/* ✅ Allow text spacing override (WCAG 1.4.12) */
* {
  line-height: inherit !important;    /* only in text-spacing tests */
  letter-spacing: inherit !important;
  word-spacing: inherit !important;
}

/* ✅ Respect zoom — use relative units */
h1 { font-size: clamp(1.5rem, 4vw, 2.5rem); }
p  { font-size: clamp(1rem, 1.2vw, 1.125rem); }
指南最低要求建议值
正文文字大小14px(0.875rem)16px(1rem)
行高1.41.5–1.6
最大行长—65–75 个字符
字母间距—0 至 +0.05em
段落间距—字号的 1.5 倍

无障碍字体选择:

  • 选择字形区分清晰的字体(能够区分 b/d/p/q)
  • 正文避免使用装饰性字体
  • 小字号时避免使用较细字重(< 300)
  • 确保 x-height 足够高,以提高可读性

5.9 动效和动画
/* ✅ Respect prefers-reduced-motion — implement always */
@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;
  }
}

/* ✅ Progressive enhancement pattern */
.card { /* base — no motion */ }

@media (prefers-reduced-motion: no-preference) {
  .card {
    transition: transform 0.2s ease, box-shadow 0.2s ease;
  }
  .card:hover {
    transform: translateY(-4px);
    box-shadow: 0 8px 24px rgba(0,0,0,0.12);
  }
}

动效规则:

  • 切勿自动播放带有动态效果的视频 — 使用 prefers-reduced-motion
  • 闪烁内容:每秒最多闪烁 3 次(WCAG 2.3.1)
  • 视差效果、整页滚动劫持 → 提供 prefers-reduced-motion: reduce 替代方案
  • 加载动画可以接受;如果没有替代方案,则不应使用整页动画过渡

5.10 响应式和移动端无障碍性
/* ✅ WCAG 1.4.10 Reflow — no horizontal scroll at 320px */
@media (max-width: 320px) {
  .container { width: 100%; padding: 0 1rem; }
  table { display: block; overflow-x: auto; }
}

/* ✅ WCAG 2.5.8 Minimum target size — 24×24px with spacing */
button, a, [role="button"] {
  min-height: 44px;      /* Meet WCAG 2.5.5 AAA for comfort */
  min-width: 44px;
  padding: 0.5rem 1rem;
}

/* ✅ Ensure focus remains visible at all sizes */
:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 3px;
}

移动端注意事项:

  • 使用适当的 type 属性:type="email"、type="tel"、type="number"
  • 不要禁用缩放:<meta name="viewport" content="width=device-width, initial-scale=1"> — 切勿添加 user-scalable=no
  • 使用 VoiceOver(iOS)和 TalkBack(Android)进行测试
  • 避免仅通过悬停触发的交互(触摸设备不支持悬停)
  • 对数字、小数、电话、电子邮件、网址、搜索输入使用 inputmode 属性

5.11 表格

<table>
  <caption>Q4 2024 Sales by Region</caption>
  <thead>
    <tr>
      <th scope="col">Region</th>
      <th scope="col">Sales (USD)</th>
      <th scope="col">Growth</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">North America</th>
      <td>$1,240,000</td>
      <td>+12%</td>
    </tr>
  </tbody>
  <tfoot>
    <tr>
      <th scope="row">Total</th>
      <td>$3,890,000</td>
      <td>+8%</td>
    </tr>
  </tfoot>
</table>

规则:

  • 使用 <caption> 为每个表格添加标题
  • 使用 <th scope="col|row|colgroup|rowgroup"> 标记所有表头单元格
  • 使用 <thead>、<tbody>、<tfoot> 构建结构
  • 不要用表格进行布局;改用 CSS Grid/Flexbox

第 6 部分 — 以无障碍性为导向的设计:前端美学指南

6.1 面向无障碍界面的设计思维

编码之前,先确立一个大胆、有意为之的美学方向,并将无障碍纳入规划:

  • 目的:要解决什么问题?谁会使用它?用户可能有哪些残障?
  • 基调:选择一种极致的美学风格:极简主义、极繁字体风、自然风、编辑风、粗野主义、复古未来主义、奢华精致风、趣味风、工业风等。
  • 约束:框架、性能、WCAG 级别(目标为 AA)、对比度要求
  • 差异化:是什么让它令人难忘且无障碍?大胆的设计与 WCAG 并不冲突。

无障碍能为设计提供指引,而不是限制设计。优秀的无障碍设计会将以下要素融入设计:

  • 将强对比度作为设计特色,而非负担
  • 将清晰的字体层级作为视觉系统
  • 将醒目的焦点状态作为有意设计的元素
  • 将语义结构作为构图逻辑
6.2 无障碍色彩系统设计

设计无障碍色彩系统:

:root {
  /* Accessible palette using HSL for easier contrast calculation */
  --color-bg: hsl(220, 15%, 10%);        /* Near-black background */
  --color-surface: hsl(220, 12%, 15%);   /* Elevated surface */
  --color-border: hsl(220, 10%, 25%);    /* Subtle border */
  --color-text-primary: hsl(220, 10%, 95%);   /* High contrast text: >7:1 */
  --color-text-secondary: hsl(220, 8%, 70%);  /* Medium contrast: ~4.5:1 */
  --color-text-muted: hsl(220, 6%, 55%);      /* Low contrast: ~3:1 (large text only) */
  --color-accent: hsl(210, 100%, 65%);        /* Action color: verify contrast */
  --color-accent-hover: hsl(210, 100%, 72%);  /* Hover state */
  --color-error: hsl(0, 80%, 65%);            /* Error — verify 4.5:1 on bg */
  --color-success: hsl(145, 60%, 55%);        /* Success — verify 4.5:1 on bg */
  --color-warning: hsl(45, 90%, 55%);         /* Warning — verify 4.5:1 on bg */

  /* Focus ring — must contrast 3:1 with adjacent colors (WCAG 2.4.7) */
  --color-focus: hsl(210, 100%, 65%);
  --focus-ring: 0 0 0 3px var(--color-focus);
}

/* ✅ Color-scheme support */
@media (prefers-color-scheme: light) {
  :root {
    --color-bg: hsl(0, 0%, 99%);
    --color-text-primary: hsl(220, 20%, 8%);
    /* ... recalculate all contrasts */
  }
}

多模态传达(绝不只依赖颜色):

/* ❌ Color only for error */
.input-error { border-color: red; }

/* ✅ Color + icon + text + pattern */
.input-error {
  border-color: var(--color-error);
  border-width: 2px;
  background-image: url("error-icon.svg");
  background-position: right 12px center;
  background-repeat: no-repeat;
  padding-right: 40px;
}
6.3 字体排版设计系统
/* ✅ Accessible, distinctive type scale */
:root {
  /* Scale: Major Third (1.25) or Perfect Fourth (1.333) */
  --text-xs:   0.75rem;    /* 12px — captions, labels */
  --text-sm:   0.875rem;   /* 14px — secondary text */
  --text-base: 1rem;       /* 16px — body */
  --text-lg:   1.125rem;   /* 18px — lead text */
  --text-xl:   1.25rem;    /* 20px */
  --text-2xl:  1.5rem;     /* 24px */
  --text-3xl:  1.875rem;   /* 30px */
  --text-4xl:  2.25rem;    /* 36px */
  --text-5xl:  3rem;       /* 48px */
  --text-6xl:  3.75rem;    /* 60px */

  /* Accessible line heights */
  --leading-tight:  1.25;
  --leading-snug:   1.375;
  --leading-normal: 1.5;
  --leading-relaxed:1.625;
  --leading-loose:  2;

  /* Weights */
  --font-light:    300;  /* Use sparingly, large sizes only */
  --font-normal:   400;
  --font-medium:   500;
  --font-semibold: 600;
  --font-bold:     700;
  --font-black:    900;
}

选择兼具独特性与无障碍性的字体:

  • 避免将 Inter、Roboto、Arial、system-ui 用于展示文字——这些字体过于普通
  • 选择光学尺寸范围较大的字体:DM Sans、Fraunces、Bricolage Grotesque、 Playfair Display、Syne、General Sans、Cabinet Grotesk、Instrument Serif
  • 将独特的展示字体与简洁易读的正文字体搭配使用
  • 确认所选字体在正文字号(14–18px)下清晰易读
  • 始终准备一组易读的后备字体
6.4 将焦点指示器作为设计元素

将焦点指示器设计为有意为之的设计特色,而不是事后补上的元素:

/* Option 1: Solid outline */
:focus-visible {
  outline: 3px solid var(--color-accent);
  outline-offset: 3px;
  border-radius: var(--radius-sm, 4px);
}

/* Option 2: Box shadow (inset for contained elements) */
:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--color-bg), 0 0 0 5px var(--color-accent);
}

/* Option 3: High visibility for text links */
a:focus-visible {
  outline: none;
  background-color: var(--color-accent);
  color: var(--color-bg);
  text-decoration: none;
  border-radius: 2px;
  padding: 0 2px;
}

/* Verify contrast: focused vs unfocused → must be ≥3:1 change */
6.5 面向操作无障碍的间距与布局
/* ✅ Generous interactive targets */
.btn {
  /* Minimum visually 44×44 (WCAG 2.5.5 AAA) */
  min-height: 44px;
  padding-inline: 1.25rem;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}

/* ✅ Inline links — guaranteed minimum height */
a {
  display: inline-block; /* Ensures tap area */
  padding: 0.25em 0;
}

/* ✅ Spacing between small targets (WCAG 2.5.8 AA) */
.icon-group { gap: 0.5rem; } /* 8px between 24px targets = sufficient */

/* ✅ Grid and flex for logical order */
/* Visual order must match DOM order for keyboard/screen reader users */
/* Avoid using CSS `order` property to reorder visual layout */
6.6 无障碍动画设计
/* ✅ Always implement reduced-motion alternative */
:root {
  --duration-fast: 150ms;
  --duration-base: 250ms;
  --duration-slow: 400ms;
  --easing-out: cubic-bezier(0.16, 1, 0.3, 1);
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --duration-fast: 0ms;
    --duration-base: 0ms;
    --duration-slow: 0ms;
  }
}

/* ✅ Purpose-driven micro-interactions */
.btn {
  transition:
    background-color var(--duration-fast) ease,
    transform var(--duration-fast) var(--easing-out),
    box-shadow var(--duration-fast) ease;
}

.btn:hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}

.btn:active {
  transform: translateY(0);
  box-shadow: none;
}

第 7 部分 — 测试清单

快速检查(5 分钟)
  • 使用 Tab 键遍历整个页面——能否访问并激活所有内容?
  • 焦点指示器是否始终可见?
  • 使用浏览器 DevTools 检查颜色对比度
  • 运行 axe DevTools 或 Lighthouse 无障碍审计
  • 检查禁用 CSS 后的页面(是否能看到语义结构?)
标准检查(WCAG 2.2 AA)

可感知

  • 所有图像都有有意义的替代文本(装饰性图像使用 alt="")
  • 颜色对比度:普通文本 ≥4.5:1,大号文本和 UI 元素 ≥3:1
  • 不只依赖颜色传递信息
  • 在 200% 缩放下,内容仍可读且功能正常
  • 页面宽度为 320px 时无水平滚动(内容可重排)
  • 覆盖文本间距不会破坏布局
  • 不播放无控制项的自动播放音频

可操作

  • 所有功能都可通过键盘访问
  • 无键盘陷阱
  • 焦点指示器始终可见
  • 焦点不会被粘性页眉或页脚遮挡
  • 提供跳过导航链接
  • 所有页面都有描述性的 <title>
  • 焦点顺序符合逻辑
  • 链接文本清晰且具有描述性
  • 触控目标至少为 24×24px,且间距充足
  • 内容不会以每秒超过 3 次的频率闪烁

易于理解

  • 正确设置 <html lang="en">
  • 使用 lang 属性标记语言变更
  • 焦点变化不会意外改变上下文
  • 各页面的导航保持一致
  • 所有表单输入框都有可见标签
  • 错误得到识别和说明,并提供解决建议
  • 帮助机制位于一致的位置

稳健

  • 所有表单控件都通过 HTML 或 ARIA 声明名称、角色和值
  • 状态消息无需改变焦点即可播报
  • HTML 有效且结构规范(无嵌套错误)
  • 正确使用 ARIA 角色和状态
  • 动态内容更新会被播报
屏幕阅读器测试

使用以下组合测试:NVDA + Chrome(Windows)、VoiceOver + Safari(Mac)、TalkBack + Chrome(Android)

  • 标题结构便于导航且逻辑清晰
  • 地标便于导航(nav、main、aside、footer)
  • 表单标签能够正确朗读
  • 字段无效时会播报错误消息
  • 会播报动态内容(警报、状态)
  • 模态框:限制焦点、按 Escape 关闭、焦点返回原处

第 8 部分 — 法律与标准背景

标准司法管辖区依据
ADA(《美国残疾人法案》)美国WCAG 2.1 AA
Section 508美国(联邦)WCAG 2.0 AA
EN 301 549欧盟WCAG 2.1 AA
AODA加拿大安大略省WCAG 2.0 AA
Equality Act 2010英国WCAG 2.1 AA
BS 8878英国WCAG 2.1
BITV 2.0德国WCAG 2.1 AA

建议目标:WCAG 2.2 AA — 超过所有主要法律的要求,并能适应未来发展。


第 9 部分 — 工具与资源

测试工具
工具类型用途
axe DevTools浏览器扩展 + CI最全面的自动化检查
WAVE(WebAIM)浏览器扩展可视化错误叠加层
LighthouseChrome DevTools快速基线审计
Colour Contrast Analyser桌面应用像素级精准对比度检查
WebAIM Contrast CheckerWeb 工具快速计算对比度
NVDA屏幕阅读器Windows 屏幕阅读器测试(免费)
VoiceOver屏幕阅读器Mac/iOS 内置
TalkBack屏幕阅读器Android 内置
Keyboard手动始终测试仅使用键盘的导航
重要参考资料

总结:不可妥协的 AA 规则

构建任何前端界面时,始终必须遵守以下 WCAG 2.2 AA 规则:

  1. 对比度:普通文本为 4.5:1,大号文本和 UI 组件为 3:1
  2. 替代文本:每张信息性图片都要有描述性替代文本;装饰性图片使用 alt=""
  3. 键盘:100% 的功能都可仅通过键盘访问和操作
  4. 焦点可见:焦点指示器必须清晰可见,且绝不能移除
  5. 标签:每个表单输入项都必须关联一个可见的 <label>
  6. 错误:以文本指出错误,不能仅依靠颜色;使用 aria-invalid;提供建议
  7. 标题:层级逻辑清晰,不跳过级别
  8. 语言:始终设置 <html lang="...">
  9. 语义化 HTML:使用 <button>、<a>、<nav>、<main> 等,不要用 <div> 替代
  10. 信息不能仅靠颜色传达:绝不能只用颜色传达任何信息
  11. 重排:内容在 320px 宽度下仍可使用,无需水平滚动
  12. 触控目标:交互元素至少为 24×24px,或提供充足间距
  13. 焦点不被遮挡:粘性页眉/页脚不能遮挡获得焦点的元素
  14. 状态消息:向屏幕阅读器播报动态状态/错误消息
  15. 帮助一致:各页面上的帮助机制应位于相同位置

评论

支持 Markdown