核心是dom顺序必须与视觉顺序一致,否则屏幕阅读器必读歪;须将写在之前、禁用tabindex>0、慎用grid-template-areas,因屏幕阅读器只认html源码顺序而非css布局。

HTML分栏布局的可访问性问题,核心不是“怎么让屏幕阅读器读得更顺”,而是“别让它读歪”——只要DOM顺序和视觉顺序不一致,再好的ARIA也救不回来。
为什么CSS Grid/Flex分栏后屏幕阅读器读序错乱
屏幕阅读器只认HTML源码顺序,不看grid-template-areas或order值。比如你用display: grid把侧边栏放在主内容右边,但HTML里<aside></aside>写在<main></main>前面,它就会先读侧边栏再读标题,用户根本找不到正文入口。
- 常见错误现象:Tab键从页脚开始聚焦、朗读完导航直接跳到版权信息、
<main></main>被完全跳过 - 使用场景:多列新闻列表、仪表盘(左导航+右数据面板)、文档页(左TOC+右正文)
- 性能影响:DOM顺序错乱会触发辅助技术反复遍历整棵树,VoiceOver启动延迟明显增加
- 关键参数差异:
grid-area: sidebar只影响视觉位置,aria-labelledby不能覆盖DOM顺序优先级
修复分栏逻辑顺序的三个硬动作
不是加role="complementary"就能补救,必须动HTML结构本身:
- 把
<main></main>写在<aside></aside>和<nav></nav>之前——哪怕视觉上它在中间;这是唯一能保证M键跳转直达正文的方式 - 禁用
tabindex="1"等正数索引,它会强行打断自然焦点流;仅在模态框内需手动聚焦时用tabindex="-1"+.focus() - 慎用
grid-template-areas定义区域名:如果grid-area: "sidebar main"但HTML是<aside></aside><main></main>,阅读器仍按aside→main读,不会按网格区重排
响应式分栏中动态切换的可访问性陷阱
移动端收起侧边栏、桌面端展开,这种切换最容易暴露DOM顺序缺陷:
- 不要用
display: none隐藏<aside></aside>就以为万事大吉——它只是从渲染树消失,若仍在DOM中且有tabindex="0",键盘用户仍能Tab进去 - 正确做法:用
inert属性(Chrome 101+)或JS批量设tabindex="-1",同时确保aria-hidden="true"只加在纯装饰容器上 - 轮播式分栏(如左右滑动的卡片组)必须同步更新
aria-live="polite"和aria-current="true",否则屏幕阅读器无法感知当前激活栏 - 动态插入新栏(如AJAX加载右侧推荐区)后,必须手动
document.querySelector('.new-sidebar h3').focus(),否则焦点卡在旧栏末尾
验证分栏顺序是否真正生效
别信Lighthouse报告里的“无障碍得分”,它不模拟真实阅读路径:
- 在DevTools里右键
→ “Hide styles”,纯看HTML源码是否符合“导航→搜索→主内容→侧边栏→页脚”这一逻辑流 - 用Tab键从头走到尾,观察焦点是否跳过
<main></main>或卡在不可见元素上 - 在NVDA中按H键跳标题,确认
<main></main>内的<h2></h2>能被直接选中;按D键跳<div>,检查是否意外出现未包裹的<code><div class="sidebar"> <p>最常出问题的地方,往往藏在“视觉OK、标签也用了、但用户就是找不到主内容”的地方——十有八九是某处<code>order、z-index或漏掉的tabindex="-1"悄悄重写了阅读路径。











