lang 必须写在 <html> 标签上且值准确(如 zh-CN),<main> 必须是 <body> 的唯一直接子元素,标题层级必须连续不跳级;语义标签优先,避免用 div + ARIA 模拟原生行为。

lang 属性必须写对,不能靠 JS 拼接
不写 lang 或写成 lang="zh"、lang="ch",屏幕阅读器(如 NVDA、VoiceOver)会 fallback 到默认语言(通常是英文),中文内容被逐字按英语发音朗读,语调断裂、分词错误,用户根本无法理解。
-
lang必须写在<html>标签上,不能只靠<meta>或局部lang补救 - 简体中文必须用
lang="zh-CN",繁体用lang="zh-TW",港澳用lang="zh-HK";后缀不可省略 - 多语言页面中局部切换语言,要用嵌套
lang,比如:<p lang="en">"The quick brown fox..."</p> - React/Vue 中避免运行时 JS 拼接
lang值(如lang="{{ locale }}"),确保 SSR 或首屏 HTML 已渲染正确值
<main> 必须是 <body> 的直接子元素且唯一
把 <main> 包在 <div class="container"> 或 <section> 里,或者服务端模板多次 include 导致多个 <main>,NVDA 等工具会跳过主内容区,用户按 M 键无法进入正文。
-
<main>必须是<body>的**直接子元素**,中间不能隔任何容器标签 - 全局只能出现一次;
<main>内部禁止再嵌套<main>,axe 会标记为 critical - 组件化框架中,
<main>必须由根布局层统一输出,子组件不得擅自渲染 -
<nav>、<aside>等 landmark 元素可并列存在,但<main>是唯一主干
标题层级必须连续,不能跳级或隐藏后留空
屏幕阅读器用户靠 H 键(H1→H2→H3…)逐级跳转。一旦出现 <h2> 后直接 <h4>,中间缺 <h3>,他们会误判章节结束,实际内容还在继续——这不是体验差,是信息架构逻辑塌方。
- CSS 隐藏标题(
display: none或visibility: hidden)但未移除 DOM,会导致层级断裂;应删 DOM 或改用aria-hidden="true"+ 视觉隐藏类 - JS 动态插入标题前,必须校验当前上下文的层级,避免
<h2>下直接插入另一个<h2> - 每个
<section>应以<h2>或更高级别标题开头;HTML5 允许重置层级,但前提是显式提供标题 - 第三方组件库默认用
<h2>作卡片标题时,若嵌套在<h2>主区下,会造成 H2→H2,需手动降级为<h3>
语义标签优先,别用 <div> + ARIA 模拟原生行为
看到 <div role="button" tabindex="0"> 就该警觉:这说明放弃了浏览器原生能力。原生 <button> 自带聚焦、空格/回车触发、角色声明;而模拟按钮必须手动补 role、tabindex、键盘事件监听——漏一个,键盘用户就卡住。
立即学习“前端免费学习笔记(深入)”;
-
<nav>不是“所有链接集合”,而是有明确导航意图的链接组;非导航链接(如“相关阅读”)不该塞进<nav> - 多个
<nav>必须加aria-label区分,例如:<nav aria-label="主导航">、<nav aria-label="页内目录"> - 面包屑必须用
<ol>,当前页加aria-current="page";不用<ul> - 表单控件必须有
<label for="xxx">或包裹式<label><input>xxx</label>,否则读屏无法播报说明
真正难的不是写对几个标签,而是让整个工程链路(模板、组件、CI、测试)都守住 lang、<main>、标题层级这三条硬线——它们不靠 lint 或 review 发现,得从构建阶段就卡死。



















