CSS探索系列之grid布局
网格布局(CSS Grid Layout) 把容器划成行和列交叉的格子,项目按网格线或命名区域落到二维平面上的任意一块。Flex 一次只排一根轴;Grid 同时管两根轴,页面骨架、卡片墙、不规则拼贴都从这里下手。
基本示例
<div class="wrapper">
<div class="item one">One</div>
<div class="item two">Two</div>
<div class="item three">Three</div>
<div class="item four">Four</div>
<div class="item five">Five</div>
<div class="item six">Six</div>
</div>
.wrapper {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
grid-auto-rows: minmax(100px, auto);
}
.item {
border: 2px solid rgb(233, 171, 88);
border-radius: 5px;
background-color: rgba(233, 171, 88, 0.5);
padding: 1em;
color: #d9480f;
}
/* .one 与 .two 在 (列2, 行1) 重叠:Grid 允许项目占同一区域 */
.one {
grid-column: 1 / 3;
grid-row: 1;
}
.two {
grid-column: 2 / 4;
grid-row: 1 / 3;
}
.three {
grid-row: 2 / 5;
grid-column: 1;
}
.four {
grid-column: 3;
grid-row: 3;
}
.five {
grid-column: 2;
grid-row: 4;
}
.six {
grid-column: 3;
grid-row: 4;
}

基础术语
- 网格(Grid):一组相交的水平线和垂直线,划出列和行。
- 网格线(Grid line):分割轨道的竖线、横线。可以从 1 起用数字引用,也可以起名字。
- 网格轨道(Grid track):两条相邻网格线之间的一条带,不是行就是列。
- 网格行(Grid row):水平轨道。
- 网格列(Grid column):垂直轨道。
- 网格单元格(Grid cell):四条线围成的最小格子。
- 网格区域(Grid area):一个或多个单元格拼成的矩形。
- 网格间隙(Gutter):轨道与轨道之间的间距,由
gap控制。 - 网格轴:行轴是行内方向(水平书写时左右),对齐属性带
justify-;列轴是块方向(水平书写时上下),对齐属性带align-。
display 属性
display: grid 生成块级网格容器,display: inline-grid 生成行内级网格容器。两者内部排版规则相同,差别只在容器自己在文档流里占一块还是挤在一行里。
它们会建立网格格式化上下文(Grid Formatting Context,GFC),不是 BFC。容器的每个子元素都是网格项(含 ::before、::after)。文本节点会生成匿名网格项。绝对定位的子元素不参与网格排版。
备注:float、clear、vertical-align 对网格项无效。
注意: 现行 Grid 语法与 IE11 的 -ms-grid 不是同一套。下文「常用值」指已支持现行 Grid 的浏览器里可放心用的取值;「其余取值」仍有缺口。
容器属性
grid-template-columns、grid-template-rows、grid-template-areas、grid-templategrid-auto-columns、grid-auto-rows、grid-auto-flowgridgap、row-gap、column-gapjustify-items、align-items、place-itemsjustify-content、align-content、place-content
grid-template-columns
定义垂直网格线的名称,以及显式列轨道的尺寸。
初始值:none(列数和列宽交给 grid-auto-columns 在隐式网格里补)。
grid-template-columns: [线名] 轨道尺寸 [线名] 轨道尺寸 ...;
常用值:
- 长度:
px、rem、em等,列宽不随内容或容器伸缩。 - 百分比:相对网格容器内容区宽度。容器宽度自己还在靠内容撑开(例如未设宽的
inline-grid)时,百分比按auto处理。 fr:弹性系数,分的是扣掉固定轨道和 gap 之后的剩余空间。auto:先按内容估尺寸。没有fr轨道时,剩余空间还可以被justify-content: stretch拉长;和fr共存时,内容定宽之后剩下的才给fr,不是先分光fr再把余量给auto。minmax(min, max):列宽夹在上下限之间。min不能是fr。repeat():重复一段轨道定义。第一个参数可以是次数,也可以是auto-fill、auto-fit。
其余取值(存在浏览器兼容限制):
min-content:按内容最小宽度(中文通常落到最宽的那个字)。max-content:按内容最大自然宽度(最宽的那一格撑开)。fit-content(<length-percentage>):须写成函数,如fit-content(200px),相当于min(max-content, max(min-content, 指定长度)),列宽尽量贴近内容,但不超过指定长度。
/* 不带命名:3 条列轨道,4 根垂直网格线(数字线号 1、2、3、4 是隐式的) */
grid-template-columns: 100px 1fr 200px;
/* 显式命名(自定义线名不能以数字开头) */
grid-template-columns: [start] 100px [center-start] 1fr [center-end] 200px [end];
/* 一条线可以有多个名字,空格分隔 */
grid-template-columns: [left sidebar-start] 240px [sidebar-end main-start] 1fr [main-end];
/* repeat 里的名字会按次数复制:
1 号线:col-start
2 号线:col-end col-start
3 号线:col-end col-start
4 号线:col-end
col-start 2 → 第 2 条名叫 col-start 的线
*/
grid-template-columns: repeat(3, [col-start] 1fr [col-end]);
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
注意:
1fr实际按minmax(auto, 1fr)处理,下限仍是内容尺寸。列里有长单词或不换行文本时,轨道收不下去,会出现横向溢出。要允许收到内容之下,写成minmax(0, 1fr)。- 百分比相对容器总宽,
fr相对剩余宽;两者混用时先结算百分比和固定尺寸,再分fr。 auto-fill:能放下多少条轨道就放多少,空轨道仍占位。auto-fit:同样尽量多放,但会把空轨道收成 0,让已有轨道去填容器。
grid-template-rows
定义水平网格线名称与显式行轨道尺寸。初始值、语法、轨道函数、线名规则与 grid-template-columns 相同,只是作用在行上。
grid-template-areas
用命名区域画出整张网格。每个字符串是一行,字符串里空格分开的单词是一列;每一行的列数必须相同,否则整条声明无效。. 表示空格。同一名字会合并成一块区域,这块必须是矩形。
.grid {
display: grid;
grid-template-columns: 200px 1fr 200px;
grid-template-rows: 80px 1fr 60px;
grid-template-areas:
"header header header"
"sidebar main aside"
"footer footer footer";
}
注意: 区域图给出的列数应与 grid-template-columns 一致;对不上时,grid-template-areas 被忽略。没有写 grid-template-rows 时,区域会按行数长出隐式行,高度由内容决定。
grid-template
grid-template-rows、grid-template-columns、grid-template-areas 的简写。
grid-template: <grid-template-rows> / <grid-template-columns>;
grid-template: <grid-template-areas> <grid-template-rows> / <grid-template-columns>;
grid-template: 100px 200px / 1fr 2fr 1fr;
/* 等价于 */
grid-template-rows: 100px 200px;
grid-template-columns: 1fr 2fr 1fr;
grid-template-areas: none;
grid-template:
"header header header" 80px
"sidebar main aside" 1fr
"footer footer footer" 60px
/ 200px 1fr 200px;
/* 等价于 */
grid-template-rows: 80px 1fr 60px;
grid-template-columns: 200px 1fr 200px;
grid-template-areas:
"header header header"
"sidebar main aside"
"footer footer footer";
grid-auto-columns
指定隐式列的轨道尺寸。初始值:auto。取值与 grid-template-columns 相同。
显式列由 grid-template-columns 画好。网格项落到显式列之外,浏览器才补隐式列,宽度听 grid-auto-columns。
.grid {
display: grid;
grid-template-columns: 100px 200px; /* 只有 2 列,列线 1→2→3 */
grid-auto-columns: 150px;
}
.item {
grid-column: 2 / 5; /* 要到列线 5,自动补出第 3、4 列 */
}
grid-auto-rows
指定隐式行的轨道尺寸。初始值、取值、触发方式和 grid-auto-columns 对称。
grid-auto-flow
控制未指定坐标的网格项怎么自动往格子里填,以及隐式网格往行长还是往列长。
初始值:row
常用值:
row:按行填,从左到右、从上到下;不够就加隐式行。column:按列填,从上到下、从左到右;不够就加隐式列。dense:尽量拿后面的项去填前面留下的空洞。单独写dense时方向仍是row。row dense:与单独写dense相同。column dense:按列做密集填充。
注意: dense 会打乱视觉顺序和 DOM 顺序的对应关系,读屏和 Tab 仍跟源码走。
grid
同时设置显式网格、隐式网格和自动放置,是 grid-template-*、grid-auto-*、grid-auto-flow 的超集简写。没写到的分量会被重置成初始值。
grid: <grid-template>
grid: <grid-template-rows> / <grid-template-columns>
grid: <grid-template-rows> / auto-flow [dense?] <grid-auto-columns>;
grid: auto-flow [dense?] <grid-auto-rows> / <grid-template-columns>;
grid: 100px 200px / 1fr 2fr;
/* 等价于 */
grid-template-rows: 100px 200px;
grid-template-columns: 1fr 2fr;
grid-template-areas: none;
grid-auto-rows: auto;
grid-auto-columns: auto;
grid-auto-flow: row;
/* auto-flow 写在 / 右侧:grid-auto-flow 为 column */
grid: 100px 200px / auto-flow 1fr;
/* auto-flow 写在 / 左侧:grid-auto-flow 为 row */
grid: auto-flow 1fr / 100px 200px;
注意: / 每一侧只能表达一种定义方式,要么显式,要么隐式,不能同一侧同时写 grid-template-rows 和 grid-auto-rows(列同理)。
gap
设置轨道之间的间距,不作用到容器内边缘。gap 不计入 fr 的「剩余空间」分母之外的固定部分,算 fr 之前会先扣掉。
row-gap: 16px; /* 行与行(纵向) */
column-gap: 12px; /* 列与列(横向) */
gap: 16px; /* 行列同一间距 */
gap: 16px 12px; /* row-gap column-gap */
grid-row-gap、grid-column-gap、grid-gap 是早期名字,现在是 row-gap、column-gap、gap 的别名。新代码用后者。
属性浏览器兼容: 弹性容器上的 gap 见 Flex 篇;网格容器上,现行 Grid 浏览器都支持。IE11 的旧 Grid 不支持。
网格里的对齐分两层:项目在各自格子里(*-items、*-self),以及整张轨道在容器里(*-content)。
justify-items
网格项在格子内沿行轴对齐。水平书写时就是水平对齐。
初始值:normal。在网格里表现与 stretch 相同。有固有宽高比的替换元素(如图片)不会被拉变形,按 start 处理。
常用值:
stretch:拉满格子的行轴尺寸。start:靠行轴起点。end:靠行轴终点。center:行轴居中。
其余取值(存在浏览器兼容限制):
left、right:物理左、右。所处轴向不与行内轴平行时,按start处理。self-start、self-end:跟项目自身书写方向对齐,可以和容器的start、end不一致。flex-start、flex-end:在 Grid 里分别等同于start、end,为兼容 Flex 写法而保留。safe、unsafe:溢出修饰符,须搭配对齐关键字。safe在放不下时改回起点对齐。移动端低版本 Safari、小程序谨慎使用。
align-items
网格项在格子内沿列轴对齐。水平书写时就是垂直对齐。
初始值:normal。在网格里表现与 stretch 相同。
常用值:
stretch:拉满格子的列轴尺寸。start:靠列轴起点(水平书写时相当于顶部)。end:靠列轴终点(水平书写时相当于底部)。center:列轴居中。baseline:按第一行文字基线对齐。
其余取值(存在浏览器兼容限制):
self-start、self-end:跟项目自身书写方向对齐。first baseline、last baseline:显式指定第一、最后基线;baseline等同于first baseline。flex-start、flex-end:在 Grid 里分别等同于start、end。safe、unsafe:含义与justify-items相同。
place-items
align-items 和 justify-items 的简写:第一个值给列轴,第二个值给行轴。
place-items: <align-items> <justify-items>;
只写一个值且对两个属性都合法时,两轴都用它。
justify-content
所有列轨道在容器内沿行轴怎么分布。列宽总和小于容器时才看得出效果。
初始值:normal
常用值:
start:列轨道靠容器行轴起点(水平书写时相当于左)。end:靠行轴终点。center:行轴居中。space-between:首尾贴边,列与列间隔相等。space-around:每列两侧间隔相等,因此列与列之间的间隔是列与边缘的两倍。space-evenly:列与列、列与边缘的间隔都相等。stretch:把auto列拉宽,填满容器。
其余取值(存在浏览器兼容限制):
left、right:物理左、右。flex-start、flex-end:在 Grid 里分别等同于start、end。safe、unsafe:溢出修饰符。
align-content
所有行轨道在容器内沿列轴怎么分布。行高总和小于容器时才看得出效果。
初始值:normal
常用值:
start:行轨道靠列轴起点(水平书写时相当于顶部)。end:靠列轴终点。center:列轴居中。space-between:首尾贴边,行与行间隔相等。space-around:每行两侧间隔相等。space-evenly:行与行、行与边缘的间隔都相等。stretch:把auto行拉高,填满容器。
其余取值(存在浏览器兼容限制):
flex-start、flex-end:在 Grid 里分别等同于start、end。baseline、first baseline、last baseline:按行基线对齐。safe、unsafe:溢出修饰符。
place-content
align-content 和 justify-content 的简写:第一个值给列轴,第二个值给行轴。
place-content: <align-content> <justify-content>;
只写一个值且对两个属性都合法时,两轴都用它。
子项目属性
设置在网格项上:
grid-row-start、grid-row-end、grid-column-start、grid-column-endgrid-row、grid-column、grid-areajustify-self、align-self、place-selfz-index、order
grid-row-start、grid-row-end
指定网格项的起始、结束行线。
初始值:auto(交给 grid-auto-flow 自动放置,跨度相当于 span 1)。
常用值:
auto:自动放置。- 整数:第几条行线,如
grid-row-start: 2。负数从显式网格末尾倒数,不算后面多出来的隐式轨道。 span <integer>:跨越指定条数的行,如grid-row-end: span 2。
其余取值(存在浏览器兼容限制):
custom-ident:用命名行线,如grid-row-start: header。integer custom-ident:第 n 条叫这个名字的线,如grid-row-start: 2 content。span custom-ident、span integer custom-ident:按名字跨越。
规则:
- 起始线编号大于结束线时,浏览器对调两端。
- 只写
grid-row-start时,结束线按auto(跨 1 行)。 span多写在结束线上;写在起始线上表示从结束线往回跨。
grid-column-start、grid-column-end
指定起始、结束列线。语法、取值、规则与 grid-row-* 对称。
grid-row、grid-column
分别是行线、列线起止的简写。
grid-row: <grid-row-start> / <grid-row-end>;
grid-column: <grid-column-start> / <grid-column-end>;
grid-area
两种用法:行列定位和区域名称。
行列定位,顺序是行起、列起、行止、列止:
grid-area: <row-start> / <col-start> / <row-end> / <col-end>;
区域名称,配合 grid-template-areas:
.grid {
display: grid;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.footer { grid-area: footer; }
justify-self、align-self、place-self
单个项目覆盖容器上的 justify-items、align-items。
初始值:auto(采用容器上的对应属性)。其余常用值、其余取值与 justify-items、align-items 相同。
place-self: <align-self> <justify-self>;
z-index
网格项可以重叠,用 z-index 比谁在上面。数值越大越靠上。非 auto 的 z-index 会让该网格项形成层叠上下文。
order
只影响自动放置时谁先填格子,以及 z-index 同为 auto 时谁先绘制。初始值 0,可取负。数值越小越早放置、绘制越靠下。已经用 grid-row、grid-column、grid-area 钉死坐标的项,格子位置不受 order 改动。Tab 顺序和读屏仍跟源码走。
常见问题
Grid 容器内的子项目是流式布局吗?
不是普通流,也不是 Flex 那种沿一根轴依次排。Grid 是二维落位:坐标来自网格线或命名区域,项目可以重叠。
没写坐标时,浏览器按 grid-auto-flow(以及 order)依 DOM 顺序填格子。这是在往单元格里填,不是文档流那种一个接一个挤、挤不下就换行。自动放置会用到 DOM 顺序,但排版模型仍是网格。
隐式网格行、列是如何触发的?
网格项落到显式网格外面,或自动放置把显式格子用完,浏览器就补隐式行、列。尺寸分别听 grid-auto-rows、grid-auto-columns。
subgrid
subgrid 让子网格沿某一方向沿用父网格的轨道(含间隙),嵌套时才能和父级格子对齐。子项自己要先跨过父级的多条轨道,再在那一向上写 subgrid。行、列可以分别设。
.child {
display: grid;
grid-template-columns: subgrid;
grid-column: span 3;
}
其余取值(存在浏览器兼容限制):Firefox 71+、Safari 16+、Chrome 117+。IE11、旧版 Safari 不支持。需要嵌套对齐又要兼容旧环境时,把轨道尺寸在子级再写一遍。
小结
Grid 先画轨道和间隙,再让项目按线或按区域落位。显式轨道是画好的格子,越界和自动放置会长出隐式轨道。对齐要分清是「在各自格子里」还是「整张网格在容器里」。