CSS Grid 布局模块

CSS Grid 布局模块

引言

网格布局(Grid Layout)是 CSS 中一种全新的布局模型,在网格布局中,网格容器的内容是通过把它定位并对齐到一个网格中来完成布局的。

网格布局通过使用一个网格来控制其内容的布局:网格是一组相互交叉的水平线与垂直线,为网格容器的内容建立起尺寸与定位的坐标系统。网格布局具有以下特性:

  • 固定、弹性与基于内容的轨道尺寸函数;
  • 通过正向(正数)与反向(负数)数值网格坐标、命名网格线以及命名网格区域实现的显式项目放置;
  • 把项目自动放置到空白区域,包括用 order 重排,以及空间敏感的轨道重复;
  • 自动增删行列以容纳额外内容;
  • 通过外边距、沟槽与对齐属性控制对齐与间距;
  • 能够重叠内容,并用 z-index 控制层叠。

与面向单轴的弹性盒子布局(Flexible Box Layout)不同,网格布局针对二维布局做了优化:即在两个维度上都希望对内容进行对齐的布局。

尽管许多布局既可以用网格也可以用弹性盒子表达,但两者各有专长。网格强制执行二维对齐,采用自顶向下的布局方式,允许项目显式重叠,并具备更强大的跨轨道能力。弹性盒子专注于单轴内的空间分配,采用更简单的自底向上的布局方式,可用基于内容尺寸的换行系统来控制其次轴,并依赖底层的标记层级来构建更复杂的布局。

Flex Layout Grid Layout
Flex Layout Example Grid Layout Example

网格布局术语

网格线

网格线是网格中水平与垂直的分隔线。每条列或行的两侧各存在一条网格线。它们可以用数值索引来指称,也可以用作者指定的名称来指称。网格项目通过网格放置属性引用网格线,以确定自身在网格中的位置。

Grid Lines

以下两个示例都创建了三条列网格线和四条行网格线。

  • 使用网格线编号

Grid Lines Number

  • 使用显式命名的网格线

Grid Lines Name

网格轨道

网格轨道(grid track)是网格列或网格行的统称——换言之,它是两条相邻网格线之间的空间。每个网格轨道都被分配一个尺寸函数,用于控制该列或行可以增长到多宽或多高,进而控制其边界网格线之间的间距。相邻的网格轨道可以用沟槽分隔,除此之外它们紧密排列。

1
2
3
4
5
#grid {
display: grid;
grid-template-columns: 150px 1fr; /* 两列 */
grid-template-rows: 100px 1fr 100px; /* 三行 */
}

这里:

  • 列轨道有 2 条:第一列固定为 150px。第二列使用弹性尺寸,它是网格中未分配空间的函数,因此会随网格容器宽度的变化而变化。
    • 如果网格容器的使用宽度为 325px,则第二列宽 175px;
    • 如果使用宽度为 150px,则第二列为 0px,且任何置于该列中的内容都将溢出网格容器。
  • 行轨道有 3 条:第 1 行(100px)、第 2 行(1fr)、第 3 行(100px)。

轨道是一维的:一条列轨道贯穿整个网格的高度,一条行轨道贯穿整个网格的宽度。它不关心“和另一条轨道交叉的地方是什么”,只关心自己这条线方向上的尺寸。

Grid Tracks

网格单元格

网格单元(grid cell)是网格行与网格列的交叉处。它是在定位网格项目时可以被引用的最小网格单位。

上面的例子中:

  • 2 条列轨道 × 3 条行轨道 = 6 个网格单元格;
  • 每个单元格由四条网格线围成:左、右两条列线,上、下两条行线。

单元格是二维的:它同时属于某条行轨道和某条列轨道,面积是“行高 × 列宽”。

网格区域

网格区域(grid area)是用于布局一个或多个网格项目的逻辑空间。网格区域由一个或多个相邻的网格单元组成。它由四条网格线界定,每一侧各一条,并参与其所相交的网格轨道的尺寸计算。网格区域可以使用网格容器的 grid-template-areas 属性显式命名,也可以通过其边界网格线隐式引用。网格项目使用放置属性被分配到一个网格区域。

Grid Areas

隐式网格

grid-template-rows、grid-template-columns 与 grid-template-areas 属性定义了构成显式网格的固定数量轨道。当网格项目被定位到这些边界之外时,网格容器通过向网格添加隐式网格线来生成隐式网格轨道。这些线与显式网格共同构成隐式网格。grid-auto-rows 与 grid-auto-columns 属性为这些隐式网格轨道定尺寸,也为那些由 grid-template-areas 创建、但未被 grid-template-rows 或 grid-template-columns 显式定尺寸的任何显式网格轨道定尺寸。

grid-auto-flow 属性控制没有显式位置的网格项目的自动放置。一旦显式网格被填满(或者根本不存在显式网格),自动放置也会导致隐式网格轨道的生成。

嵌套网格与子网格项目

网格项目自身也可以通过 display: grid 成为网格容器。一般情况下,这个嵌套网格内容的布局与它所参与的父网格布局相互独立。

然而在某些情况下,可能需要让多个网格项目的内容彼此对齐。嵌套网格可以把其行与/或列的定义交由父网格容器决定,从而成为子网格。此时,子网格的网格项目参与父网格的尺寸计算,使两个网格的内容得以对齐。

子网格通过 grid-template-rows 或 grid-template-columns 的 subgrid 关键字建立,可以在任一轴或两个轴上子网格化。

例如,假设我们有一个由带标签的输入框列表组成的表单,我们希望标签与输入框对齐,这可以用子网格布局实现。

Grid Subgrid Label

重新排序与可访问性

网格布局赋予作者对文档进行大幅重排的强大能力。然而,这些能力并不能替代文档源码的正确排序。order 属性和网格放置不会影响非视觉媒体(如语音)中的排序。同样,在视觉上重排网格项目,也不会影响顺序导航模式的默认遍历顺序。

仅将 order 和 网格放置 属性被用于内容的视觉重新排序,而非逻辑重新排序。

Grid Order

网格容器

建立网格容器

Name ‘display’
values grid | inline-grid
  • grid:
    该值使元素生成一个网格容器盒子,当置于流式布局中时,它是块级的。
  • inline-grid:
    该值使元素生成一个网格容器盒子,当置于流式布局中时,它是行内级的。

网格容器为其内容建立一个独立的网格格式化上下文。这与建立独立的块格式化上下文相同,区别在于使用的是网格布局而非块布局,因此一些以块布局为前提设计的属性在网格布局语境下不适用。具体而言:

  • float 与 clear 对网格项目没有效果。不过,float 属性仍会影响网格容器子元素的 display 计算值,因为这发生在网格项目确定之前。
    Grid Float

  • vertical-align 对网格项目没有效果。

  • ::first-line 与 ::first-letter 伪元素不适用于网格容器,且网格容器不会为其祖先贡献首行格式化行或首字母。

若元素的指定 display 为 inline-grid,且该元素被浮动或绝对定位,则 display 的计算值为 grid。

尽管元素的计算值变成了 grid,但由于它同时是浮动或绝对定位元素,其宽度计算被 CSS 2.1 的定位/浮动规则接管了,转而使用 shrink-to-fit(收缩适应)。

Grid Absolute

网格限制

由于内存有限,UA(用户代理)可以将隐式网格的可能尺寸限制在某个由 UA 定义的上限之内(该上限应能容纳 [-10000, 10000] 范围内的网格线),并丢弃该上限之外的所有网格线。如果某个网格项目被放置在该上限之外,则其网格区域必须被钳制到这个受限网格之内。

钳制规则如下:

  • 如果该网格区域会跨越到受限网格之外,则其跨度(span)会被钳制到受限网格的最后一条网格线。
  • 如果该网格区域会被完全放置在受限网格之外,则其跨度必须被截断为 1,并将该区域重新定位到网格该侧的最后一个网格轨道之中。

如果某个 UA 仅支持每个维度上最多 1000 条轨道的网格,则以下放置属性:

1
2
3
4
.grid-item {
grid-row: 500 / 1500;
grid-column: 2000 / 3000;
}

最终将等价于:

1
2
3
4
.grid-item {
grid-row: 500 / 1001;
grid-column: 1000 / 1001;
}

网格项目

广义而言,网格容器(grid container)中的“网格项”(grid item)就是代表其流内内容的盒子。

网格容器的每个流内子元素都会成为一个网格项目,而每一段子文本序列都会被包裹进一个匿名的块容器网格项目。不过,如果该文本序列仅包含空白(即可以被 white-space 属性影响的字符),则它不会被渲染(就如同其文本节点被设为 display:none 一样)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div style="display: grid">

<!-- 网格项目:块级子元素 -->
<div id="item1">block</div>

<!-- 网格项目:浮动元素;浮动被忽略 -->
<div id="item2" style="float: left;">float</div>

<!-- 网格项目:包裹行内内容的匿名块盒 -->
anonymous item 3

<!-- 网格项目:行内子元素 -->
<span>
item 4
<!-- 网格项目不会在块级元素处被拆分 -->
<q style="display: block" id=not-an-item>item 4</q>
item 4
</span>
</div>

元素之间的空白会消失:它不会成为独立的网格项目,即使元素之间的文本确实会被包裹进匿名网格项目。

Grid Item

网格项目的显示

除非是子网格,网格项目会为其内容建立一个独立的格式化上下文。不过,网格项目是网格级盒子,而非块级盒子:它们参与其容器的网格格式化上下文,而非块格式化上下文。

如果某元素最近的祖先元素(跳过 display:contents 的祖先)的计算 display 值为 grid 或 inline-grid,则该元素自身的 display 值会被块化(blockified)。即使 grid 或 inline-grid 元素最终并未生成网格容器盒子(例如它被替换,或位于 display: none 子树中),块化仍会发生。

Grid Display

网格项目的尺寸

网格项目在给定维度上的自动尺寸计算,会因其自对齐值而不同:

  • normal
    • 如果网格项目没有首选宽高比,且在相关轴上没有自然尺寸(若它是替换元素),则该网格项目按 align-self: stretch 的方式计算尺寸。
    • 否则,网格项目按块级元素在相应轴上的尺寸计算规则来计算尺寸。
  • stretch
    • 使用非替换盒的行内尺寸计算规则,即拉伸适应尺寸(stretch-fit size)。
  • 所有其他取值
    • 将项目按 fit-content 设定尺寸。
对齐(Alignment) 非替换元素尺寸 替换元素尺寸
normal 填满网格区域 使用自然尺寸
stretch 填满网格区域 填满网格区域
start/center 等 fit-content 尺寸(类似浮动) 使用自然尺寸
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<style>
* {
box-sizing: border-box;
}
.container {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: repeat(5, 170px);
gap: 10px;
background-color: #89abd3;
}
.item {
background-color: pink;
}
.item1 {
align-self: normal;
}
.item2 {
align-self: stretch;
}
.item3 {
align-self: start;
}
.item4 {
align-self: center;
}
.item5 {
align-self: end;
}
</style>

<div class="container">
<div class="item item1">align-self: normal</div>
<img
class="item item1"
src="./10065e821ebafb73354c5ed204f8a1fc_1792054229717608364.png"
alt=""
/>
<div class="item item2">align-self: stretch</div>
<img
class="item item2"
src="./10065e821ebafb73354c5ed204f8a1fc_1792054229717608364.png"
alt=""
/>
<div class="item item3">align-self: start</div>
<img
class="item item3"
src="./10065e821ebafb73354c5ed204f8a1fc_1792054229717608364.png"
alt=""
/>
<div class="item item4">align-self: center</div>
<img
class="item item4"
src="./10065e821ebafb73354c5ed204f8a1fc_1792054229717608364.png"
alt=""
/>
<div class="item item5">align-self: end</div>
<img
class="item item5"
src="./10065e821ebafb73354c5ed204f8a1fc_1792054229717608364.png"
alt=""
/>
</div>

Grid Item Sizing

网格项目的外边距与内边距

由于相邻的网格项目独立地包含在由各自网格区域形成的包含块中,相邻网格项目的外边距不会折叠。

Grid Item Margins

网格项目上的百分比外边距与内边距,与块盒上的情况一样,是相对于其包含块的行内尺寸来解析的,例如在水平书写模式下,左/右/上/下的百分比都相对于其包含块的宽度解析。

Grid Item Paddings

Z 轴排序

当网格项目被定位到相交的网格区域时,甚至当它们因负外边距或定位而被置于互不相交的区域时,都可能发生重叠。网格项目的绘制顺序与行内块完全相同,区别在于使用的是经 order 修改后的文档顺序,而非原始文档顺序,并且 auto 以外的 z-index 值即使在 position 为 static 时也会创建层叠上下文(表现得就像 position 为 relative 一样)。因此,z-index 属性可以很容易地用来控制网格项目的 Z 轴顺序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<style>
.container {
display: grid;
grid-template-columns: repeat(7, 1fr);
grid-template-rows: repeat(2, 50px);
margin: 0 auto;
height: 400px;
width: 325px;
}
.item {
background-color: pink;
}
.item1 {
grid-column: 1 / 4;
grid-row: 1 / 2;
background-color: #89abd3;
}
.item2 {
grid-column: 3 / 6;
grid-row: 1 / 2;
}
.item3 {
grid-column: 5 /8;
grid-row: 1 / 2;
background-color: #89abd3;
}
.item4 {
grid-column: 1 / 4;
grid-row: 2 / 3;
background-color: #89abd3;
}
.item5 {
grid-column: 3 / 6;
grid-row: 2 / 3;
}
.item6 {
grid-column: 5 /8;
grid-row: 2 / 3;
background-color: #89abd3;
}
.first-index {
z-index: 2;
}
.second-index {
z-index: 1;
}
</style>

<div class="container">
<div class="item item1 first-index">item1 z-index</div>
<div class="item item2 second-index">item2 z-index</div>
<div class="item item3">item3 z-index</div>
<div class="item item4">item1 normal</div>
<div class="item item5">item2 normal</div>
<div class="item item6">item3 normal</div>
</div>

Grid Z-Index

定义网格

显式网格

grid-template-rows、grid-template-columns 与 grid-template-areas 三个属性共同通过指定网格容器的显式网格轨道来定义其显式网格。由于存在被放置在显式网格之外的网格项目,最终网格可能更大;此时会创建隐式轨道,这些隐式轨道将由 grid-auto-rows 和 grid-auto-columns 属性设定尺寸。

Grid Auto Rows/Columns

显式网格的尺寸,由以下二者中较大者决定:由 grid-template-areas 定义的行/列数,与由 grid-template-rows / grid-template-columns 设定尺寸的行/列数。任何由 grid-template-areas 定义、但未被 grid-template-rows / grid-template-columns 设定尺寸的行/列,其尺寸取自 grid-auto-rows / grid-auto-columns 属性。如果这些属性未定义任何显式轨道,则显式网格在每个轴上仍包含一条网格线。

Grid Larger Size

网格放置属性中的数字索引从显式网格的边缘开始计数。正索引从起始侧计数(从最靠近起始侧的显式线为 1 开始),负索引从结束侧计数(从最靠近结束侧的显式线为 -1 开始)。

Grid Negative Indexes

显式轨道尺寸

grid-template-columns 指定网格列的轨道列表,grid-template-rows 指定网格行的轨道列表。这些属性以空格分隔的轨道列表形式,指定了网格的线名称及轨道尺寸函数。

取值含义如下:

  • none
    表示该属性不创建任何显式网格轨道(但显式网格轨道仍可由 grid-template-areas 创建)。在没有显式网格时,任何行/列都将被隐式生成,其尺寸由 grid-auto-rows 和 grid-auto-columns 属性决定。

  • <track-list> | <auto-track-list>
    以一系列轨道尺寸函数与线名的形式指定轨道列表。每个轨道尺寸函数都可以指定为:一个长度;一个相对于网格容器尺寸的百分比;对占据该列或该行的内容的一种度量;或网格中剩余空间的一个分数。它也可以用 minmax() 记法指定为一个范围,从而可以把上述任意机制组合起来,为该列或该行分别指定最小与最大轨道尺寸函数。

  • subgrid <line-name-list>?

    • subgrid 取值表示该网格将采用其父网格在该轴上被跨越的那部分(子网格化的轴)。网格各行/列的尺寸不再被显式指定,而是取自父网格的定义;子网格的项目将参与与父网格共享的任何轨道的固有尺寸计算。
    • <line-name-list> 参数允许对与父网格共享的网格线做局部命名:若给定了 <line-name-list>,则所指定的各个 <line-names> 会被分配到子网格显式网格的各条线上,每行一个,从第 1 条线开始。多余的 <line-names> 会被忽略。
    • 若不存在父网格,或者该网格容器因其他原因被迫建立独立的格式化上下文,则使用值为初始值 none,且该网格容器不是子网格。未被子网格化的轴称为独立轴。

轨道尺寸

  • <length-percentage [0,∞]>
    一个非负的长度或百分比,按 CSS3 Values 的定义。<percentage> 值相对于网格容器的内部行内尺寸;在行网格轨道中,则相对于网格容器的内部块尺寸。若网格容器的尺寸取决于其轨道的尺寸,则为计算网格容器的固有尺寸之目的,该 <percentage> 必须按 auto 处理,随后在布局网格及其项目时相对于由此得到的网格容器尺寸解析。

    Grid Length Percentage

  • <flex [0,∞]>
    一个带 fr 单位的非负尺寸,用以指定该轨道的弹性系数。每个 <flex> 尺寸轨道按弹性因子比例分得剩余空间的一份。例如,给定轨道列表 1fr 2fr,两个轨道将分别占据剩余空间的 1/3 和 2/3。如果弹性因子之和小于 1,它们只会占据剩余空间的相应比例,而不会扩展填满整个空间。

    full flex partial flex
    Grid Flex Grid Partial Flex
  • minmax(min, max)
    定义一个大于或等于 min 且小于或等于 max 的尺寸范围。如果 max 小于 min,则 max 会以 min 为下限(实质上得到 minmax(min, min))。在 minmax() 里,fr 只能作为最大值(第二个参数),不能作为最小值(第一个参数)。

    Grid Minmax

  • auto

    • 作为最大值:表示占据该网格轨道的网格项目中最大的 max-content 贡献;但与 max-content 不同,它允许轨道被 align-content 与 justify-content 属性扩展。
    • 作为最小值:表示占据该网格轨道的网格项目中最大的最小尺寸(由 min-width/min-height 指定)。

当出现在 minmax() 记法之外时:等价于 minmax(auto, auto),表示上述最小值与最大值之间的范围。

  • max-content

表示占据该网格轨道的网格项目中最大的 max-content 贡献。

  • min-content

表示占据该网格轨道的网格项目中最大的 min-content 贡献。

  • fit-content(<length-percentage>)

表示公式 max(minimum, min(limit, max-content)),其中 minimum 表示一个 auto 最小值(通常但不总是等于 min-content 最小值),limit 是作为参数传给 fit-content() 的轨道尺寸函数。这实质上等于取 minmax(auto, max-content) 与 minmax(auto, limit) 中较小者。

命名网格线

虽然网格线始终可以用数值索引来指称,但线名可以让网格放置属性更易理解与维护。线名可以用 grid-template-rows 与 grid-template-columns 属性显式指派,也可以由 grid-template-areas 属性通过命名网格区域隐式指派。

其中同一条网格线可以具有多个名称。

Grid Named Lines

线名不能是关键字 span 或 auto.

repeat()

repeat() 表示轨道列表中被重复的一段,从而可以用更紧凑的形式书写大量具有重复模式的列或行。

1
2
3
4
5
6
grid-template-columns: 10px [col-start] 250px [col-end]
10px [col-start] 250px [col-end]
10px [col-start] 250px [col-end]
10px [col-start] 250px [col-end] 10px;
/* 与上面相同,但更易书写 */
grid-template-columns: repeat(4, 10px [col-start] 250px [col-end]) 10px;

repeat() 语法的一般形式大致为: repeat( [ <integer [1,∞]> | auto-fill | auto-fit ] , <track-list> )

第一个参数指定重复次数。第二个参数是一个轨道列表,它会被重复该次数。不过存在一些限制:

  • repeat() 记法不能嵌套.
  • auto-fill 或 auto-fit 不能与完全固有或弹性的尺寸组合。

repeat(auto-fill, ...) 的工作方式是:浏览器先拿到一个确定的轨道尺寸,然后用容器可用宽度除以这个尺寸(并考虑 gap),从而算出最大能重复的次数。
如果轨道尺寸本身是“不确定”的,这个除法就无法进行。

  • 完全内在尺寸(如 min-content、max-content、auto、fit-content())的大小由内容决定。在放置项目之前,浏览器不知道内容有多宽,因此无法预先知道一个轨道需要多大。
  • 灵活尺寸(fr 单位)的大小由剩余空间决定。在知道有多少个轨道之前,无法确定剩余空间,更无法反向推算出轨道数量,这构成了一个循环依赖。

完全内在/灵活的尺寸必须“先分配空间”,才能知道自己多大。auto-fill/auto-fit 无法处理这种先有鸡还是先有蛋的循环。

  • 无效写法:
1
2
3
repeat(auto-fill, min-content); /* 错误 */
repeat(auto-fit, auto); /* 错误 */
repeat(auto-fill, 1fr); /* 错误 */
  • 有效写法:
1
2
repeat(auto-fill, minmax(250px, 1fr)); /* 正确 */
repeat(auto-fit, minmax(min-content, 1fr)); /* 也可能有效,因为 1fr 是确定的 */

auto-fill 与 auto-fit

在独立轴上,当给定 auto-fill 作为重复次数时:若网格容器在相关轴上具有确定的首选尺寸或最大尺寸,则重复次数为「在不使网格(计入间隙后)溢出其网格容器内容盒的前提下,尽可能大的正整数」;若任何重复次数都会溢出,则为 1 次。否则,若网格容器在相关轴上具有确定的最小尺寸,则重复次数为满足该最小要求的最小正整数。

每条轨道若其最大轨道尺寸函数为确定值,则按该最大值看待;否则若其最小轨道尺寸函数为确定值,则按该最小值看待。若两者都是确定值,则以最小轨道尺寸函数为最大轨道尺寸函数托底。若两者都不是确定值,则重复次数为一。

用容器 W = 350px、gap = 0 举例:

情况 示例 代表尺寸 重复次数
max 是 definite,min 不是 minmax(auto, 120px) 取 max:120px floor(350/120)=2
min 是 definite,max 不是 minmax(100px, 1fr) 取 min:100px floor(350/100)=3
两者都 definite minmax(80px, 120px) max(120,80)=120px floor(350/120)=2
两者都 definite,但 max < min minmax(150px, 100px) max(100,150)=150px floor(350/150)=2
两者都不 definite minmax(auto, max-content) 规范兜底为 1 1

auto-fit 关键字的行为与 auto-fill 相同,区别在于:在网格项目放置完成之后,任何空的重复轨道会被折叠。

auto-fit auto-fill
Grid auto-fit Grid auto-fill

fr 单位

用 fr 单位定尺寸的轨道称为弹性轨道,因为它们会响应剩余空间而伸缩,类似基础尺寸为零的弹性项目在弹性容器中填充空间的方式。

剩余空间的分配发生在所有非弹性轨道尺寸函数都达到其最大值之后。这些行或列的总尺寸从可用空间中减去,得到剩余空间,随后按弹性系数比例分配到各弹性尺寸的行与列之间。

每条列或行的剩余空间份额可按 该列/行的 <flex> × <剩余空间> / <所有弹性系数之和> 计算。

介于 0fr 与 1fr 之间的 <flex> 值有一种稍显特殊的行为:当弹性系数之和小于 1 时,它们占取的空间将少于剩余空间的 100%。一个轨道的 <flex> 值实际上是对剩余空间某一比例的「请求」,其中 1fr 意为「剩余空间的 100%」;那么若该轴上的各轨道请求的总量超过 100%,这些请求会被重新平衡,以保持相同比例但恰好用满 100%。不过,若各轨道请求的量不足全额(例如三条轨道各为 .25fr),则它们各自恰好得到所请求的量(各得剩余空间的 25%,最后 25% 留空)。

除非作者确实想要这种「部分填充」行为,否则应坚持使用 ≥ 1 的取值;例如用 1fr 与 2fr 通常比用 .33fr 与 .67fr 更好,因为增删轨道时它们更可能按预期表现。

当可用空间为无限时(即网格容器的宽度或高度不确定时),弹性尺寸的网格轨道按其内容定尺寸,同时保持各自的比例。每条弹性尺寸网格轨道的使用尺寸计算方式是:先求出该轨道的 max-content 尺寸,再除以相应的弹性系数,得到「假想 1fr 尺寸」;取这些值中的最大值作为解析出的 1fr 长度(即弹性分数),再乘以各网格轨道的弹性系数,得出其最终尺寸。

grid-template-areas 属性

该属性指定命名网格区域,它们不与任何特定的网格项目绑定,但可被网格放置属性引用。grid-template-areas 属性的语法还提供了网格结构的可视化,使网格容器的整体布局更易理解。

取值含义如下:

  • none
    表示本属性不定义任何命名网格区域,同样也不定义任何显式网格轨道(不过显式网格轨道仍可能由 grid-template-columns 或 grid-template-rows 创建)。

  • <string>+

为 grid-template-areas 属性列出的每个独立字符串创建一行,并为该字符串中的每个单元格创建一列,解析方式如下:

按最长匹配语义,把字符串分词为下列记号:

  • 一段 ident 码点序列,表示一个命名单元格记号,其名称由该序列的码点组成。
  • 一段由一个或多个 “.”(U+002E FULL STOP)组成的序列,表示一个空单元格记号。
  • 一段空白序列,表示无内容(不产生记号)。
  • 一段其他任意字符的序列,表示一个垃圾记号(trash token)。

空单元格记号表示网格容器中一个未命名区域。
命名单元格记号创建同名的命名网格区域。
行内与行间的多个命名单元格记号会创建单个命名网格区域,跨越相应的网格单元格。
垃圾记号是语法错误,会使该声明无效。

所有字符串必须定义相同数量的单元格记号(命名单元格记号与/或空单元格记号),且至少有一个单元格记号,否则声明无效。若某个命名网格区域跨越多个网格单元格,但这些单元格不构成单个填满的矩形,则声明无效。

在序列化 grid-template-areas 的 <string> 值的指定值或计算值时,每个空单元格记号被序列化为单个 “.”(U+002E FULL STOP),相邻单元格记号之间以一个空格(U+0020 SPACE)分隔,其余所有空白都被省略。

grid-template-areas 属性会从模板中的命名网格区域生成隐式指派的线名。对于每个名为 foo 的命名网格区域,会创建四个隐式指派的线名:两个名为 foo-start(命名该区域的 row-start 与 column-start 线),以及两个名为 foo-end(命名该区域的 row-end 与 column-end 线)。

这些隐式指派的线名的行为与其他任何线名一样,只是它们不会出现在 grid-template-rows/grid-template-columns 的值中。即使定义了同名的显式指派线名,这些隐式指派的线名也只是「更多具有相同名称的线」而已。

由于命名网格区域是通过它所生成的隐式指派线名被引用的,显式添加同样形式(foo-start/foo-end)的命名线,实际上就创建了一个命名网格区域。这类隐式命名区域不会出现在 grid-template-areas 的值中,但仍可被网格放置属性引用。

Grid same name

grid-template 属性

grid-template 属性是用于在单条声明中设置 grid-template-columns、grid-template-rows 与 grid-template-areas 的简写。它有几种不同的语法形式:

  • none

把三个属性都设为其初始值(none)。

  • <‘grid-template-rows’> / <‘grid-template-columns’>

把 grid-template-rows 与 grid-template-columns 分别设为指定值,并把 grid-template-areas 设为 none。

1
grid-template: auto 1fr / auto 1fr auto;

等价于

1
2
3
grid-template-rows: auto 1fr;
grid-template-columns: auto 1fr auto;
grid-template-areas: none;

[ <line-names>? <string> <track-size>? <line-names>? ]+ [ / <explicit-track-list> ]?

  • 把 grid-template-areas 设为所列出的各字符串。
  • 把 grid-template-rows 设为每个字符串之后的 <track-size>(缺失的尺寸补以 auto),并拼接各尺寸之前/之后定义的命名线。
  • 把 grid-template-columns 设为斜杠之后指定的轨道列表(若未指定则为 none)。

这种语法允许作者让轨道名称与尺寸和相应的网格区域在书写上对齐:

1
2
3
grid-template: [header-top] "a a a"      [header-bottom]
[main-top] "b b b" 1fr [main-bottom]
/ auto 1fr auto;

等价于

1
2
3
4
grid-template-areas: "a a a"
"b b b";
grid-template-rows: [header-top] auto [header-bottom main-top] 1fr [main-bottom];
grid-template-columns: auto 1fr auto;

隐式轨道尺寸

grid-auto-columns 与 grid-auto-rows 属性用于指定那些未通过 grid-template-rows 或 grid-template-columns 设定大小的网格轨道(track)的尺寸。如果指定了多个尺寸值,系统会根据需要重复该模式,以确定受影响轨道的大小。

  • 在最后一个显式设定了大小的轨道之后,紧随其后的第一个轨道将采用所指定尺寸序列中的第一个值,后续轨道依此类推;
  • 在显式网格区域之前的最后一个隐式网格轨道,则采用所指定尺寸序列中的最后一个值,并以此类推向反方向延伸。

grid-auto-flow 属性

未被显式放置的网格项目,由自动放置算法自动放入网格容器中的未占用空间。grid-auto-flow 控制自动放置算法的工作方式,精确指定自动放置的项目如何流入网格。

  • row
    自动放置算法通过依次填满每一行来放置项目,必要时添加新行。若既未给出 row 也未给出 column,则假定为 row。

  • column
    自动放置算法通过依次填满每一列来放置项目,必要时添加新列。

  • dense
    若指定,则自动放置算法使用「密集」打包算法,当后面出现较小项目时会尝试填补网格中较早的空洞。这可能使项目出现「乱序」——当这样做能填补较大项目留下的空洞时。若省略,则使用「稀疏」算法:放置算法在放置项目时只会沿网格「向前」移动,绝不回退去填补空洞。这确保所有自动放置项目都「按顺序」出现,即使因此留下了本可被后续项目填补的空洞。

放置网格项目

网格项目的网格区域在网格中的位置由其放置定义,放置由网格位置与网格跨度组成:

  • 网格位置
    网格项目在每个轴上于网格中的位置。网格位置可以是确定的(被显式指定),也可以是自动的(由自动放置决定)。

  • 网格跨度
    网格项目在每个轴上占据多少条网格轨道。某个轴上的网格跨度可以是隐含的、显式的或自动的:

    • 若其网格放置属性的 *-start 与 *-end 两个取值都指定了线,则其网格跨度是隐含的。
    • 若它具有显式的 span 值,则其网格跨度是显式的。
    • 否则其网格跨度是自动的:若它在该轴上被子网格化,则其网格跨度由其 <line-name-list> 决定;否则其网格跨度为 1。

命名区域

通过在 grid-area 中指定区域名称,可以把项目放入某个命名网格区域。

1
2
3
4
article {
grid-area: main;
/* 把该项目放入命名区域 "main"。 */
}

项目也可以只与某个命名网格区域部分对齐,而让其他边缘对齐到别的线:

1
2
3
4
.one {
grid-row-start: main;
/* 把 row-start 边缘对齐到 "main" 命名区域的起始边缘。 */
}

数值索引与跨度

网格项目可以用数字来定位与定尺寸:

1
2
3
4
5
.two {
grid-row: 2; /* 把项目放入第二行。 */
grid-column: 3; /* 把项目放入第三列。 */
/* 等价于 grid-area: 2 / 3; */
}

默认情况下,网格项目的跨度为 1。不同的跨度可以显式给出:

1
2
3
4
5
6
7
8
9
10
11
.three {
grid-row: 2 / span 5;
/* 从第 2 行开始,
向下跨 5 行(结束于第 7 行)。 */
}

.four {
grid-row: span 5 / 7;
/* 结束于第 7 行,
向上跨 5 行(起始于第 2 行)。 */
}

命名线与跨度

除了用数字计数线,也可以用线名来引用线:

1
2
3
4
.five {
grid-column: first / middle;
/* 从名为 "first" 的线跨到名为 "middle" 的线。 */
}

若存在多条同名线,它们实际上建立了一个命名网格线集合,可以通过按名称过滤放置来专门索引:

1
2
3
4
5
6
7
.six {
grid-row: text 5 / text 7;
/* 在第 5 条与第 7 条名为 "text" 的线之间跨越。 */
grid-row: text 5 / span text 2;
/* 同上——从第 5 条名为 "text" 的线开始,
再跨过两条 "text" 线,到达第 7 条。 */
}

自动放置

网格项目可以被自动放入下一个可用的空网格单元格;若没有剩余空间,则扩展网格。

1
2
3
.eight {
grid-area: auto; /* 初始值 */
}

若项目应占据不止一个单元格,自动放置可以与显式跨度结合使用:

1
2
3
4
.nine {
grid-area: span 2 / span 3;
/* 自动放置的项目,覆盖两行三列。 */
}

自动放置算法是「横向搜索并添加行」还是「横向搜索并添加列」,由 grid-auto-flow 属性控制。

基于线的放置

1
2
3
4
5
<grid-line> =
auto |
<custom-ident> |
[ [ <integer [-∞,-1]> | <integer [1,∞]> ] && <custom-ident>? ] |
[ span && [ <integer [1,∞]> || <custom-ident> ] ]

grid-row-start、grid-column-start、grid-row-end 与 grid-column-end 属性通过向网格项目的放置贡献一条线、一个跨度或什么都不贡献(自动),来确定该网格项目在网格中的尺寸与位置,从而指定其网格区域的 inline-start、block-start、inline-end 与 block-end 边缘。

取值含义如下:

  • <custom-ident>

    • 首先尝试把网格区域的边缘匹配到某个命名网格区域:若存在一条线名恰为 <custom-ident>-start 的线(用于 grid--start)或 <custom-ident>-end 的线(用于 grid--end),则把第一条这样的线贡献给该网格项目的放置。
    • 否则,视同把整数 1 与该 <custom-ident> 一起指定。
  • [ <integer [-∞,-1]> | <integer [1,∞]> ] && <custom-ident>?
    把第 N 条网格线贡献给该网格项目的放置。若给定负整数,则反向计数,从显式网格的末端边缘开始。若给定了名称作为 <custom-ident>,则只计具有该名称的线。若具有该名称的线不够多,则为找到此位置之目的,假定所有隐式网格线都具有该名称。

  • span && [ <integer [1,∞]> || <custom-ident> ]
    向该网格项目的放置贡献一个网格跨度,使该网格项目网格区域的相应边缘,在相应方向上距其对侧边缘 N 条线。例如 grid-column-end: span 2 表示从 grid-column-start 线沿末端方向数的第二条网格线。若给定了名称作为 <custom-ident>,则只计具有该名称的线。若具有该名称的线不够多,则为计算此跨度之目的,假定显式网格中与搜索方向相应一侧的所有隐式网格线都具有该名称。

  • auto
    该属性对该网格项目的放置不作任何贡献,表示自动放置,或默认跨度为 1。

放置简写

grid-row 与 grid-column 属性分别是 grid-row-start/grid-row-end 与 grid-column-start/grid-column-end 的简写。

若指定两个 <grid-line> 值,则斜杠之前的值赋给 grid-row-start/grid-column-start 长写属性,斜杠之后的值赋给 grid-row-end/grid-column-end 长写属性。

当省略第二个值时,若第一个值是 <custom-ident>,则 grid-row-end/grid-column-end 长写属性也被设为该 <custom-ident>;否则设为 auto。

grid-area 属性是 grid-row-start、grid-column-start、grid-row-end 与 grid-column-end 的简写。

若指定四个 <grid-line> 值,则 grid-row-start 设为第一个值、grid-column-start 设为第二个值、grid-row-end 设为第三个值、grid-column-end 设为第四个值。

当省略 grid-column-end 时,若 grid-column-start 是 <custom-ident>,则 grid-column-end 设为该 <custom-ident>;否则设为 auto。

当省略 grid-row-end 时,若 grid-row-start 是 <custom-ident>,则 grid-row-end 设为该 <custom-ident>;否则设为 auto。

当省略 grid-column-start 时,若 grid-row-start 是 <custom-ident>,则全部四个长写属性都设为该值;否则设为 auto。

绝对定位

以网格容器为包含块

若某个绝对定位元素的包含块由网格容器生成,则该包含块对应于由其网格放置属性所确定的网格区域。随后,偏移属性(top/right/bottom/left)照常表示从该包含块相应边缘向内的偏移。

虽然把元素绝对定位到网格容器确实能让它对齐到容器的网格线,但这类元素不占据空间,也不以其他方式参与网格的布局。

Grid Contain Absolute

注意事项

网格放置冲突处理

  • 若网格项目的放置包含两条线,而起始线比末端线更靠末端,则交换这两条线。
    Grid Swap Lines

  • 若起始线与末端线相等,则移除末端线。
    Grid Equal Line

  • 若放置包含两个 span,则移除由末端网格放置属性所贡献的那一个。
    Grid Two Spans

  • 若放置只包含一个针对命名线的跨度,则把它替换为跨度为 1。

求 fr 的尺寸

  • 令剩余空间为待填充空间减去非弹性网格轨道的基础尺寸。
  • 令弹性系数和为各弹性轨道弹性系数之和。若该值小于 1,则把它设为 1。
  • 令假想 fr 尺寸为剩余空间除以弹性系数和。
  • 若假想 fr 尺寸与某条弹性轨道弹性系数之积小于该轨道的基础尺寸,则重新开始本算法,把所有此类轨道当作非弹性轨道处理。
  • 返回该假想 fr 尺寸。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<style>
* {
box-sizing: border-box;
}
.container {
display: grid;
grid-template-columns:
75px minmax(75px, 20px) minmax(75px, 1fr)
minmax(75px, 2fr);
gap: 10px;
width: 360px;
text-align: center;
}
.item {
background-color: pink;
}
</style>
<div class="container">
<div class="item">item</div>
<div class="item">item</div>
<div class="item">item</div>
<div class="item">item</div>
</div>

此时网格项目的尺寸分别为 75px / 75px / 75px / 105px, minmax(75px, 1fr) 并没有进行剩余空间的占用,但 minmax(75px, 2fr) 占用了所有的剩余空间。

  1. 计算剩余空间: 容器 360px,减去 3 个 10px 的 gap,再减去前两列固定的 75px+75px,剩余 180px 可供第 3、4 列分配。
  2. 计算弹性系数和: 第 3 列和第 4 列的弹性系数分别为 1fr 和 2fr,弹性系数和为 3fr。
  3. 计算假想 fr 尺寸: 假想 fr 尺寸为 180px/3,即 60px。
  4. 弹性轨道弹性系数之积对比该轨道的基础尺寸: 第 3 列的弹性系数积为 1fr*60px=60px,该轨道的基础尺寸 75px, 60px 大于 75px,所以第 3 列当作非弹性轨道处理。所以第 3 列的尺寸为 75px。
  5. 第 3 列冻结后,原本打算分给它的空间被释放。第 4 列是唯一未冻结的灵活轨道,于是它获得了全部剩余空间:180px(总可用空间)- 75px(第 3 列冻结占用的空间)= 105px。

参考文献

grid额外拓展


CSS Grid 布局模块
https://www.my-web.cn/css/grid/
发布于
2026年11月10日
许可协议