具有最小尺寸最大列数的自动填充 CSS 网格

Avatar of Mike Herchel
Mike Herchel

DigitalOcean 为您旅程的每个阶段提供云产品。从 200 美元的免费额度 开始!

Drupal 10 核心代码中,我们正在实现一种新的自动填充 CSS 网格技术,我认为它足够酷,可以与世界分享。

要求如下:

  • 用户指定最大列数。这是自动填充网格的“自然”状态。
  • 如果网格单元格的宽度小于用户指定的宽度,则自动填充网格将重新调整自身并减少列数。
  • 网格单元格应始终拉伸以适应自动填充网格容器的宽度,无论列数是多少。
  • 所有这些都应独立于视口宽度工作,并且不需要 JavaScript。
Screenshot showing the auto-filling CSS Grid's settings in Drupal.

自动填充 CSS 网格的实际效果

以下是如何在左侧的可拖动 div 元素压缩自动填充 CSS 网格时,该网格的行为。

代码如下

如果您不想了解自动填充网格背后的原理,只想复制/粘贴代码,请看这里!

.grid-container {
  /**
   * User input values.
   */
  --grid-layout-gap: 10px;
  --grid-column-count: 4;
  --grid-item--min-width: 100px;

  /**
   * Calculated values.
   */
  --gap-count: calc(var(--grid-column-count) - 1);
  --total-gap-width: calc(var(--gap-count) * var(--grid-layout-gap));
  --grid-item--max-width: calc((100% - var(--total-gap-width)) / var(--grid-column-count));

  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(max(var(--grid-item--min-width), var(--grid-item--max-width)), 1fr));
  grid-gap: var(--grid-layout-gap);
}

自动填充 CSS 网格背后的理论和工具

上面的代码使用了多个现代 CSS 工具,包括 CSS 网格的 repeat()auto-fill()minmax() 函数,以及 CSS 的 max()calc() 函数。以下是其工作原理。

CSS 网格的 auto-fill() 函数

这一切的关键在于 auto-fill()。我们需要每一行都填充尽可能多的列。有关 auto-fill 的更多信息,请查看 Sara Soueidan 关于 auto-fillauto-fit 之间差异 的精彩文章,其中包括这段展示其工作原理的有用视频。

但是,我们如何确保它不会填充过多列呢?

CSS 的 max() 函数

这就是 max() 函数发挥作用的地方!我们希望每个网格单元格的宽度最大达到某个百分比,例如对于四列网格,最大为 25%。但是,我们不能让它低于用户指定的最小宽度。

因此,假设一个四列网格和 100px 的最小单元格宽度,max() 函数将如下所示:max(25%, 100px)

但是,25% 的值仍然不完全正确,因为它没有考虑网格间隙。我们真正需要的是类似这样的东西

max(calc(25% - <grid-gap-for-one-cell>), 100px)

我们可以在 CSS 中使用 calc() 计算! (谁说 CSS 不是编程?)

--gap-count: calc(var(--grid-column-count) - 1);
--total-gap-width: calc(var(--gap-count) * var(--grid-layout-gap));
--grid-item--max-width: calc((100% - var(--total-gap-width)) / var(--grid-column-count));

现在我们有了另一个使它工作的关键!这将告诉网格单元格达到其最大宽度(这考虑了用户指定的列),但永远不会低于 100px

max(100px, var(--grid-item--max-width))

通过 Chris Coyier 关于 CSS 的 min()max()clamp() 函数 的文章,了解更多关于 max() 函数的信息。

CSS 网格的 minmax() 函数

我们越来越接近了,但还缺少一个关键要素:网格并不总是拉伸到其父容器的宽度。

这正是 minmax() 函数的设计目的。以下 CSS 将最小宽度设置为 <grid-item-width>,如果还有空间,它将平均拉伸所有单元格以适应父级的宽度!

minmax(<grid-item-width>, 1fr)

让我们将它们组合在一起,创造一些魔法!

使用上述工具,我们可以组合这段神奇的代码,完全按照我们的意愿执行!

--gap-count: calc(var(--grid-column-count) - 1);
--total-gap-width: calc(var(--gap-count) * var(--grid-layout-gap));
--grid-item--max-width: calc((100% - var(--total-gap-width)) / var(--grid-column-count));

grid-template-columns: repeat(auto-fill, minmax(max(var(--grid-item--min-width), var(--grid-item--max-width)), 1fr));

CSS 真有趣!

CSS 已经发展了很长一段时间。我在这方面玩得很开心,也很高兴现在可以使用此类用例,而无需使用 JavaScript。

特别感谢 Andy Blum,他建议使用 auto-fill() 而不是 auto-fit()。此外,还要特别感谢所有实现者和规范编写者,他们使此类高级功能标准化并成为可能。