grid-auto-flow: dense 会让视觉与 dom 顺序不一致,因为它仅改变自动放置项的渲染位置,将后续项往前插入已有轨道的空单元格,而不改变 html 中的原始顺序;屏幕阅读器按 dom 顺序读取,导致可访问性错乱,且无法通过 order 或 aria-order 修复。

grid-auto-flow: dense 为什么会让视觉和 DOM 顺序不一致
它只是让自动放置的项“往前插”,不改变 HTML 中的顺序,但浏览器渲染时可能把第 5 个 <div> 塞进第 1 行右侧空位,而第 2 个还在第 2 行开头。屏幕阅读器按 DOM 读,用户看到的却是乱序布局。
<p>常见错误现象:<code>grid-auto-flow: row dense 下卡片视觉上左高右低,但焦点顺序跳来跳去;用键盘 Tab 导航时,焦点从底部突然跳回顶部。
- 无法通过
order修复——order只影响 flex,Grid 中无效 - 不能靠
aria-order补救——该属性不存在,ARIA 规范不支持重定义阅读顺序 - 唯一可控方式:确保 DOM 顺序 ≈ 视觉流,或改用
flex-wrap避开这个问题
dense 模式在响应式下根本不起作用
很多人加了 grid-auto-flow: row dense,再配 grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)),以为小屏能自动收紧。其实 dense 不参与列数重算,它只在当前已生成的轨道里找空格子填。
典型误用场景:宽屏 4 列 → 窄屏自动变成 2 列,但原来 grid-column: span 3 的项直接溢出容器,dense 不会把它缩成 span 1,也不会挪它位置。
- 真正起效的是媒体查询 + 重新定义
grid-template-columns -
minmax(150px, 1fr)在窄屏下容易让所有项挤进第一行,撑高容器——这时grid-auto-rows: minmax(100px, auto)才能压住高度 - 如果用了
grid-template-areas,dense 完全失效,区域模板锁死一切位置
跨格元素一出现,dense 就基本失效
grid-auto-flow: dense 只对“完全未被占据”的格子生效。只要一个元素写了 grid-column: 1 / -1 占满整行,它下面那行就是起点,dense 绝对不会把后续项塞进这行右侧“看起来是空的”地方——因为那不是空格子,而是被跨格项覆盖过的轨道交点。
常见错误现象:首项占整行,第二项老老实实从第二行开头排,右上角留白明显,开了 dense 也没变化。
- 残余 L 形空隙(比如 3×3 网格中一个 2×2 元素)dense 无法利用
- 手动定位项(哪怕只写
grid-row: 2)会彻底脱离 dense 管控 - gap 是间隙,不是空格子——dense 对
gap完全无视,它只看网格线围成的单元格是否“物理空”
标签云、瀑布流这类高度动态场景千万别硬上 dense
标签文字长度不同、字号响应式变化、换行策略不一,导致每个 .tag 高度不可预测。grid-auto-flow: dense 要求轨道高度稳定,但它既不创建新行,也不拉伸/压缩已有轨道,只会把新项硬塞进固定高度的格子里,结果不是溢出就是留白。
真实表现:加载图片后标签突然变高,整个网格重排抖动;小屏下所有标签挤进一行,容器高度爆炸式增长。
- 纯 CSS 替代方案:
display: flex+flex-wrap: wrap+gap,天然适配动态高度 - 真要 Grid 做标签云,就得放弃自适应——统一设
grid-auto-rows: 2.5rem,标签内用display: flex居中,字号只能媒体查询阶梯切换 -
column-count+break-inside: avoid是目前最稳的 Masonry 类替代,兼容性好且无 JS 依赖











