27 高级网格模板值
有尺寸限制的定价方案 示例 27a
在前面讲过的定价方案示例中,你可能已经注意到,即使在移动屏幕上,各列也会拉伸至容器的整个宽度。但是,由于每张卡片内的内容太少,卡片过宽会影响美观。所以,我们希望将每张卡片的宽度限制在最大 18rem,同时最小宽度为内容的宽度,这样卡片就不会缩得比内容还窄。
标记
<div class="container grid sm:grid-cols-3 gap-8">
<div class="plan"></div>
<div class="plan plan-highlight"></div>
<div class="plan"></div>
</div>
解决方案
仅使用 Tailwind 无法解决这个问题。我们可以直接给.plan
元素添加max-w-
和min-w-
类来限制卡片的宽度。但是,列的宽度仍然很大,这使得在大屏幕上无法将三张卡片居中对齐。所以,下面是一个完美的解决方案:
<div
class="container
grid
grid-cols-[minmax(auto,18rem)]
sm:grid-cols-[repeat(3,minmax(auto,18rem))]
justify-center
gap-8"
>
...
</div>
我们用grid-cols-[repeat(3,minmax(auto, 18rem))]
替换了grid-cols-3
。在 CSS 中,这等同于:
grid-template-columns: repeat(3, minmax(auto, 18rem));
理解minmax()
概念
minmax()
函数接受两个参数:最小值(min)和最大值(max)。它用于指定一个尺寸范围,该范围大于或等于最小值,且小于或等于最大值。这两个值可以是任何长度单位,如像素(px)、百分比(%)、根字体大小(rem),甚至是像 1fr
、min-content
或 max-content
这样的值。在前面的示例中,我们希望卡片的宽度最小为内容的宽度,最大为 18rem。所以我们将 auto
作为最小值,18rem 作为最大值。
带有代码片段的博客文章页面 示例 27b
示例灵感来自 CSS Tricks 文章——防止网格布局失控
我们之前研究过使用 grid-cols-[22rem,1fr]
创建一个带有侧边栏的简单页面布局。这是一个简单的解决方案,通常也能正常工作。但是,考虑一下这个类似布局的博客文章页面示例。在这里,我们需要使用 <pre>
标签来显示代码片段。而代码片段有时会包含很长的代码行或注释。当我们给 <pre>
元素设置 max-width: full
和 overflow-scroll
时,期望它最多占据 100% 的宽度,并显示一个水平滚动条。
但是看看这个链接,将窗口缩小,看看会发生什么。
主要内容会扩展以占据 <pre>
元素的整个宽度,导致整个页面 “失控”。但这是为什么呢?
1fr
值会在内容较小时拉伸列以占据剩余空间,否则,最小宽度为 auto
。所以 1fr
实际上等同于 minmax(auto, 1fr)
。因此,当你添加一个宽度很大的 <pre>
元素时,那列的最小宽度就会是 <pre>
元素的宽度。让我们看看这个问题的解决方案:
解决方案
<section class="min-h-screen grid grid-cols-[minmax(0,1fr),16rem]">...</section>
现在范围是从 0 到 1fr
,就可以正常工作了。如果你觉得这些内容难以理解,只需要记住一点——minmax(0, 1fr)
总是比 1fr
更好的选择。这就是为什么,如果你查看 Tailwind 的 grid-cols-*
类,它们对应的 CSS 值是:
Tailwind 类 | CSS 属性及值 |
---|---|
grid-cols-1 | grid-template-columns: repeat(1, minmax(0, 1fr)); |
grid-cols-2 | grid-template-columns: repeat(2, minmax(0, 1fr)); |
以此类推。但你可以直接使用 grid-cols-*
工具类,而无需担心布局失控的问题。
无需媒体查询的响应式网格布局 示例 27c
还记得我们是如何通过在两个断点处添加媒体查询来改变列数,从而使博客文章显示具有响应性的吗?实际上,我们并不需要这么做。网格布局有一种根据可用空间决定创建多少列的方法。不过,Tailwind 并没有针对此的现成解决方案。我们还是要使用自定义值。
HTML 代码
<div class="container">
<div class="item">...</div>
<!-- Five more item cards -->
</div>
解决方案
<div
class="container grid grid-cols-[repeat(auto-fit,minmax(16rem,1fr))] gap-8"
>
...
</div>
如果你觉得这些自定义值很难读懂,也可以使用自定义 CSS。
.container {
...
grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
}
调整浏览器窗口大小,你会看到网格会自动创建或多或少的列。现在让我们剖析一下,了解其中的原理。
之前,对于大屏幕,我们使用repeat(3, 1fr)
。现在,我们将第一个值替换为auto-fit
,第二个值替换为minmax(16rem, 1fr)
。
现在你已经知道minmax(16rem, 1fr)
的作用了,它无论如何都会占据至少 16rem 的宽度。如果有更多可用空间,它会拉伸以占据更宽的宽度。但是auto-fit
是什么呢?
理解auto-fit
概念
auto-fit
这个关键字告诉浏览器去处理列的数量和大小,这样当宽度不足以在不溢出的情况下容纳所有元素时,元素就会换行。repeat
第二个值中的1fr
确保在有更多可用空间,但又不足以容纳另一整列的情况下,多余的空间会分配给其他列,保证行尾不会留下任何空白空间。
浏览器的计算方式
为了更好地理解上述示例,假设屏幕宽度为 40rem。容器左右各有 2rem 的内边距,那么现在剩余的宽度为:
40rem - 4rem = 36rem
这很容易容纳一个宽度为 16rem 的列。放置好一个列后,剩余宽度为:
36rem - 16rem = 20rem
我们能再放置一个 16rem 宽的列并留出 2rem 的间距吗?可以。放置好第二个列后,剩余宽度为:
20rem - (16rem + 2rem) = 2rem
现在有 2rem 的额外空间,将其平均分配给两列,那么现在每列的宽度变为 17rem。
另一方面,如果屏幕宽度是 36rem 而不是 40rem,按照同样的步骤计算,你会发现无法放置第二个列。减去容器的内边距后,剩余 32rem,刚好是单列的宽度。
但实际上,你不必担心上述所有计算。理想情况下,你只需要一条 CSS 规则,就能创建一个等宽列的响应式网格布局:
grid-template-columns: repeat(auto-fit, minmax(<fixed-width-value>, 1fr));
现在考虑这样一种场景,你只有一篇博客文章。如何防止它占据整行呢?
解决方案
grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
我们将auto-fit
关键字替换为了auto-fill
。
理解 auto-fill
概念
auto-fill
与 auto-fit
非常相似。在之前的例子中,如果有超过 5 篇博客文章,你根本不会察觉到二者有任何区别。你可以自己试试。这两个关键词会产生相同的结果。但是,当项目数量较少且有更多空间来填充项目时:
auto-fit
会分配剩余空间,使行中没有空白区域。auto-fill
会创建与项目大小相同的空白列。
如果这还不清楚,CSS Tricks 上的这篇文章——auto-fill vs auto-fit 解释得非常清楚。
现在,使用这种方法,在不使用媒体查询的情况下,让以下示例具有响应式效果:
- 网格中的特色徽标 Featured Logos in a Grid
- 响应式定价方案 Responsive Pricing Plans
由你决定是使用 auto-fit
还是 auto-fill
。