ARIA属性不是“加了就无障碍”,而是仅在HTML原生语义无法表达行为或状态时精准补位;必须优先使用<button><nav><main>等原生标签,严格同步JS状态,避免滥用role和aria-hidden。

用 role 和 aria- 前缀前先确认是否真需要
绝大多数现代框架(React、Vue、Svelte)默认不阻止原生语义化标签的可访问性行为,但开发者常因“习惯封装”或“样式驱动结构”而绕过 <button>、<nav>、<main> 等原生元素,改用 <div> + role="button"。这反而破坏了键盘焦点、屏幕阅读器隐式语义和浏览器默认行为。
真正需要 role 的场景其实很窄:比如自定义下拉菜单中无法用 <select> 实现的复杂控件,或动态渲染后需补足缺失的容器语义(如 role="region" 配合 aria-labelledby)。其他情况优先选原生标签——它们自带 focusable、Enter/Space 触发、disabled 状态处理和无障碍树节点。
- 用
<button type="button">代替<div role="button" tabindex="0"> - 导航区域用
<nav>,别用<div role="navigation"> -
aria-hidden="true"只用于纯粹装饰性元素(如图标字体),不能用于隐藏交互内容
React/Vue 中事件处理器必须配 keyboard 支持
框架里写 onClick 或 @click 很自然,但屏幕阅读器用户和键盘用户依赖 onKeyPress 或 onKeyDown 模拟点击逻辑。仅靠鼠标事件会让键盘用户卡在可聚焦却不可触发的元素上。
典型错误是给一个 <div @click="handleClick"> 加 tabindex="0",却不监听 Enter 或 Space。这导致元素能获得焦点,但按空格键无响应——AT(辅助技术)会报“不可操作”,而非“已禁用”。
立即学习“前端免费学习笔记(深入)”;
Orderly React SDK 钩子使用参考指南,包括 useOrderEntry、usePositionStream、useOrderbookStream、useCollateral 等。
- React:同时绑定
onClick和onKeyPress,并在后者中判断e.key === 'Enter' || e.key === ' ' - Vue:用
@click.prevent+@keydown.enter.prevent+@keydown.space.prevent - Svelte:用
on:click和on:keydown|preventDefault,检查event.key
动态内容更新必须触发 aria-live 区域
框架中状态变化引发的 DOM 更新(如搜索结果加载、表单校验提示、通知弹出)对屏幕阅读器是“不可见”的,除非显式声明该区域为实时区域。否则用户完全不知道内容已变,尤其当焦点未落在更新区域时。
aria-live 不是装饰属性,它直接控制 AT 是否播报、何时播报、播报优先级。值选错会导致静默(off)、打断当前播报(assertive)或延迟播报(polite)。
- 表单校验错误提示:用
aria-live="polite",避免打断用户输入 - 全局通知(如“保存成功”):用
aria-live="assertive",确保立即播报 - 不要把
aria-live放在组件根节点上——它应紧邻动态内容,且保持 DOM 稳定(避免重渲染导致区域被销毁) - React 中可用
useEffect+ref手动更新textContent,比依赖框架 diff 更可靠
SSR/SSG 场景下 useEffect 里的可访问性代码会失效
Next.js、Nuxt、SvelteKit 等服务端渲染框架中,useEffect、onMount 或 mounted 钩子只在客户端执行。这意味着所有依赖这些钩子注入的 aria- 属性、tabindex 或 aria-live 初始化,对初始 HTML 完全无效——搜索引擎、Lighthouse、部分 AT 在首屏就失败。
解决办法不是“等 hydration 完再修复”,而是让可访问性属性在 SSR 输出中就存在。例如:焦点管理不能靠 useEffect(() => ref.focus()),而应直接在 HTML 中用 autofocus;动态 aria-expanded 不能靠 JS 切换,而应由服务端根据初始状态渲染正确值。
- 避免在
useEffect里设置aria-*属性,改用服务端 props 或静态初始值 -
autofocus是唯一被 SSR 支持的焦点控制属性,其他都得靠 JS,但必须兼容无 JS 场景(如 fallback 提示) - Lighthouse 的“Accessibility”审计跑在初始 HTML 上,不是 hydration 后,这点容易被忽略


















