语义化结构是wcag 2.1合规的硬性门槛,必须使用等原生标签而非div模拟,严格遵循标题层级(h1唯一、main内首标为h2)、表单需label绑定或aria-labelledby,动态内容须用aria-live声明。

语义化结构不是“加分项”,而是 WCAG 2.1 合规的硬性门槛。没用 <main></main>、<nav></nav> 或跳级用 <h3></h3> 直接替代 <h2></h2>,就可能触发 WCAG 1.3.1 (Info and Relationships) 和 2.4.6 (Headings and Labels) 失败。
为什么 <div> 套 <code>class="header" 无法通过无障碍审计
辅助技术(如 NVDA、VoiceOver)不读 class 名,只依赖原生标签的隐式 role 和 DOM 结构语义。一个 <div class="nav"> 在 AT 中就是普通容器,不会被识别为导航区,用户按快捷键 <code>N 也无法跳转。
-
<nav></nav>自带role="navigation",且支持aria-label显式命名(如aria-label="主导航") -
<main></main>必须且只能出现一次,多个会触发 axe 工具报错"Multiple 'main' elements" -
<section></section>需要配<h2></h2>或更高级别标题,否则会被视为“无意义分组”,AT 可能忽略其结构作用
<h1></h1> 到 <h6></h6> 的嵌套逻辑怎么写才不踩坑
标题层级不是样式选择,是内容逻辑树。屏幕阅读器用户靠 H 键循环跳标题,跳空或断层会直接丢失上下文。
- 每个页面严格一个
<h1></h1>,通常放在<header></header>内,代表整个页面主题 -
<main></main>内的第一个标题必须是<h2></h2>(不能是<h1></h1>),后续按需递进:<h3></h3>表示子节,<h4></h4>表示子子节 - 禁止视觉上“看起来像标题”但用
<p></p>+ CSS 加粗替代,AT 完全无视 - 避免
<h2></h2>→<h4></h4>这类跳跃,axe 会报"Heading order" violation
表单和交互组件的语义化补漏点
很多团队以为结构语义做完就完了,结果表单一测全挂——因为 <input> 单独存在时,AT 根本不知道它要填什么。
-
<label></label>必须用for属性绑定id,不能只靠视觉位置对齐;否则键盘聚焦时 AT 不读说明文字 - 单选/复选组必须用
<fieldset></fieldset>+<legend></legend>包裹,<legend></legend>是整组的可访问标题 - 自定义按钮(比如用
<div> 实现的关闭图标)必须加 <code>role="button"+tabindex="0"+aria-label,否则键盘不可达 - 动态加载内容(如搜索建议列表)需用
aria-live="polite"告知 AT 内容已更新
真正卡住合规改造的,往往不是写不出 <nav></nav>,而是改完后忘了验证:是否所有 <main></main> 都有且仅有一个?是否每个 <section></section> 都有对应标题?是否所有表单控件都被 <label></label> 或 aria-labelledby 显式关联?这些细节不逐项核对,自动化工具扫不出全部问题,人工测试又容易漏。











