在 Drupal 10 核心代码中,我们正在实现一种新的自动填充 CSS 网格技术,我认为它足够酷,可以与世界分享。
要求如下:
- 用户指定最大列数。这是自动填充网格的“自然”状态。
- 如果网格单元格的宽度小于用户指定的宽度,则自动填充网格将重新调整自身并减少列数。
- 网格单元格应始终拉伸以适应自动填充网格容器的宽度,无论列数是多少。
- 所有这些都应独立于视口宽度工作,并且不需要 JavaScript。

自动填充 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()
函数。以下是其工作原理。
auto-fill()
函数
CSS 网格的 这一切的关键在于 auto-fill()
。我们需要每一行都填充尽可能多的列。有关 auto-fill
的更多信息,请查看 Sara Soueidan 关于 auto-fill
和 auto-fit
之间差异 的精彩文章,其中包括这段展示其工作原理的有用视频。
但是,我们如何确保它不会填充过多列呢?
max()
函数
CSS 的 这就是 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()
函数的信息。
minmax()
函数
CSS 网格的 我们越来越接近了,但还缺少一个关键要素:网格并不总是拉伸到其父容器的宽度。
这正是 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()
。此外,还要特别感谢所有实现者和规范编写者,他们使此类高级功能标准化并成为可能。
很酷的设置,确实展示了网格布局与新函数一起使用时的强大功能。
我可能遗漏了一些东西,但名称
--grid-item--max-width
似乎具有误导性。它不是最大值,而是像另一个一样,是一个最小值——项目永远不会比它们中的任何一个更窄。是的,我理解你的观点。但它并不是真正可能的最小宽度。它是“正常”宽度。我只是将此称为
--grid-item--max-width
,因为它没有硬编码为px
(或其他任何内容)中的固定值。你理解得很好,只是给它命名很难 :)
太感谢了!!!
真的太感谢了!!
哈!这让我笑了。不客气!
非常酷!将 CSS 网格与 calc 一起使用使其非常强大。
我用 Fylgja 自动网格做了类似的事情。
我从未听说过 Fylgja……但它看起来确实非常相似(而且很酷!)。查看源代码,它几乎相同,区别在于它们不支持最大列数。
酷!但是,如果您不想只指定最大列数,还想使用仅 CSS 指定最小列数,请查看以下内容。
解释
https://stackoverflow.com/questions/52417889/setting-minimum-and-maximum-number-of-columns-using-css-grid/52418341/#69154193
演示
我也有一种类似的方法来定义仅具有最大列数的网格
我目前正在尝试基于类似想法的网格系统。这是它的文档页面:https://fluid-grid.com/
请查看,我目前正在收集反馈;)
太酷了!文档和交互式示例非常棒。我玩得很开心,希望学到很多东西。感谢您将其整合在一起!
嗨,很酷的东西!我刚刚试了一下,我觉得我发现了一个小问题:我总是比我指定的少一列。
所以
--grid-column-count: 4
给了我3列,--grid-column-count: 3
给了我2列,依此类推。忽略我之前的评论——我在后面 CSS 中覆盖了间隙。 那就是导致我的列数错误的原因。很棒的教程!!
真的很酷。
但是有没有人遇到过 gulp-sass 的相同问题
错误:“var(–grid-item–min-width)”对于“max”不是一个数字
尝试 Sass 插值(https://sass-lang.com.cn/documentation/interpolation)。基本上,将其包裹在
#{...}
中。如果其他人遇到此问题,可以通过将
max
(或min
)关键字大写来修复。如果这失败了
minmax(max(var(--grid-item--min-width)
如果你这样写应该可以工作
minmax(Max(var(--grid-item--min-width)
我在 GitHub 上的这个线程中找到了答案。
这太棒了,并且非常适合我正在处理的一个项目的用例。非常感谢您分享。
耶!
很棒的文章!我喜欢这个解决方案的动态性。
如何将.grid-item的高度设置为等于当前列的宽度?我想将网格项制作成完美的正方形,但我无法弄清楚如何使用该函数以像素为单位查找列宽并将.grid-item的高度设置为它。
我目前的尝试集中在将行高设置为等于列宽,但所有尝试都失败了。
任何帮助将不胜感激!
您无需为此连接到网格系统。要创建完美的正方形,请使用 CSS 的
aspect-ratio
属性。https://mdn.org.cn/en-US/docs/Web/CSS/aspect-ratio哦,哇,我过度设计了解决方案。谢谢!
我刚刚读完了您网站上的“用 CSS 制作彩虹”文章。我希望将来能在 CSS-tricks 上看到更多您的内容。
精彩的写作!我没有看到的部分是如何更新
--grid-column-count
。所有的数学都依赖于这个值是动态的,对吧?谢谢!
是的,
--grid-layout-gap
、--grid-column-count
和--grid-item--min-width
都可以是动态的。您需要服务器端模板或 JavaScript 将属性作为内联样式注入到
.grid-container
元素(或其祖先之一)中。查看我在 https://codepen.io/mherchel/pen/xxXXbog 上的 CodePen,了解如何通过 JavaScript 注入内联样式的示例。
哇,太棒了!感谢你的提示,这绝对超出了我的能力范围;)
此致
谢谢!网格很有趣。玩一玩,你就能想出很酷的东西 :)
有没有办法强制列数为 1、2 或 4?我正在处理一个带有 Bootstrap 卡片布局,我希望最大列数为 4。1 或 2 列在移动设备或小屏幕上看起来不错,但在达到 3 列时看起来很糟糕。
我想我可以使用媒体查询,但这感觉违背了像这样的灵活布局的初衷。
太棒了,正是我需要的!感谢您提供精彩的教程并分享您的代码。
很棒的文章!!是否可以将每个项目的最小宽度调整为内容宽度?
这太酷了,非常感谢!
我注意到的一件小事是,由于为
--grid-item--min-width
设置的值是固定的(例如:250px),当视口变得非常窄时,可能会发生 X 轴溢出。为了防止这种情况,您可以将您的
--grid-item--min-width
设置为用户输入值和 100% 之间的最小值,所以你怎么看?☺️