CSS Grid 不仅重塑了我们思考和构建网页布局的方式,而且还有助于编写更具弹性的代码,取代了我们之前使用的“hacky”技术,在某些情况下,甚至无需依赖针对特定分辨率和视口的代码。网页开发这个时代的酷点在于,**我们能够用更少的代码做更多的事情。**
在本文中,我们将开始深入了解 CSS Grid 的强大功能,方法是构建几个常见的响应式导航布局。这比你想象的要容易,而且由于 CSS Grid 旨在实现响应式,因此它需要的代码量少于在各处编写媒体查询。让我们开始吧!
布局 #1:英雄内容和文章列表
查看 CodePen 上的示例
英雄内容和文章列表,作者:Juan Martín García (@imjuangarcia)
在 CodePen 上。
我们将从创建常见的网站布局开始这一系列示例:一个全宽的英雄区域,下方是一个卡片网格。
这两个元素都将响应窗口大小调整并相应地进行调整。虽然乍一看这似乎需要很多代码,但响应式行为仅使用**六行 CSS Grid 代码完成,并且无需编写任何@media
规则**。让我们分解代码以了解发生了什么
英雄区域
让我们看看.hero
元素的代码
<section class="hero">
<h1>You thirsty?</h1>
<article>
<p>Explore local breweries with just one click and stirred by starlight across the centuries light years great turbulent clouds circumnavigated paroxysm of global death.</p>
<a href="#breweries">Browse Breweries</a>
</article>
</section>
.hero {
/* Photo by mnm.all on Unsplash */
background: url('https://images.unsplash.com/photo-1518176258769-f227c798150e') center;
background-size: cover;
padding: 4rem 2rem;
/* Grid styles */
display: grid;
align-items: center;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}
我们有一堆背景样式来启用啤酒背景,一些填充以将内容与屏幕边缘分隔开,然后是三行网格样式
- 第一行(
display: grid;
)将.hero
元素的行为更改为网格容器。这意味着.hero
内部的元素现在是网格项。 - 第二行(
align-items: center;
)将在我们的网格上垂直居中列。但是这两行本身不会有任何作用,直到我们设置网格的列。 - 这就是第三行发挥作用的地方。该单个属性中发生了很多事情,所以让我们一步一步来。
repeat() 函数
一般来说,我们通常在 CSS Grid 上定义列和行的方法是在定义属性后添加每个轨道的值,如下所示
.element {
/* This will result on four columns, each one of 1fr */
grid-template-columns: 1fr 1fr 1fr 1fr;
/* This will result on two rows, each one of 300px */
grid-template-rows: 300px 300px;
}
现在,这相当乏味。我们可以使用repeat()
函数使其不那么冗长,更容易理解。该函数接受两个参数
- 重复值的次数。
- 值本身。
在将我们的代码重构为使用repeat()
后,我们应该期望这些代码行产生相同的结果
.element {
/* this is the same as grid-template-columns: 1fr 1fr 1fr 1fr; */
grid-template-columns: repeat(4, 1fr);
/* this is the same as grid-template-rows: 300px 300px; */
grid-template-rows: repeat(2, 300px);
}
干净多了,对吧?
minmax() 函数
现在,上面的示例明确地为轨道定义了大小(1fr
和300px
)。这可能适用于某些场景,但对于我们这里的啤酒示例,我们需要能够根据视口的宽度自动计算轨道的尺寸,并自动调整显示的列数。为了能够做到这一点,我们将使用minmax()
函数定义一个值范围。我们将定义什么?您可能已经猜到了:我们希望这些列能够调整到的*最小*和*最大*值。
在我们上面啤酒示例的英雄区域中,我们将我们的minmax()
属性设置为最小尺寸为 240px,最大尺寸为 1fr。如果您从未听说过fr
单位,它们代表分数单位。Jen Simmons 在此视频中和 Robin Rendle 在此文章中对它们进行了更好的解释。

这会导致我们的轨道在视口上有足够的空间时(即桌面分辨率)为 1fr,而在没有足够空间容纳两列时(例如在移动设备上)为 240px。这就是为什么当我们使浏览器更宽时它们会很好地增长,因为它们正在获取剩余的空间并在现有列之间平均分配它。现在,让我们转到拼图的最后一块!
auto-fit 关键字
auto-fit
关键字允许我们在视口没有足够的空间来适应 240px 最小值而不会溢出内容时,将我们的列换行到行中。Sara Soueidan 撰写了一篇优秀的文章,关于使用auto-fill
和auto-fit
关键字自动调整列的大小,如果您想更深入地了解幕后发生的事情,可以阅读。现在,使用最后一段代码,我们应该能够实现以下结果

文章列表
现在我们已经彻底回顾了英雄元素内部元素的行为,您可能会发现下面啤酒厂列表的前两行 CSS 代码已经很熟悉了
.breweries > ul {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
grid-gap: 1rem;
}
没错!我们使用的是完全相同的方法:在第一行中我们定义了网格,在第二行中我们使用相同的魔法单行代码调整了轨道大小,在第三行中我们为这些列设置了间隙。并没有什么新鲜事,真正巧妙的是,我们的代码足够灵活,可以根据我们无序列表中项目的数量来调整轨道数量和大小

就是这样,朋友们!一个完全响应式的网站布局,仅使用六行 CSS 代码。不错吧?请务必查看源代码并在 CodePen 上试用此示例。
布局 #2:全宽图片库
查看 CodePen 上的示例
全宽图片库,作者:Juan Martín García (@imjuangarcia)
在 CodePen 上。
在这个下一个示例中,我们将充分利用我们新学到的repeat()
、auto-fit
和minmax()
组合的强大功能来创建这个响应式图片库。我们还将使用grid-column
和grid-row
调整轨道大小,并了解方便的属性:grid-auto-flow: dense;
值的组合,它允许我们更改无法适应我们显式轨道元素的默认行为:而不是将它们换行到新行或列中,我们将使它们适应网格上的未使用位置。让我们开始编码吧!
网格设置
网格是使用我们熟悉的 `display: grid;` 属性创建的,其中列的定义使用了 `repeat()`、`auto-fit` 和 `minmax()`。我们还使用 `repeat()` 函数添加了一堆行,并使用 `grid-gap` 为图像定义了间隙。但这里的新成员是 `grid-auto-flow: dense;`。我们稍后会详细介绍它。
.gallery > .gallery__list {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
grid-template-rows: repeat(6, 200px);
grid-gap: 1rem;
grid-auto-flow: dense;
}
我们还使用 `nth-child()` 伪类选择器创建了一个重复模式,以使用 `grid-column` 和 `grid-row` 为我们的轨道设置不同的尺寸。请注意,我们在这里使用 `span` 关键字允许所选项目占用多列或多行。
/* This will create 2x images every 4 elements */
.gallery > .gallery__list > li:nth-child(4n) {
grid-column: span 2; /* Spans two columns */
grid-row: span 2; /* Spans two rows */
}
/* This will create 3x images every 8 elements */
.gallery > .gallery__list > li:nth-child(8n) {
grid-column: span 3;
grid-row: span 3;
}
最后,我们将确保我们的图像覆盖其容器的整个区域,无论它是 1x、2x 还是 3x,都使用 `object-fit: cover;`。如果您从未听说过 `object-fit`,它的工作原理与 `background-image` 非常相似,但使用 HTML 的 `` 标签。
.gallery > .gallery__list > li > figure > img {
width: 100%;
height: 100%;
object-fit: cover;
}
现在,这里真正的重点是 `grid-auto-flow: dense;`。看看当我们从代码中删除它时会发生什么。

看到我们精心制作的网格上的那些空洞了吗?这是因为其中一些元素占据了 2x 或 3x 的空间,并且当我们的轨道上没有足够的空间容纳它们时,它们会换行到新的一行,因为这是默认行为。通过将其从 `row` 更改为 `dense`,我们告诉网格使用可能适合它们的元素填充任何可能存在的间隙,无论它们在 DOM 中的源顺序如何。
这就是为什么这种技术可能特别适用于图像库等内容,但可能不适用于您可能需要保留标记顺序的其他用例。请随时使用 CodePen 演示 来查看项目放置位置之间的差异。
布局 #3:Trello 风格卡片布局
查看 Juan Martín García 编写的 Pen
Trello 风格卡片布局 (@imjuangarcia)
在 CodePen 上。
现在,进入最后一个演示,我们将利用嵌套网格的能力来重新创建这个 Trello 看板。我们将创建一个网格来容纳我们的四列,并在这些列内部,我们将为我们的卡片创建一个子网格。即使此示例不会探索新的属性或革命性的方法,它也将帮助我们了解使用几行 CSS 代码构建复杂布局是多么容易。此演示有很多额外的代码来实现 Trello 布局的样式,因此我们将只关注网格样式。
列
要创建四列,我们将在容器上使用 `display: grid;` 并使用我们神奇的一行代码来设置 `grid-template-columns`。我们还将在它们之间定义一个间隙,并使用 `align-items: flex-start;` 以确保我们的列不会拉伸到屏幕底部。
.column__list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
grid-gap: .5rem;
align-items: flex-start;
}
现在,原始的 Trello 默认情况下不是响应式的:如果您调整 Trello 看板上浏览器的尺寸,您会注意到您最终会在列上出现水平滚动条,而不是将它们换行到新的一行。我们这里没有遵循这种行为,因为我们想要构建响应式布局,但是如果您好奇,并且想要模拟 Trello 的功能,您可以通过添加两行 CSS 代码来实现。
.column__list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
grid-gap: .5rem;
align-items: flex-start;
/* Uncomment these lines if you want to have the standard Trello behavior instead of the column wrapping */
grid-auto-flow: column;
grid-auto-columns: minmax(260px, 1fr);
}
我们在之前的演示中了解了 `grid-auto-flow`,并发现它允许我们控制自动放置算法的工作方式以及如何在网格流中添加隐式元素。默认行为是 `row`,这意味着任何无法容纳在网格中的额外元素都将换行到新的一行。我们在之前的演示中将其更改为 `dense`,并且在此演示中将其更改为 `column`:这样,此处添加的任何新列都将出现在隐式列中,并具有水平滚动条。我们还将使用 `grid-auto-columns` 属性为这些自动生成的列定义宽度。

卡片
对于卡片网格,我们将使用类似的方法。我们将在容器上使用 `display: grid;`。我们不会在这里定义任何列,因为我们不希望有任何列,并且我们将使用 `grid-template-rows: auto;` 来避免所有卡片具有相同的高度——我们希望其中一些更大,一些更小,这取决于添加到它们中的内容类型。
.card__list {
display: grid;
grid-template-rows: auto;
grid-gap: .5rem;
margin: .5rem 0;
}
再次强调,就是这样!再添加两行代码来设置卡片的间隙和边距,我们就完成了!Pen 中的所有其他内容都是标准的 CSS,用于实现 Trello 的外观和感觉。
那么……媒体查询是否已死?
在过去,当我们使用 `display: inline-block` 或浮动构建布局时,媒体查询在更改元素尺寸(当视口变小时)方面很有意义。但是现在,凭借我们能够用几行 CSS 代码创建的强大布局,您可能会忍不住认为媒体查询注定要消亡。我强烈反对这种观点:我相信我们应该改变我们对它们的思考方式,因此应该以不同的方式使用它们。
正如 Rachel Andrew 大约一年前所说,我们应该在布局出现问题时使用媒体查询来修复它,而不是针对特定的设备:因为设备实在太多了!随着媒体查询 级别 4 和 5 的出现,我们不仅能够检测屏幕尺寸,还可以检测指针类型。因此,我们可以深入了解用户的系统偏好,并为那些偏好减少运动或是否应该使用反色的人调整我们的代码。这意味着媒体查询并没有消亡;相反,我认为现在是使用媒体查询的激动人心的时刻,但我们需要学习正确地使用它们。同时,使用 Flexbox 或 CSS Grid 等现代技术构建强大的布局将为您节省大量时间、代码和麻烦。
很棒的文章。谢谢。
在 Trello 示例中,应该是 `align-items: start` 而不是 `flex-start`。我们没有使用 flex。
没错!抓住了!感谢您如此仔细地阅读。
这是一个谦虚的标题!曾经有一段时间没有媒体查询,我们将“响应式”设计称为“流体”设计。我们可能正处于一个可以从那些日子汲取灵感,拂去灰尘并恢复旧式流体设计的时代。
绝对地!感谢您的阅读!
这可以用来制作更好更简单的电子邮件模板吗,在我的情况下是 MailChimp?
我担心像电子邮件客户端这样的受限环境不会支持这种语法。如果您需要帮助,我非常喜欢 MJML。
如果您习惯使用 Bootstrap,还可以使用 Bootstrap Email。
您好,我想知道如果我们想隐藏某些内容,在这种情况下断点是否必不可少?
是的,要隐藏内容,您需要使用媒体查询。
从移动设备查看时,某些照片会被截断。它们可以很好地调整大小,直到平板电脑尺寸。
您好!感谢您检查演示。具体是哪一个?
有没有办法在 IE11 中使用它?IE11 不理解 `grid-template-column` 部分。
您好 Johnny!是的,您可以使用 `-ms-grid-columns` 前缀该属性。
感谢你的分享。自动填充和自动适应应该添加到专门的文章中:“Grid 完全指南”。
绝对的!
文章不错。但是为什么不省略定位,而使用 Grid 将 figcaption 放置在图像上方呢?
是的,这样也可以。我之前没想过这一点。
感谢你撰写这篇优秀的文章,我正在思考第一个啤酒厂示例,如果我在 ul 容器中包含奇数个 li 元素,并且希望最后一个元素跨越 ul 容器的整个可用长度,该怎么办?这样,在奇数个 li 网格项之后就不会出现空闲的尾随空间。例如,在转换为单列布局之前,我们有两个列时就会出现这种情况。我尝试过使用 :nth-child(odd),但效果不佳,并且还会影响第一个 li 网格项。
你可以为此使用
:last-child
! https://mdn.org.cn/es/docs/Web/CSS/:last-child这是一篇很棒的文章。我在想,如果我们使用 grid-areas 并为每个类分配区域,那么在没有 grid-areas 的情况下,使其响应式是否会像现在一样简单?
是的,这样也可以,但是你需要编写媒体查询来更改网格区域。
很棒的文章!阅读完这篇文章后,我对自己的网格技能更有信心了。
当我尝试啤酒厂示例时,我必须添加 list-style none 来阻止项目符号出现,但我注意到你不需要这样做???
ian
这是因为我在 Codepen 上使用了 Eric Meyer 的
reset.css
!你可以从这里下载并使用它: https://meyerweb.com/eric/tools/css/reset/很棒的文章,但这些技巧不能应用于导航。我们发现仍然需要使用 @media。
是否可以动态更改元素的跨度?例如,在图像库示例中,由于其他图像的 col 跨度为 3,因此图像在移动设备上有时非常小。能否动态更改跨度,以便在移动设备上所有图像都具有相同的大小并排显示,而在桌面设备上以漂亮的网格形式显示并具有不同的尺寸?