CSS Grid 布局的一个坑
上周做后台管理系统,有个列表页需要响应式布局。我想着用 Grid 应该很简单,结果踩了个坑。
问题场景
需求是这样的:大屏三列,平板两列,手机一列。我想当然写了:
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
}
@media (max-width: 768px) {
.grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 480px) {
.grid {
grid-template-columns: 1fr;
}
}
看起来没问题对吧?但是当列表项高度不一致的时候,问题来了——有些卡片会”悬空”,和上面的卡片之间有很大的空隙。
原因
Grid 默认的 align-items 是 stretch,但这不会让卡片自动填充垂直方向的空隙。而且 grid-auto-flow 默认是 row,元素按行填充,高度参差不齐时就会出现这种情况。
解决方案
折腾了半天,最后用了 grid-auto-flow: dense,让后面的元素自动往前补位:
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 16px;
grid-auto-flow: dense;
}
.card {
break-inside: avoid;
}
等等,这样写的话 repeat(auto-fill, ...) 配合媒体查询就有点多余了。直接用 minmax 让它自己判断列数就行。
说实话最开始我用了 Masonry 库,后来发现 Grid 就能搞定,感觉有点多余了。但如果你的卡片高度差异特别大,Masonry 还是更稳定一些。
还有个问题
用了 dense 之后,DOM 顺序和视觉顺序可能不一致,如果用户用 Tab 键导航,焦点跳转会很奇怪。无障碍这块儿确实得注意。
后来想了想,干脆把卡片高度统一了,问题从根本上解决。产品设计也觉得这样更整齐。
总结
Grid 很强,但也不是万能的。有些场景传统的 Float 或者 Flex 换行反而更省心。选方案的时候多想想实际需求,别为了用新特性而用。