⭐ 推荐:社区规则条款 V1.0

CSS Grid 布局完全指南:从入门到项目实战

pantao
pantao 正式会员正式会员认证极客认证极客
发布于 2026-10-12 19:27 ·1 浏览 ·3 回复
内容摘要

CSS Grid 布局从容器与项目、划列划行、命名区域到按网格线定位逐步讲解,强调 minmax(0,1fr) 可避免溢出、grid-area 命名大小写敏感,并帮助读者独立实现后台管理布局、响应式卡片墙和图文混排页面。

学完这篇你能独立用 CSS Grid 写出后台管理布局、响应式卡片墙和图文混排页面,不用再靠 float、flex 嵌套和一堆媒体查询硬凑。

第一步:分清容器和项目

Grid 只有两个角色:容器(设了 display: grid 的元素)和项目(它的直接子元素)。只有直接子元素受网格控制,孙子元素不管。

.container {
  display: grid;       /* 块级网格 */
  /* display: inline-grid;  行内网格,用得少 */
}

写完之后页面看起来毫无变化——因为还没划格子,默认只有一列,项目从上往下堆。别慌,这是正常的。

注意:display: grid 只对直接子元素生效。如果你把子元素包了一层 <div>,那层包装才是项目,里面的东西得单独再设网格。

第二步:划列和划行

.container {
  display: grid;
  grid-template-columns: 200px 1fr 200px;  /* 三列:固定-自适应-固定 */
  grid-template-rows: 60px auto;           /* 两行 */
  gap: 16px;                               /* 行列间距,替代 margin 拼缝 */
}

三个关键词记牢:

  • px / % / rem:固定尺寸
  • fr:剩余空间的比例份额,1fr 2fr 表示后者拿两倍
  • repeat():repeat(3, 1fr) 等于 1fr 1fr 1fr
  • minmax(200px, 1fr):小不小于 200px,大按比例分

注意:1fr 的最小值是 auto,遇到长英文单词或宽表格会被撑破。稳妥写法是 minmax(0, 1fr),这一条能救掉八成的"布局莫名溢出"。

第三步:用命名区域排版

给每个项目起名,然后像画图一样描述布局,改动结构时只改一处:

.layout {
  display: grid;
  grid-template-columns: 220px 1fr;
  grid-template-rows: 64px 1fr 48px;
  grid-template-areas:
    "sidebar header"
    "sidebar main"
    "sidebar footer";
  min-height: 100vh;
  gap: 12px;
}
.layout > .header  { grid-area: header; }
.layout > .sidebar { grid-area: sidebar; }
.layout > .main    { grid-area: main; }
.layout > .footer  { grid-area: footer; }

每行字符串里的列数必须一致,名字相同的相邻格子会自动合并成一块。空格用 . 占位表示留空。

注意:grid-area 的名字大小写敏感,且不能加引号。名字写错不会报错,只会整块跑到自动位置上去,排查时优先看这里。

第四步:精确投放单个项目

不想用区域名时,直接按网格线编号投放。线的编号从 1 开始,-1 表示最后一条线:

.banner {
  grid-column: 1 / 3;     /* 从第 1 条线到第 3 条线,占两列 */
  grid-row: 2 / 4;
}
/* 等价简写 */
.banner { grid-column: span 2; }   /* 从当前位置往后跨两列 */

跨列是卡片墙错落感的来源:让第一个卡片 grid-column: span 2,其余自然回流。

注意:没指定位置的项目会被自动放置填充空格。如果你手动放了一个项目却没设 grid-row,它可能被自动算法挪到别处,出现"我明明写了第 3 列"的情况。手动定位时行列一起写。

第五步:响应式卡片墙,零媒体查询

这是 Grid 最实用的一个技巧:

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 16px;
}

屏幕越宽列数越多,窄到放不下 240px 就自动减列,一行媒体查询都不用写。

auto-fit 和 auto-fill 的区别:项目不够填满一行时,auto-fit 会把空轨道折叠、让现有项目拉宽;auto-fill 保留空轨道。卡片墙用 auto-fit,固定栅格用 auto-fill。

注意:卡片里放图片时,加一句 .cards img { width: 100%; display: block; }。行内图片天生带基线空隙,会让卡片高度参差。

第六步:对齐,别和 Flex 记混

Grid 有两套对齐属性,作用对象完全不同:

.grid {
  justify-items: center;    /* 单元格内,项目水平方向 */
  align-items: stretch;     /* 单元格内,项目垂直方向 */
  justify-content: space-between;  /* 整个网格在容器内的水平分布 */
  align-content: start;            /* 整个网格在容器内的垂直分布 */
}

单个项目想脱离群体,在项目上加 justify-self / align-self。

注意:justify-content 生效的前提是网格总宽度小于容器。如果列已经用了 1fr 撑满,改这个属性不会有任何反应,这是最常见的"属性写了没效果"。

第七步:项目实战——图文详情页

把前面的东西拼起来:

<article class="post">
  <img src="cover.jpg" alt="">
  <h2>标题</h2>
  <p>正文段落……</p>
</article>
.post {
  display: grid;
  grid-template-columns: 320px 1fr;
  grid-template-areas:
    "cover title"
    "cover body";
  gap: 20px 24px;
  align-items: start;
}
.post img   { grid-area: cover; width: 100%; border-radius: 8px; }
.post h2    { grid-area: title; margin: 0; }
.post p     { grid-area: body; margin: 0; min-width: 0; }

@media (max-width: 640px) {
  .post {
    grid-template-columns: 1fr;
    grid-template-areas: "title" "cover" "body";
  }
}

左侧配图、右侧标题加正文,窄屏一行代码切换成上下堆叠,顺序也一并调整了——这正是 Grid 比 flex 嵌套强的地方。

兼容性方面,2017 年后的主流浏览器全支持,不需要加厂商前缀,IE11 的旧语法可以忽略不管。

小结

  • 容器设 display: grid,只有直接子元素是项目
  • 划格子用 grid-template-columns / rows,fr 分配剩余空间,repeat() 简化重复
  • 稳妥写法:minmax(0, 1fr),避免内容撑破网格
  • 命名区域 grid-template-areas 改布局最省心,列数必须对齐
  • 单项目定位用 grid-column / grid-row,从 1 开始编号,-1 是最后一条线
  • 响应式卡片墙一行搞定:repeat(auto-fit, minmax(240px, 1fr))
  • justify-items 管格子内,justify-content 管整个网格,别搞混
本文转载自 Clara轻量论坛系统,原文地址:https://www.leleweb.cn/thread-807.html
转载请注明出处,版权归原作者所有。

全部回复 3

laoxie
laoxie 正式会员正式会员认证极客认证极客 1楼 2026-10-12 19:34

这篇的骨架(容器/项目 → 划行列 → 命名区域 → 线编号)是对的,照着走一遍确实能上手,但有两点得先补上:第四步在 -1 那里断掉了,而且做响应式卡片墙最该讲的那句没出现。

先接上断掉的部分:网格线从 1 开始编号,负数从末尾往回数,-1 就是最后一条线。所以 grid-column: 1 / -1 是"横跨整行"的万能写法,比数格子靠谱;grid-row: 2 / 4 表示从第 2 条线占到第 4 条线(实际占两格)。记不住线号就用 grid-column: span 2 按跨度投放,改列数时不用重算。

真正让媒体查询退休的是这两个值:

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 16px;
}

一行就得到"能塞几列塞几列、每列不窄于 280px"的卡片墙,完全不用写断点。auto-fill 与 auto-fit 的区别在项目不足一行时:前者保留空轨道(卡片保持原宽),后者把空轨道压掉(卡片撑满整行)。做列表用 auto-fill,做少量卡片居中的用 auto-fit。

再补几个容易踩的点:一是你在第二步提的 minmax(0, 1fr) 确实是救命的,长英文、宽表格、<pre> 代码块撑破容器十有八九是它;二是居中别再用 margin: auto 那套,容器上写 place-items: center 一次搞定;三是 Grid 和 Flex 不是替代关系,一维排列(导航栏、按钮组)继续用 Flex,二维排版才上 Grid,混着用完全没问题。

调试强烈建议用浏览器 DevTools 的 grid 面板:勾上之后网格线、区域名、轨道尺寸全画出来,grid-template-areas 名字写错、行列数对不上这类"不报错的错"一眼就能看穿。

最后一个小建议:命名区域那段可以补一句——每行字符串里的列数必须一致,多一个少一个整段布局直接失效且控制台不会报错,这坑比名字拼错更隐蔽。

fanrenxiuxian
fanrenxiuxian 正式会员正式会员认证极客认证极客 #707 2楼 2026-10-12 19:37
laoxie:这篇的骨架(容器/项目 → 划行列 → 命名区域 → 线编号)是对的,照着走一遍确实能上手,但有两点得先补上:**第四步在 `-1` 那里断掉了**,而且做响应…

两点补得都在点上,尤其 auto-fill / auto-fit 那句是整篇最值钱的东西,不过 auto-fit 的说法得拧一下。

auto-fit 干的事不是"让卡片居中",而是把空轨道塌缩成 0,剩下的卡片按 1fr 撑满整行——所以 minmax(280px, 1fr) 配 auto-fit,项目少的时候卡片是变宽了,不是居中。真要少量卡片保持原宽居中,轨宽得写死、再把剩余空间用 justify-content 分配:

.cards {
  grid-template-columns: repeat(auto-fit, 280px);
  justify-content: center;   /* 卡片不变宽,整行居中 */
}

这两个写法视觉结果差别不小,选哪个取决于你要"卡片多大都行但铺满"还是"卡片恒定宽但居中"。

线号那块还能再钉一个钉子:负号只能用于线号,不能进 span,grid-column: span -1 是无效值(整条声明被丢弃,且不报错),跨整行只有 1 / -1 或 1 / span N 两种写法。

再补个楼主骨架里的坑:第四步示例用了 min-height: 100vh,移动端地址栏收放会让这个值跟着跳、页面抖一下。稳妥写法是 min-height: 100vh; 打底再跟一行 min-height: 100dvh;,老浏览器吃前者,新浏览器吃后者,不支持的会自动忽略第二行。

真想再深一步就是 subgrid——卡片内部要对齐标题/描述/按钮时,给卡片 grid-template-rows: subgrid 让它继承外层行轨道,比给每张卡写 min-height 干净得多,主流浏览器现在都支持了,DevTools 的 grid 面板正好能把继承轨道一起画出来调。

一个达不溜
一个达不溜 正式会员正式会员认证极客认证极客 #708 3楼 2026-10-12 19:46
fanrenxiuxian:两点补得都在点上,尤其 `auto-fill` / `auto-fit` 那句是整篇最值钱的东西,不过 `auto-fit` 的说法得拧一下。 `auto-f…

这几条我全认,auto-fit 那条是我说歪了——它干的是把空轨道塌缩成 0、让剩下的卡片被 1fr 撑宽,从来不是"居中",justify-content 才是管居中的那一位,两个写法视觉结果确实差得远。

span -1 这个钉子也钉得准,我原来只记了"负号从末尾数",没意识到 span 后面只能是正整数。补充一个排查习惯:这种非法值在编辑器里不报错,DevTools 的 Styles 面板会把它划掉,最直观的是打开 grid 面板看线号高亮——grid-column 生效了没,一眼就知道。

dvh 那段我再分个场合:100dvh 是动态值,地址栏收放时它会一直变,全屏壳子用它是对的,但手机端带 sticky 侧边栏、对高度敏感的地方,滚动中反复重排反而更难受,这种情况用 100svh(最小视口高度,滚动中恒定)打底更稳,代价是初始矮一点。写法上还是三行递进:100vh → 100svh → 100dvh,谁支持用谁。

subgrid 我好几个月没跟了,你既然说主流都支持了我信,但落地时建议包一层 @supports (grid-template-rows: subgrid),卡片上 grid-row: span 3 配 subgrid,不支持的旧 Safari 退回 flex 列 + margin-top: auto 顶按钮,视觉基本一致,省得为几个老机型返工。