不能嵌套在 等标签内,因规范要求其必须全局唯一、不可嵌套;嵌套会导致语义错误,被识别为 region 而非 main,axe 报 landmark-unique 失败,屏幕阅读器误读。

为什么不能嵌套在里
因为规范明确定义 <main></main> 必须是页面中全局唯一的、不可嵌套的主导航内容容器。一旦把它塞进 <section></section>,语义就崩了:浏览器和辅助技术会认为这个“主要内容”只是某个章节的子内容,而不是整页的核心。
常见错误现象:<main></main> 在 DevTools 里被识别为 region 而非 main;axe 扫描报 landmark-unique 失败;屏幕阅读器跳过它或读作“section 内的 main”。
- 检查方式:打开浏览器的 Accessibility 面板,点选
<main></main>元素,确认 Role 显示为main,且没有父级 landmark 干扰 - 修复动作:把
<main></main>提到最外层,和<header></header>、<nav></nav>、<footer></footer>同级;如果需要分块内容,用<article></article>或带标题的<section></section> - 注意兼容性:IE 不原生支持,但现代项目基本不需 polyfill —— 只要不用
document.createElement('main')强行注册,CSS 选择器和 JS 查询都正常工作
<nav></nav> 的语义是“一组用于在文档内或跨文档跳转的链接”,不是“所有带交互的 UI 元素集合”。混入表单、按钮、搜索框,等于告诉爬虫和读屏器:“这些也是导航项”,结果就是导航流混乱、SEO 权重稀释。
典型翻车现场:<nav><form><input type="search"></form></nav>;或者 <nav><button onclick="toggleMenu()">☰</button></nav>。
- 正确做法:搜索框归
<header></header>或独立<form role="search"></form>;菜单开关按钮放在<header></header>或用<button aria-expanded></button>控制侧边栏,侧边栏本身用<aside></aside> - 必须加
aria-label或aria-labelledby:如果<nav></nav>没有可见标题(比如底部一排小图标链接),得补上说明,否则读屏器只报“navigation”,不知道这是什么导航 - 多个
<nav></nav>是允许的:顶部主导航、页脚快捷链接、文章内目录,各自独立,互不嵌套
用前先问一句:它有标题吗?能单独被 RSS 抓取吗?
<section></section> 不是视觉分隔器,而是逻辑分节单元。没标题的 <section></section> 几乎一定该换成 <div> —— 浏览器不会报错,但语义链断了,RSS 工具无法提取,分享卡片可能截断错位置。
<p>容易被忽略的边界:评论区、相关推荐、作者信息卡,看起来像“侧边”,但如果它们和当前文章强关联、有明确标题(如 <code><h3>评论</h3>),就该用 <section></section>;如果只是广告位或纯装饰区块,用 <aside></aside> 更准。
- 硬性检查项:每个
<section></section>必须直接包含一个<h2></h2>–<h6></h6>,且该标题描述本节内容(不能是空<h3></h3>或仅图标) - 不要为了“结构好看”强行拆:比如把一段带小标题的说明文字包成
<section></section>,但实际它只是段落变体,<div class="note"> 更诚实 <li>嵌套没问题,但层级要合理:例如 <code><article><section><h3>子章节</h3></section></article>合法;<section><section><h4>...</h4></section></section>就容易让解析器困惑主次关系
CI 和本地开发怎么真正拦住语义错误
靠人眼 Review 拦不住 <main></main> 嵌套或缺失 <h2></h2>,必须让机器在提交前报错。htmlhint 是目前最轻量、最易集成的校验工具,规则粒度够细,且不依赖构建流程。
关键配置项(.htmlhintrc):
{
"rules": {
"id-unique": true,
"attr-req-alt": true,
"headings": true,
"landmark-one-main": true,
"landmark-no-duplicate-main": true
}
}
- VS Code 插件必开:HTMLHint,保存即标红,比等 CI 失败快十倍
- Git Hooks 加一层保险:用 husky + lint-staged,在 pre-commit 阶段跑
htmlhint src/**/*.html - PR 检查重点不是“有没有用语义标签”,而是“有没有破坏语义链”——比如
<main></main>是否被display: none或aria-hidden="true"错误隐藏
最常被绕过的点:开发者改了结构却忘了更新 axe 扫描配置,或只在 Chrome 里测,没切到 Safari 的 VoiceOver 实际体验一遍。语义是否生效,最终得由辅助技术验证,不是 DOM 看着像就行。











