无障碍与 WCAG 2.2 合规
构建满足 WCAG 2.2 AA 合规的无障碍 Web 界面的实用指南。
无障碍入门
为什么
无障碍界面排斥了 15% 的残障用户,产生法律风险(ADA、EAA),并降低 SEO 因为搜索引擎无法解析 ARIA 误用的内容。无障碍不是可选项。
WebAIM Million 2024 报告发现 96.3% 的首页存在可检测的 WCAG 失败。2023 年 ADA 相关诉讼超过 4,500 起,使无障碍成为法律和伦理必需,而非可有可无。
何时使用
始终。无障碍必须从第一个组件开始内建,而非上线前补加。如果现有 UI 未考虑无障碍,应立即审计。
如何做
1. 语义 HTML — 使用 <button>、<nav>、<main>、<label> 而非 <div onClick>
2. 键盘导航 — 每个可交互元素必须可聚焦可操作
- Tab 顺序遵循视觉顺序(不用正 tabindex)
- Enter/Space 激活按钮,Escape 关闭对话框
3. ARIA — 仅在语义 HTML 不足时使用
- aria-label 用于纯图标按钮
- aria-expanded 用于可折叠区域
- role="dialog" + aria-modal="true" 用于模态框
4. 颜色对比 — 文本最低 4.5:1,大文本和 UI 组件 3:1
5. 焦点管理 — 模态框内陷阱焦点,关闭时恢复焦点
6. 测试 — CI 中 axe-core,用 NVDA/VoiceOver 手动屏幕阅读器测试
示例
// 无障碍模态对话框
function Modal({ open, onClose, title, children }: ModalProps) {
const dialogRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!open) return;
// 陷阱焦点 + 关闭时恢复
const previouslyFocused = document.activeElement as HTMLElement;
dialogRef.current?.focus();
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === "Escape") onClose();
// 焦点陷阱逻辑...
};
document.addEventListener("keydown", handleKeyDown);
return () => {
document.removeEventListener("keydown", handleKeyDown);
previouslyFocused?.focus(); // 恢复焦点
};
}, [open, onClose]);
if (!open) return null;
return (
<div role="dialog" aria-modal="true" aria-labelledby="modal-title"
ref={dialogRef} tabIndex={-1} className="fixed inset-0 z-50">
<h2 id="modal-title">{title}</h2>
{children}
<button onClick={onClose} aria-label="关闭对话框">X</button>
</div>
);
}
常见问题
- ⚠在 <div> 上用 role='button' — 应使用真正的 <button> 以支持键盘
- ⚠aria-label 覆盖可见文本 — 屏幕阅读器丢失按钮文本内容
- ⚠正 tabindex(tabindex='1')— 破坏自然 tab 顺序,用 tabindex='-1' 或 '0'
- ⚠缺少 focus-visible 样式 — 键盘用户看不到焦点位置
最佳实践
- ✓优先语义 HTML 而非 ARIA — ARIA 是最后手段,不是首选
- ✓CI 中运行 axe-core — 新违规时构建失败,而非仅警告
- ✓用真实屏幕阅读器测试 — 自动化工具最多发现 40% 的问题
- ✓使用 prefers-reduced-motion 媒体查询 — 为前庭敏感者禁用动画
常见问题解答
每个元素都需要 ARIA 才能无障碍吗?
不需要——优先语义 HTML(<button>、<nav>、<label>)。ARIA 是 HTML 无法表达模式时的最后手段。滥用 ARIA 往往更糟。
WCAG 2.2 应目标哪个级别?
AA 是法律和实践基线(ADA、EAA 合规)。全产品达 AA,关键流程如结账可行时达 AAA。
自动化工具之外如何测试无障碍?
axe-core 约发现 40% 问题。其余用真实屏幕阅读器(NVDA/VoiceOver)和纯键盘导航测试——每个可交互元素必须可达可操作。
颜色对比度需要多少?
普通文本 4.5:1,大文本(≥18px 或 14px 粗体)和 UI 组件 3:1。在设计工具里用对比检查器,别只靠目测。
模态框如何管理焦点?
打开时焦点陷阱在模态内,关闭时恢复到触发元素。用 aria-modal='true' 并监听 Escape 关闭。