CSS Grid
CSS Grid
最初学习 Grid 布局时,按照网上的教程,大概是这么个流程,先摸 grid-template-columns,再记 grid-column: 1 / 3,然后学 grid-area,接着就开始用它做布局。很多人就此打住,把 Grid 当”更强的 Flexbox”用。
这个理解不能说错,但真的只是皮毛。
真正的重点是:CSS Grid 不是布局工具,而是二维空间系统。
Grid 的核心不是 cell,而是 line
线网模型
当你写这行 CSS:
grid-template-columns: 200px 1fr 200px;
浏览器内部生成的并不是三个”格子”,而是一组网格线:
line 1 | track 1 | line 2 | track 2 | line 3 | track 3 | line 4
线的数量 = 轨道数量 + 1。
grid-column: 1 / 3,表示把这个元素放在 line 1 和 line 3 之间的空间里。这不是排版,是坐标定位。
定位模型的区别
| 模型 | 本质 | 特点 |
|---|---|---|
position: absolute | 像素坐标 | 手写坐标,不易维护 |
display: flex | 流式排序 | 单轴排列,天然适应内容 |
display: grid | 线性坐标系统 | 结构化坐标,可组合 |
Grid 本质上是个二维绝对定位系统,但跟 absolute 不一样的是,它的坐标是结构化的、可组合的、可语义化的。
Grid Line 语义化
数字写法的问题
grid-column: 2 / 5;
- 不可读——看到
2 / 5你完全不知道这是什么东西 - 不可维护——调整布局时一个数字改动可能引发连锁反应
- 跟设计语义完全不沾边
更麻烦的是,在最左边加一列,所有基于数字的定位都得重新算一遍。
Named Lines
CSS Grid 允许给网格线命名:
grid-template-columns:
[sidebar-start] 240px
[sidebar-end content-start] 1fr
[content-end];
然后这样用:
.sidebar {
grid-column: sidebar-start / sidebar-end;
}
.main {
grid-column: content-start / content-end;
}
除了实现语义替换,在考虑布局时的角度也有变化,从数字坐标系统,变成空间语义系统。布局不再是”元素 A 在第 2 到第 5 条线之间”,而是”侧边栏占侧边栏的区域,主内容区占主内容区的区域”,让组件的语义与位置的语义直接匹配。
主流布局模式
这是 Web 应用常见的 Grid 结构,内容居中:
grid-template-columns:
[full-start] minmax(24px, 1fr)
[content-start] min(1200px, 100%)
[content-end] minmax(24px, 1fr)
[full-end];
表面看,这是在”让内容居中”。但它实际在定义三层空间结构:
| gutter (auto) | content (bounded) | gutter (auto) |
三个空间区域,每个都有明确的语义和约束:
- 左右 gutter:
minmax(24px, 1fr)保证最小 24px 安全边距,同时能随屏幕自动扩展 - 内容区域:
min(1200px, 100%)确保最大宽度 1200px,小屏自动占满
这里有一个思路的变化,从“考虑怎么居中内容”到“怎么将不同类型的东西放在它的位置上”。
容器先行
以前的思维惯性是,先写 item
.sidebar { width: 240px; }
.main { margin-left: 240px; }
这种方式的问题是每个组件都得知道布局的存在——它们必须知道自己该往哪挪、该留多少空间,强耦合,很多布局问题就是这么来的。
在 Grid 最佳实践中,应该是先规划容器
.layout {
display: grid;
grid-template-columns: 240px 1fr;
}
组件只需要声明自己属于哪一列,不用知道任何布局细节:
.sidebar { grid-column: 1; }
.main { grid-column: 2; }
本质转变是:从”组件决定布局”到”布局决定组件”。父容器定义空间规则,子元素只需要选自己属于哪个空间。
Subgrid
Flexbox 有一个限制,每个 Flex 容器都是独立的布局系统,在每个 Flex 组件内,都需要提供额外的定位措施。
这导致嵌套的 Flex 布局天然不对齐——子元素在子容器内居左,兄弟容器的子元素可能在完全不同的位置。这种不对齐需要大量 hack 来解决:额外的 wrapper、负 margin、calc 计算……
Subgrid 允许子布局继承父 Grid 的轨道结构:
.card {
display: grid;
grid-template-columns: subgrid;
}
整个应用可以共享:
- 统一的列系统
- 统一的对齐规则
- 统一的间距系统
以 Notion、Linear 为例
它们的布局结构,核心并不是”一堆组件”,而是一个全局统一的空间系统。每个卡片、每个弹窗、每个侧边栏都在同一个坐标体系下运转。选择合适的布局架构,能达到事半功倍的效果,如果使用 absolute 或 flex 来实现类似布局,则要花费更多功夫。
CSS Grid Lanes:瀑布流布局的首选
Safari Technology Preview 234 引入了 CSS Grid Lanes。
用 display: grid-lanes 创建瀑布流布局:
.container {
display: grid-lanes;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 16px;
}
定义列时创建”瀑布”布局,定义行时创建”砖块”布局。
flow-tolerance 属性
Grid Lanes 引入了一个新属性 flow-tolerance,控制元素在轨道间移动的积极性:
.container {
flow-tolerance: 2em;
}
默认值 1em。这个属性解决的是:元素该跳到更短的轨道上,还是保持当前位置?这个权衡直接影响最终布局的紧凑程度。
适合的场景
Grid Lanes 特别适合:
- 照片画廊
- 报纸风格的文章网格
- 电商产品列表
- mega menu
过去这些布局都需要 JavaScript 库,现在纯 CSS 能做到了。
布局思路
| 层级 | 本质 | 思维模式 |
|---|---|---|
| Component | 内容 | UI = 组件树 |
| Flex | 排列 | UI = 流式组合 |
| Grid | 空间 | UI = 空间坐标 |
| System | 规则 | UI = 物理法则 |
用 Flex 思考时,你在想”元素该怎么排列”。
用 Grid 思考时,你在想”空间该怎么分配”。
用 Subgrid + Grid Lanes 思考时,你在想”整个界面的空间规则是什么”。
CSS Grid 刚普及时,大部分教程把它介绍成”二维版 Flexbox”。这个类比有帮助,但也容易让人停在表面。
当你真正摸透 Grid 的线网模型,你会发现它解决的不是”怎么排列元素”,而是”怎么定义空间规则”。很多以前需要 hack 才能解决的布局问题,一旦接受这个转变,就变得自然而然了。
Subgrid 和 Grid Lanes 把这个思维继续扩展:从单层空间系统,到多层嵌套的统一空间系统,再到支持瀑布流的动态空间系统。CSS 正在成为一个真正强大的布局语言,Grid 是这整个演进的核心。