CSS Grid 布局应用说明
这篇文章解决什么问题
CSS Grid 经常被一句话概括成“二维布局”,但真实项目里,它解决的不只是“横向加纵向”。
它更适合解决这类问题:
- 页面有明确的行和列,比如后台工作台、卡片墙、图片墙。
- 布局要对齐多列内容,不能只靠
margin和width硬凑。 - 卡片数量不固定,但希望自动换行、自动填满空间。
- 左侧导航、主内容、右侧信息栏需要稳定排布。
- 表单要做标签列、内容列、按钮列的对齐。
- 有些元素要跨行或跨列,比如统计卡、主图、推荐位。
本文会从 Grid 的心智模型开始,横向对比 Flex,再讲常用属性、真实场景和容易写错的地方。
先说结论
CSS Grid 的核心价值是:
先定义一个网格,再把子元素放进这个网格。
最小可用写法:
css
.card-list {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
}这表示:
.card-list是 Grid 容器。- 它有 3 列。
- 每列平分剩余空间。
- 子元素之间有
16px间距。
如果要做响应式卡片列表,常见写法是:
css
.card-list {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 16px;
}这段代码的意思是:
- 每个卡片最小
240px。 - 空间足够时自动增加列数。
- 空间不够时自动换行。
- 每列最大可以撑满剩余空间。
这是 Grid 在业务项目里最常见、最实用的写法之一。
Grid 和 Flex 怎么选
很多人学 Grid 时,第一个问题不是语法,而是:什么时候用 Grid,什么时候用 Flex?
简单判断:
- 一维排列,用 Flex。
- 二维布局,用 Grid。
- 内容自己排一行或一列,用 Flex。
- 你先设计好行列结构,再放内容,用 Grid。
对比表:
| 能力 | Flex | Grid |
|---|---|---|
| 核心方向 | 一维 | 二维 |
| 主要关注 | 主轴和交叉轴 | 行和列 |
| 适合内容 | 导航、按钮组、横向列表、居中 | 页面骨架、卡片墙、表单、仪表盘 |
| 换行后对齐 | 不擅长跨行列对齐 | 擅长 |
| 子项跨列 | 不直观 | grid-column 很自然 |
| 布局控制权 | 内容驱动更多 | 容器规划更多 |
示例:
css
.toolbar {
display: flex;
align-items: center;
gap: 8px;
}工具栏是一行内容,用 Flex 更直接。
css
.dashboard {
display: grid;
grid-template-columns: 240px 1fr 320px;
gap: 24px;
}后台页面有左中右三块区域,用 Grid 更清晰。
不要把它们看成互相替代。真实项目里,经常是外层 Grid,内层 Flex:
css
.page {
display: grid;
grid-template-columns: 240px 1fr;
}
.toolbar {
display: flex;
justify-content: space-between;
align-items: center;
}Grid 的基本模型
Grid 有两个核心角色:
- Grid 容器:写
display: grid的元素。 - Grid 子项:容器的直接子元素。
html
<div class="grid">
<div>1</div>
<div>2</div>
<div>3</div>
</div>css
.grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 12px;
}只有 .grid 的直接子元素会成为 Grid 子项。孙子元素不会自动参与这层 Grid。
html
<div class="grid">
<article>
<h3>标题</h3>
<p>内容</p>
</article>
</div>这里 article 是 Grid 子项,h3 和 p 不是这层 Grid 的子项。
如果 article 内部也要用 Grid,需要给 article 单独写:
css
.grid article {
display: grid;
}定义列:grid-template-columns
grid-template-columns 用来定义列。
固定列:
css
.layout {
display: grid;
grid-template-columns: 240px 1fr;
}这表示左列固定 240px,右列占剩余空间。
三等分:
css
.cards {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}更常写成:
css
.cards {
display: grid;
grid-template-columns: repeat(3, 1fr);
}混合宽度:
css
.page {
display: grid;
grid-template-columns: 220px minmax(0, 1fr) 320px;
}这里中间列用了 minmax(0, 1fr),原因是实际业务里中间内容可能有长文本、表格、代码块。如果只写 1fr,有时内容最小宽度会把列撑开,导致页面横向溢出。
fr 是什么
fr 表示 Grid 中的剩余空间份额。
css
.grid {
display: grid;
grid-template-columns: 1fr 2fr;
}如果容器扣掉间距后还有 900px,第一列大约 300px,第二列大约 600px。
fr 不是百分比。它会在 Grid 算完固定尺寸、间距、内容约束后,再分配剩余空间。
对比:
css
.grid-a {
grid-template-columns: 50% 50%;
gap: 16px;
}这个写法容易溢出,因为两列已经占满 100%,再加 gap 就超过容器宽度。
更稳:
css
.grid-b {
grid-template-columns: 1fr 1fr;
gap: 16px;
}fr 会把 gap 扣掉之后再分配空间。
定义行:grid-template-rows
grid-template-rows 用来定义行。
css
.panel {
display: grid;
grid-template-rows: auto 1fr auto;
min-height: 480px;
}这个结构常用于面板:
- 头部高度由内容决定。
- 中间区域占剩余空间。
- 底部高度由内容决定。
示例:
html
<section class="panel">
<header>标题</header>
<main>内容</main>
<footer>操作按钮</footer>
</section>css
.panel {
display: grid;
grid-template-rows: auto minmax(0, 1fr) auto;
min-height: 480px;
}中间行写 minmax(0, 1fr),也是为了避免内容最小高度把布局撑开。
间距:gap
Grid 里不要优先用子元素 margin 做网格间距。用 gap 更干净。
css
.cards {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
}也可以分开写:
css
.cards {
row-gap: 24px;
column-gap: 16px;
}gap 的好处:
- 不需要处理第一项、最后一项的额外 margin。
- 行间距和列间距语义清楚。
- 配合
fr时不会产生额外溢出。
响应式卡片:repeat()、minmax()、auto-fit
这组写法非常常见:
css
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 16px;
}逐段理解:
css
minmax(240px, 1fr)表示每列最小 240px,最大占一份剩余空间。
css
repeat(auto-fit, ...)表示容器能放几列就放几列。
这个写法适合:
- 商品列表
- 文章卡片
- 图片墙
- 资源列表
- 功能入口
示例:
html
<div class="card-grid">
<article>卡片 1</article>
<article>卡片 2</article>
<article>卡片 3</article>
<article>卡片 4</article>
</div>css
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 16px;
}如果你希望列数完全由断点控制,也可以写媒体查询:
css
.card-grid {
display: grid;
grid-template-columns: 1fr;
gap: 16px;
}
@media (min-width: 768px) {
.card-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (min-width: 1200px) {
.card-grid {
grid-template-columns: repeat(4, 1fr);
}
}两种方式都合理:
- 内容卡片最小宽度明确,用
auto-fit + minmax()。 - 设计稿强依赖固定断点,用 media query。
auto-fit 和 auto-fill
auto-fit 和 auto-fill 看起来很像,但空列处理不同。
css
.grid-fit {
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}
.grid-fill {
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
}简单理解:
| 写法 | 空列怎么处理 | 常见用途 |
|---|---|---|
auto-fit | 折叠空列,让已有项拉伸 | 卡片列表、商品列表 |
auto-fill | 保留空列轨道 | 需要稳定网格轨道的场景 |
大多数业务卡片列表,优先用 auto-fit。
原因是用户通常希望已有卡片撑满当前行,而不是右侧保留看不见的空列。
子项跨列和跨行
Grid 子项可以跨列:
css
.featured-card {
grid-column: span 2;
}也可以指定起止线:
css
.hero {
grid-column: 1 / 3;
}表示从第 1 条列线开始,到第 3 条列线结束。也就是跨 2 列。
跨行:
css
.tall-card {
grid-row: span 2;
}示例:
css
.magazine-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
}
.magazine-grid .main-story {
grid-column: span 2;
grid-row: span 2;
}适合:
- 推荐位
- 数据大卡
- 主图区域
- 杂志式布局
注意:跨行布局要确认内容高度,否则容易出现视觉空洞。卡片瀑布流不是 Grid 的强项,复杂瀑布流通常需要额外策略。
命名区域:grid-template-areas
如果页面骨架很明确,可以用命名区域。
html
<div class="app-shell">
<header class="header">Header</header>
<aside class="sidebar">Sidebar</aside>
<main class="main">Main</main>
<aside class="panel">Panel</aside>
</div>css
.app-shell {
min-height: 100vh;
display: grid;
grid-template-columns: 240px minmax(0, 1fr) 320px;
grid-template-rows: auto minmax(0, 1fr);
grid-template-areas:
"header header header"
"sidebar main panel";
}
.header {
grid-area: header;
}
.sidebar {
grid-area: sidebar;
}
.main {
grid-area: main;
}
.panel {
grid-area: panel;
}优点:
- 页面结构一眼能看出来。
- 改布局时比一堆
grid-column更直观。 - 适合后台壳、文档壳、管理台布局。
响应式时也很清楚:
css
@media (max-width: 900px) {
.app-shell {
grid-template-columns: 1fr;
grid-template-rows: auto auto minmax(0, 1fr) auto;
grid-template-areas:
"header"
"sidebar"
"main"
"panel";
}
}缺点是:区域必须是矩形,不能写成断裂形状。
错误示例:
css
grid-template-areas:
"a a b"
"a c c";这里 a 不是一个完整矩形,布局无效。
对齐:justify 和 align
Grid 对齐有两层:
- 对齐单个子项。
- 对齐整个网格。
子项在自己格子里怎么对齐:
css
.grid {
display: grid;
justify-items: center;
align-items: center;
}含义:
justify-items控制水平方向。align-items控制垂直方向。
单个子项也可以覆盖:
css
.item-special {
justify-self: end;
align-self: start;
}整个网格在容器里怎么对齐:
css
.grid {
display: grid;
width: 800px;
height: 400px;
grid-template-columns: repeat(3, 160px);
justify-content: center;
align-content: center;
}含义:
justify-content控制整组列在容器水平方向的位置。align-content控制整组行在容器垂直方向的位置。
简单记:
items管格子里的项目。content管整个网格。justify多数时候看横向。align多数时候看纵向。
自动布局:grid-auto-flow
Grid 默认按行自动放置子项:
css
.grid {
display: grid;
grid-auto-flow: row;
}也可以按列:
css
.grid {
display: grid;
grid-auto-flow: column;
}还有一个常见但要谨慎的值:
css
.grid {
grid-auto-flow: dense;
}dense 会尝试把后面的元素填进前面的空洞。
它适合图片墙、视觉卡片墙,但不适合强阅读顺序的内容。因为视觉顺序可能和 DOM 顺序不完全一致,读屏和键盘访问仍按 DOM 顺序走。
所以文章列表、表单、流程步骤这类有明确顺序的内容,不要随便用 dense。
常见场景一:后台三栏布局
html
<div class="admin-page">
<aside class="admin-nav">导航</aside>
<main class="admin-main">主内容</main>
<aside class="admin-panel">信息栏</aside>
</div>css
.admin-page {
min-height: 100vh;
display: grid;
grid-template-columns: 240px minmax(0, 1fr) 320px;
gap: 24px;
}重点是中间列:
css
minmax(0, 1fr)这能减少表格、代码块、长文本把主内容列撑爆的概率。
如果移动端要堆叠:
css
@media (max-width: 960px) {
.admin-page {
grid-template-columns: 1fr;
}
}常见场景二:表单对齐
表单里经常需要标签和输入框对齐。
html
<form class="settings-form">
<label for="name">名称</label>
<input id="name" />
<label for="email">邮箱</label>
<input id="email" />
</form>css
.settings-form {
display: grid;
grid-template-columns: 120px minmax(0, 1fr);
gap: 16px 20px;
align-items: center;
}如果有按钮区域,可以让它从第二列开始:
css
.form-actions {
grid-column: 2;
}这种写法比给每个标签手写宽度、再用 margin 调输入框更稳定。
常见场景三:自适应卡片列表
html
<section class="article-grid">
<article>文章 1</article>
<article>文章 2</article>
<article>文章 3</article>
</section>css
.article-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 20px;
}这个模板适合大多数列表页。你只需要改两个值:
260px:卡片允许的最小宽度。20px:卡片间距。
如果卡片内还有图片,可以再配合 aspect-ratio:
css
.article-card-cover {
aspect-ratio: 16 / 9;
overflow: hidden;
}Grid 负责整体排布,aspect-ratio 负责单张卡片里的媒体比例。
常见场景四:九宫格和固定区域
css
.shortcut-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px;
}如果要固定每个格子的比例:
css
.shortcut-item {
aspect-ratio: 1 / 1;
display: grid;
place-items: center;
}place-items: center 是简写,等价于:
css
align-items: center;
justify-items: center;它适合图标入口、功能宫格、上传占位框。
常见场景五:主次卡片布局
html
<section class="news-grid">
<article class="lead">主新闻</article>
<article>新闻 2</article>
<article>新闻 3</article>
<article>新闻 4</article>
</section>css
.news-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
}
.news-grid .lead {
grid-column: span 2;
grid-row: span 2;
}这种布局用 Flex 会很别扭,用 Grid 更符合结构。
如果小屏改成单列:
css
@media (max-width: 768px) {
.news-grid {
grid-template-columns: 1fr;
}
.news-grid .lead {
grid-column: auto;
grid-row: auto;
}
}跨行跨列的规则,在小屏经常要重置,否则容易出现奇怪空位。
常见坑
只想排一行按钮,却用了 Grid
css
.actions {
display: grid;
grid-template-columns: repeat(3, auto);
}按钮组通常用 Flex 更自然:
css
.actions {
display: flex;
align-items: center;
gap: 8px;
}不要为了用 Grid 而用 Grid。
1fr 被内容撑爆
css
.layout {
display: grid;
grid-template-columns: 240px 1fr;
}如果右侧有表格、长链接、代码块,可能横向溢出。
更稳:
css
.layout {
display: grid;
grid-template-columns: 240px minmax(0, 1fr);
}Grid 项内部也可能需要:
css
.main {
min-width: 0;
}用百分比列再加 gap
css
.grid {
display: grid;
grid-template-columns: 50% 50%;
gap: 16px;
}这可能溢出。优先:
css
.grid {
grid-template-columns: 1fr 1fr;
gap: 16px;
}把 grid-column 写在非直接子元素上
html
<div class="grid">
<article>
<h3 class="title">标题</h3>
</article>
</div>css
.title {
grid-column: span 2;
}这里不会影响外层 Grid,因为 .title 不是 .grid 的直接子项。
应该写在 article 上:
css
.grid article {
grid-column: span 2;
}或者让 article 自己也成为 Grid 容器。
过度依赖 grid-template-areas
命名区域适合页面骨架,但不适合所有组件。
如果只是普通卡片列表:
css
.cards {
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}没必要写很多区域名。区域名越多,维护成本越高。
忽略 DOM 顺序
Grid 可以改变视觉位置,但不要把它当成改内容顺序的工具。
css
.important {
grid-column: 1;
grid-row: 1;
}视觉上它到了前面,但 DOM 顺序没有变。读屏、键盘焦点、复制顺序仍然按 DOM 走。
重要内容顺序应该先由 HTML 决定,再由 CSS 做布局。
实战模板
自适应卡片列表
css
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 16px;
}固定侧栏 + 自适应内容
css
.page-layout {
display: grid;
grid-template-columns: 260px minmax(0, 1fr);
gap: 24px;
}头中尾面板
css
.panel {
min-height: 420px;
display: grid;
grid-template-rows: auto minmax(0, 1fr) auto;
}表单标签对齐
css
.form-grid {
display: grid;
grid-template-columns: 120px minmax(0, 1fr);
gap: 16px 20px;
align-items: center;
}居中内容
css
.empty-state {
min-height: 240px;
display: grid;
place-items: center;
}主次卡片
css
.feature-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
}
.feature-grid .primary {
grid-column: span 2;
grid-row: span 2;
}总结
CSS Grid 最适合处理有明确行列关系的布局。
记住几个判断:
- 一维内容优先 Flex。
- 二维结构优先 Grid。
- 卡片列表常用
repeat(auto-fit, minmax(..., 1fr))。 - 页面骨架常用
grid-template-columns和grid-template-areas。 - 中间弹性列常用
minmax(0, 1fr)。 - 网格间距优先用
gap。 - 跨列跨行用
grid-column/grid-row。 - 不要用 Grid 改变真实内容顺序。
真实项目里,不要先背属性。先看布局是不是已经有明确的行和列。如果有,Grid 通常就是更干净的方案。