html5语义标签自带隐式aria角色,无需额外role属性;多个必须用aria-label区分;仅当无原生标签可用时才显式添加role。

直接用 <header></header>、<nav></nav>、<main></main>、<aside></aside>、<footer></footer> 这五个标签,就能让屏幕阅读器用户一键跳转到对应区域——不需要加 role,加了反而可能出错。
为什么不用写 role="navigation" 却能被识别为导航?
因为浏览器和辅助技术对这些 HTML5 标签做了隐式角色映射:<nav></nav> 默认就是 role="navigation",<main></main> 就是 role="main",<footer></footer> 是 role="contentinfo"。这不是“约定俗成”,而是 W3C 规范强制要求的行为。
常见错误现象:
- 给
<nav></nav>再写role="navigation"—— Chrome DevTools 的 Accessibility 面板会显示 “Computed Role: navigation”,但 Lighthouse 可能报landmark-is-unique或干扰旧版 NVDA 解析 - 把
<nav></nav>套在<div role="group"> 里 —— 外层 <code>role会覆盖内层语义,导致地标丢失 - 用
<section></section>替代<nav></nav>后又加role="navigation"—— 这违反 ARIA 第一条规则:“有原生语义时,别重写” -
<nav aria-label="主导航"></nav>→ 用于顶部横向菜单 -
<nav aria-label="页脚链接"></nav>→ 放在<footer></footer>内的链接集合 -
<nav aria-label="文章内导航"></nav>→ 文章页右侧 TOC(目录) - 用
<div> 实现搜索框容器,且无法改造成 <code><form role="search"></form>—— 此时必须加role="search",否则不会出现在地标列表中 - 老项目无法重构语义结构,比如仍用
<div class="sidebar"> 表示侧边栏 —— 可加 <code>role="complementary",但必须配aria-label或aria-labelledby,否则报landmark-no-label绝对不要做:
- 给
<section></section>加role="region"却不配aria-label—— 它在地标面板里直接消失,用户完全感知不到 - 把
role="main"加在<div> 上却放两个 —— 浏览器静默忽略第二个,Lighthouse 明确报错 <code>document-has-main-element - 在表格行(
<tr>)上加 <code>role="search"—— 这会让该行脱离表格语义,触发aria-requires-children错误最常被忽略的一点:地标不是“写完就生效”的静态配置。SPA 路由切换时,
<main></main>的内容变了,但焦点不会自动移到新内容区——得用document.getElementById('main').focus()或aria-live配合手动管理。否则用户卡在旧内容里,根本不知道页面已更新。
- 给
多个 nav 怎么区分?必须用 aria-label
一个页面可以有多个 <nav></nav>,但所有默认都叫“导航区域”。屏幕阅读器用户听到的是一串无意义的“导航、导航、导航”,根本分不清哪个是顶部菜单、哪个是页脚链接、哪个是面包屑。
解决方案只有一种:用 aria-label 提供唯一上下文。
注意:aria-labelledby 也可用,但必须确保引用的 ID 对应一个可见且语义明确的文本节点;aria-label 更轻量、更可控,优先选它。
什么时候才需要显式写 role 属性?
只有两种真实场景值得破例:











