Skip to content

27 高级网格模板值

有尺寸限制的定价方案 示例 27a

在前面讲过的定价方案示例中,你可能已经注意到,即使在移动屏幕上,各列也会拉伸至容器的整个宽度。但是,由于每张卡片内的内容太少,卡片过宽会影响美观。所以,我们希望将每张卡片的宽度限制在最大 18rem,同时最小宽度为内容的宽度,这样卡片就不会缩得比内容还窄。

Pricing Plans with Size Limits

Try it out

标记

html
<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-类来限制卡片的宽度。但是,列的宽度仍然很大,这使得在大屏幕上无法将三张卡片居中对齐。所以,下面是一个完美的解决方案:

html
<div
  class="container
    grid
    grid-cols-[minmax(auto,18rem)]
    sm:grid-cols-[repeat(3,minmax(auto,18rem))]
    justify-center
    gap-8"
>
  ...
</div>

Working Demo

我们用grid-cols-[repeat(3,minmax(auto, 18rem))]替换了grid-cols-3。在 CSS 中,这等同于:

css
grid-template-columns: repeat(3, minmax(auto, 18rem));

理解minmax() 概念

minmax() 函数接受两个参数:最小值(min)和最大值(max)。它用于指定一个尺寸范围,该范围大于或等于最小值,且小于或等于最大值。这两个值可以是任何长度单位,如像素(px)、百分比(%)、根字体大小(rem),甚至是像 1frmin-contentmax-content 这样的值。在前面的示例中,我们希望卡片的宽度最小为内容的宽度,最大为 18rem。所以我们将 auto 作为最小值,18rem 作为最大值。

带有代码片段的博客文章页面 示例 27b

示例灵感来自 CSS Tricks 文章——防止网格布局失控

我们之前研究过使用 grid-cols-[22rem,1fr] 创建一个带有侧边栏的简单页面布局。这是一个简单的解决方案,通常也能正常工作。但是,考虑一下这个类似布局的博客文章页面示例。在这里,我们需要使用 <pre> 标签来显示代码片段。而代码片段有时会包含很长的代码行或注释。当我们给 <pre> 元素设置 max-width: fulloverflow-scroll 时,期望它最多占据 100% 的宽度,并显示一个水平滚动条。

Blog Post Codes

但是看看这个链接,将窗口缩小,看看会发生什么。

Try it out

主要内容会扩展以占据 <pre> 元素的整个宽度,导致整个页面 “失控”。但这是为什么呢?

1fr 值会在内容较小时拉伸列以占据剩余空间,否则,最小宽度为 auto。所以 1fr 实际上等同于 minmax(auto, 1fr)。因此,当你添加一个宽度很大的 <pre> 元素时,那列的最小宽度就会是 <pre> 元素的宽度。让我们看看这个问题的解决方案:

解决方案

html
<section class="min-h-screen grid grid-cols-[minmax(0,1fr),16rem]">...</section>

Working Demo

现在范围是从 0 到 1fr,就可以正常工作了。如果你觉得这些内容难以理解,只需要记住一点——minmax(0, 1fr) 总是比 1fr 更好的选择。这就是为什么,如果你查看 Tailwind 的 grid-cols-* 类,它们对应的 CSS 值是:

Tailwind 类CSS 属性及值
grid-cols-1grid-template-columns: repeat(1, minmax(0, 1fr));
grid-cols-2grid-template-columns: repeat(2, minmax(0, 1fr));

以此类推。但你可以直接使用 grid-cols-* 工具类,而无需担心布局失控的问题。

无需媒体查询的响应式网格布局 示例 27c

还记得我们是如何通过在两个断点处添加媒体查询来改变列数,从而使博客文章显示具有响应性的吗?实际上,我们并不需要这么做。网格布局有一种根据可用空间决定创建多少列的方法。不过,Tailwind 并没有针对此的现成解决方案。我们还是要使用自定义值。

Respinsive Grid without Media Queries

HTML 代码

html
<div class="container">
  <div class="item">...</div>
  <!-- Five more item cards -->
</div>

解决方案

html
<div
  class="container grid grid-cols-[repeat(auto-fit,minmax(16rem,1fr))] gap-8"
>
  ...
</div>

如果你觉得这些自定义值很难读懂,也可以使用自定义 CSS。

css
.container {
  ...
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
}

Working Demo

调整浏览器窗口大小,你会看到网格会自动创建或多或少的列。现在让我们剖析一下,了解其中的原理。

之前,对于大屏幕,我们使用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 规则,就能创建一个等宽列的响应式网格布局:

css
grid-template-columns: repeat(auto-fit, minmax(<fixed-width-value>, 1fr));

现在考虑这样一种场景,你只有一篇博客文章。如何防止它占据整行呢?

解决方案

css
grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));

Working Demo

我们将auto-fit关键字替换为了auto-fill

理解 auto-fill 概念

auto-fillauto-fit 非常相似。在之前的例子中,如果有超过 5 篇博客文章,你根本不会察觉到二者有任何区别。你可以自己试试。这两个关键词会产生相同的结果。但是,当项目数量较少且有更多空间来填充项目时:

  • auto-fit 会分配剩余空间,使行中没有空白区域。
  • auto-fill 会创建与项目大小相同的空白列。

如果这还不清楚,CSS Tricks 上的这篇文章——auto-fill vs auto-fit 解释得非常清楚。

现在,使用这种方法,在不使用媒体查询的情况下,让以下示例具有响应式效果:

  1. 网格中的特色徽标 Featured Logos in a Grid
  2. 响应式定价方案 Responsive Pricing Plans

由你决定是使用 auto-fit 还是 auto-fill

Released under the No License.