{T}

组件无障碍 a11y

学习目标

  • 掌握语义化标签优先于 ARIA 的原则
  • 理解 aria-* 属性的典型用法
  • 掌握自定义组件的键盘交互与焦点管理

语义化优先

能用原生元素就别用 div 模拟: <button> 自带键盘可聚焦与回车/空格触发,远胜 <div onClick>。读屏软件依赖语义角色(role)与名称(accessible name)来播报。

jsx
// ❌ 读屏无法识别这是可点击的
<div onClick={onClick}>删除</div>
// ✅ 自带 role=button、可聚焦、键盘触发
<button onClick={onClick}>删除</button>

ARIA 何时用

当原生语义不够时再用 aria-*

  • aria-label / aria-labelledby:给无文本控件提供可访问名称;
  • aria-expanded:标记折叠状态;
  • aria-live="polite":动态内容变化时通知读屏(如提示文案);
  • role="dialog" + aria-modal:对话框。
jsx
<span aria-live="polite">{message}</span>

键盘交互与焦点

自定义组件(如下拉、模态)必须实现键盘行为:

  • 模态打开时把焦点移入、关闭时归还触发元素(useRef + useEffect);
  • Escape 关闭模态;
  • 列表类组件实现方向键 / Home / End 导航(roving tabindex);
  • onKeyDown 而非 onClick 处理键盘(同时保留点击)。
jsx
function Modal({ onClose, children }) {
  const ref = useRef(null);
  useEffect(() => { ref.current?.focus(); return () => triggerRef.current?.focus(); }, []);
  return <div role="dialog" aria-modal="true" ref={ref} onKeyDown={e => e.key === 'Escape' && onClose()}>{children}</div>;
}

与表单的结合

错误提示用 aria-describedby 关联到输入框,读屏会在聚焦时朗读错误:

jsx
<input aria-describedby="err" aria-invalid={!!err} />
<span id="err">{err}</span>

总结

a11y 的核心是「语义优先、ARIA 补位、键盘可达、焦点可控」。它应作为组件验收标准,而非事后补救——这与 33-错误边界实践进阶 同属「生产级组件」的必备素养。

继续阅读