必须且只能出现一次,否则屏幕阅读器无法准确定位内容起点;它专用于包裹当前页面独有核心内容,漏写会导致视障用户手动滑动6–8次才能进入正文。

<main></main> 必须且只能出现一次,否则屏幕阅读器会找不到内容起点;<nav></nav> 和 <button></button> 不是“看着高级才用”,而是解决 iOS 点击延迟、键盘回车提交、语音跳转这三类真实交互断裂的刚需。
为什么 <main></main> 漏写会让用户多滑 8 秒才看到正文
移动端视障用户靠 VoiceOver 或 TalkBack 逐区块朗读页面。<main></main> 是唯一被识别为“主体内容起始点”的标签。漏掉它,辅助工具只能从 <header></header> 开始逐个读导航、logo、搜索框——用户得手动滑动 6–8 次才能进正文。
- 必须全页仅出现一次,不能嵌套在
<article></article>或<section></section>内部 - 不要把广告位、相关推荐塞进
<main></main>,它只该包裹当前页面真正要传达的核心信息 - Chrome DevTools 的 Accessibility 面板里可直接验证:选中
<main></main>节点后,“AX Role” 应显示main,而非generic
<nav></nav> 包什么、不包什么,直接影响微信 WebView 的焦点行为
微信内置浏览器对 <nav></nav> 的 focus 管理比普通 <div> 稳定得多,但前提是语义准确。它只该包裹主导航链接(如首页、分类、我的),不是所有带链接的容器都叫 <code><nav></nav>。
- 面包屑(Home > 分类 > 商品)属于
<ol class="breadcrumb"></ol>,不是<nav></nav> - 页脚里的“关于我们”“联系我们”属于补充信息,用
<aside></aside>或普通<div> 更合适 <li>若页面有多个导航区(比如顶部主菜单 + 侧边栏分类),每个都应独立用 <code><nav></nav>,并加aria-label区分,例如<nav aria-label="主导航"></nav>和<nav aria-label="分类导航"></nav> - 不要写
<div onclick="submitForm()">提交,改用 <code><button type="button" onclick="submitForm()">提交</button> - 表单内按钮优先用
<button type="submit"></button>,而不是给<input type="button">加 JS 监听 - 若样式需完全自定义,用
appearance: none重置默认样式,别为了“看起来不像按钮”而放弃语义 - 回车键默认触发表单提交,无需额外监听
keydown - 搭配
<label for="xxx"></label>可让点击文字聚焦输入框,小屏手指容错率翻倍 - 避免把整个页面包进一个
<form></form>,每个逻辑表单单元(如登录框、评论框)单独用一个
<button></button> 替代 div onclick 不是为了语义漂亮,是绕过 iOS Safari 的 300ms 延迟
在 iOS Safari 中,非 <button></button> 元素(包括 <div>、<code><span></span>)默认有 300ms 点击延迟,用于判断是否双击缩放。用 <button></button> 天然规避,且支持 type="submit"、键盘空格触发等原生行为。
<form></form> 是唤起「完成」键盘按钮的唯一通行证
安卓和 iOS 键盘的「完成」按钮(右下角那个 ✅ 或「发送」)只在 <form></form> 包裹的 <input> 上才会出现。用 <div> 模拟表单,等于主动放弃这个交互链路。
<ul>
<li>
<code><form></form> 必须包含至少一个可提交控件(如 <input type="text">、<textarea></textarea>)
<time datetime="..."></time> 缺少 datetime 属性时,根本不触发「添加到日历」;<footer></footer> 若没包裹在 或最近的 <section></section> 内,部分安卓 WebView 会直接忽略其语义。这些细节不靠模拟器,得拿真机连 Safari Web Inspector 或 Chrome Remote Debugging 才能验证。











