四、ARIA标签与角色归纳总结
ARIA 这玩意儿,说白了就是给 HTML 元素“补户口”的。很多原生元素,比如按钮、输入框,浏览器自己就能告诉辅助技术“我是个啥,我能干啥”。但一旦你用了 <div> 做按钮,用 <span> 做滑块,浏览器就懵了——它不知道这些东西到底要干嘛。ARIA 就是来填这个坑的。
我最早接触 ARIA 是在一个国际化后台系统里。产品经理说“盲人用户也要能用”,我当时心里想:加几个 role 和 aria-label 不就行了?后来踩过坑才明白,ARIA 用错了比不用更可怕。
4.1 ARIA 角色分类
WAI-ARIA 1.2 定义了三大类角色,你想想看,就像公务员系统里的编制:
- Landmark(地标角色)——给页面分区的,比如
banner、navigation、main、contentinfo。屏幕阅读器用户可以跳着读,不用从头扫到尾。 - Widget(小部件角色)——用来做交互组件,比如
button、slider、tab、dialog。它告诉辅助技术“这个东西能点、能拖、能切换”。 - Document Structure(文档结构角色)——介于两者之间,比如
article、section、heading。它们帮用户理解内容层次。
我个人习惯把角色理解成“标签的职责”。比如 role="navigation" 就是告诉屏幕阅读器“这里是导航区,你可以跳过或直接定位”。
| 角色 | 分类 | 用途 |
|---|---|---|
banner | Landmark | 网站头部(通常包含logo和站点名) |
navigation | Landmark | 主要导航区域 |
main | Landmark | 页面主要内容区 |
button | Widget | 可点击的按钮(非原生按钮) |
tab | Widget | 标签页组件里的单个标签 |
dialog | Widget | 模态对话框 |
article | Document Structure | 独立的内容单元(如博客文章) |
4.2 ARIA 属性与状态
角色告诉“你是什么”,属性和状态告诉“你现在的细节”。比如 aria-label 给元素一个可读的名称,aria-expanded 告诉用户某个区域是否展开。
核心原则:能用原生 HTML 语义标签,就尽量不用 ARIA。<button> 天然就是 button 角色,你不需要再写 role="button"。ARIA 是兜底方案,不是首选。
记得有一次我接手一个国际化项目,里面有个自定义下拉框,用 <div> 做的。视觉上完全没问题,但屏幕阅读器读出来就是“div,collapse,group”。用户根本不知道这是个可选择的下拉菜单。后来加了 role="combobox"、aria-expanded="false" 和 aria-controls,才算是正经能用了。
常用的属性/状态我列了个清单:
- 标签类:
aria-label、aria-labelledby、aria-describedby - 关系类:
aria-controls、aria-owns、aria-activedescendant - 状态类:
aria-expanded、aria-pressed、aria-selected、aria-disabled - 实时区域:
aria-live、aria-relevant、aria-atomic
避坑技巧:我曾经在动态加载列表时忘了给容器加 aria-live="polite",结果屏幕阅读器完全没反应,用户以为页面死掉了。后来在 CI 里加了 axe-core 检查这种遗漏。
4.3 实战:在 CI 中自动校验 ARIA
你想想看,光靠人工 review 哪能记得住所有规则。我在 CI 流程里集成两个工具:@axe-core/puppeteer(浏览器级检查)和 eslint-plugin-jsx-a11y(代码级检查)。
一个典型的 CI 配置示例(.github/workflows/a11y.yml):
name: Accessibility Check
on: [pull_request]
jobs:
a11y:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npx axe-puppeteer http://localhost:3000
env:
CI: true
不过要注意,axe 默认规则集可能漏掉一些国际化相关的 ARIA 用法。比如 aria-label 里如果写了英文,但页面语言是中文,这其实是一种隐性的无障碍问题。我额外加了一条规则检查 aria-label 的语言是否匹配 lang 属性。
4.4 常见错误与最佳实践
- 滥用 role="presentation":把语义元素变成纯表现层,会让导航信息丢失。慎用。
- 忘记更新 aria-expanded:手风琴组件展开后状态不变,辅助技术读到的还是闭合。建议用 JS 强制同步。
- aria-label 替代了可见文本:如果按钮上已经有文字了,别再加 aria-label,会覆盖掉屏幕阅读器的自然读取顺序。
- tabindex 与角色冲突:比如
role="button"但没加tabindex="0",键盘完全无法聚焦。
警告:aria-hidden="true" 会从无障碍树中完全移除元素。我曾见过有人把整个侧边栏都 hidden 了,结果用户直接忽略了一大块导航。只在真正不需要展示时用。
4.5 知识框架总览
下面这张图把 ARIA 的核心要素串起来了。从角色、属性、状态出发,最终映射到浏览器生成的无障碍树。你可以在 CI 环节对照这张图去检查自己的组件。
这张图告诉你一个关键点:角色、属性、状态三者共同决定了元素在无障碍树里的最终表现。任何一个环节缺失或错误,都可能让辅助技术“读不懂”。
4.6 小结
ARIA 不是魔法,它只是 HTML 语义化的补充。用好它的核心原则就三条:能原生就别用 ARIA、用了就必须保持同步更新、在 CI 里自动检查比人工 review 靠谱得多。
我自己的项目里,我会把 ARIA 检查的阈值设成 error 阻断 CI 通过。一开始大家嫌烦,但习惯了之后,改掉的 bug 至少有三分之一都是 ARIA 相关。你想想看,这点代价换来的是对所有用户的公平,值不值?