<main>标签自带role="main"语义,手动添加属冗余污染;原生button无需role="button",否则破坏默认行为;仅在IE11等旧环境或模板限制时才需div role="main"且须保证唯一。

别给 main 加 role="main",也别给 button 加 role="button"——这不是增强可访问性,是制造语义污染。
为什么 main 标签上写 role="main" 是错的
浏览器解析 main 时,强制注入 role="main" 到无障碍树,这是 HTML5 规范硬性要求。手动重复添加不会提升兼容性,旧版读屏器(如 IE11 + JAWS)识别的是标签名本身,不是你写的属性。
- NVDA 2018 以前版本可能因双重声明跳过该 landmark,导致用户无法用快捷键导航到主内容
- 团队看到
role="main"会默认你在降级处理,怀疑 SSR 失败或误用了div - axe-core 等工具虽不报 error,但标记为 “ARIA 属性冗余” warning,暴露协作理解偏差
- Next.js 的
app/layout.tsx里放main是典型结构误用——它必须落在app/page.tsx或路由组件内,否则 VoiceOver 可能直接跳过
role="button" 不等于“能点”,只加这个属性根本没用
给一个 div 加 role="button",屏幕阅读器会朗读为“按钮”,但键盘用户按 Tab 进不去、按 Space/Enter 没反应、焦点样式也不出现——等于挂了块假招牌。
- 必须配
tabindex="0",否则不在标准 Tab 流中 - 必须监听
onkeydown,显式处理 Enter 和 Space(注意:Space 要preventDefault(),否则触发页面滚动) - 禁用状态不能只靠 CSS 灰显,得用
aria-disabled="true"+ 键盘事件拦截 - 原生
button绝对不要加role="button"——部分旧版 NVDA 会识别为“未知按钮”,反而破坏默认行为
哪些 role 值真能放心用,且必须配套 aria-*
WAI-ARIA 定义了 60+ 个 role,但日常开发中稳定、兼容、AT 支持好的不到 10 个。关键不是“能不能加”,而是“加了之后是否同步实现了对应行为和状态通知”。
立即学习“前端免费学习笔记(深入)”;
-
role="alert":用于中断性提示,需配合动态插入或aria-live="assertive",IE11 对运行时添加支持极差 -
role="dialog"+aria-modal="true":缺一不可,否则焦点逃逸、背景仍可操作 -
role="navigation":比单用nav多一层老版本 JAWS 兼容保障,但不必在nav上重复加 -
role="treeitem"必须配aria-expanded、aria-level、aria-setsize,否则读屏不知道它是否可展开、在哪一层、有多少兄弟节点
真正容易被忽略的约束:结构合法性比 role 冗余更致命
比起要不要加 role,更常踩坑的是元素嵌套是否合法。浏览器不报错,但 VoiceOver、NVDA 可能直接跳过整个区域。
-
main不能是header、footer、nav、aside的子元素——哪怕 DOM 结构看起来合理 - SSR hydration 不一致时(服务端渲染了
main,客户端 React 却删了它),无障碍树会残留空壳,读屏找不到主内容 - 用
div role="main"只在两种真实场景下必要:支持 IE11 / Android 4.4 WebView 且不能 polyfill;或模板系统无法动态输出main标签——且必须保证全局唯一



















