项目流,第 1 部分
一种新的统一布局概念
CSS Grid 和 Flexbox 为 Web 带来了令人惊叹的布局工具,但它们尚未能满足设计师的所有需求。其中之一就是一种流行的布局模式,称为“瀑布流”或“堆砌式”,目前仍需要一个 JavaScript 库来实现。
CSS 瀑布流布局功能最早由 Mozilla 提出,并于2020 年在 Firefox 中通过标志实现。从一开始,关于这种机制就存在激烈的争论。Apple 的 WebKit 团队接过了 Mozilla 的工作,实现了草案规范,并将其引入了 Safari 技术预览版 163。这重新激发了讨论。到 2024 年 10 月,有两种相互竞争的观点正在辩论——是“只用 Grid”还是创建一个全新的“瀑布流布局”。我们在一篇关于此网站的先前文章中详细阐述了这些观点。
自十月以来发生了很多事情。现在,第三条前进道路正在出现——这条道路意味着 CSS 工作组既不选择“只用 Grid”也不选择“全新瀑布流布局”。它融合了两者的想法,并引入了一个全新的概念,以创建一个统一的项目流属性系统。本文解释了项目流是什么,以及它对 Flexbox 和 Grid 的影响。在第 2 部分中,另一篇文章将更全面地解释其对未来瀑布流式布局的意义。

但首先,为什么会出现被称为项目流的第三种可能性?为什么不直接在“只用 Grid”和“全新瀑布流布局”之间做出选择呢?嗯,早在十月,负责瀑布流的人员请 W3C 技术架构组(TAG)对辩论进行权衡。TAG 给出了冗长的回复,但其中最有趣的部分是这个:
总的来说,我们认为瀑布流、网格和换行 Flexbox 应该整合到一组统一的属性中。Chrome 的 [新瀑布流布局] 提案过于急于拆分属性集,但即使是 WebKit [最初是 Mozilla | 只用 Grid] 提案,似乎也错失了开发更通用属性的机会。
哇。将 Flexbox、Grid 和 Masonry 整合到一组统一的属性中?那会是什么样子?
这个建议不是将所有 Flexbox 与所有 Grid 结合起来,而是创建一组新的属性和值,专门“替换” `flex-flow` 和 `grid-auto-flow` 属性。(如果您愿意,您将始终可以使用旧的语法。)想想最初对齐属性和`gap`属性是如何被定义为只在一个布局系统中工作,然后才被修改和扩展以在多个布局上下文中工作。
我们 Apple 的几位同事聚在一起,开始思考这样一个统一的布局 `*-flow` 系统如何运作。我们决定暂时将其简写命名为 `item-flow`。它将是主要的属性,其独立属性和值都将由此衍生。总而言之,这些新规则将控制流向、项目如何换行、项目如何打包、布局中是否存在“松弛”等。
随着我们深入细节,我们开始感到兴奋。突然间,人们多年来一直希望 Flexbox 和 Grid 拥有的新功能有了明显的归宿。一切似乎都优雅地融合在一起。新的功能也随之出现:
- Flexbox 可以获得一种进行紧密打包的方式。
- Grid 可以获得关闭换行的能力。
- 瀑布流布局现在可以通过 `item-flow` 的一个值来触发。
- 以及更多……
在本文中,我们希望向您——网站和 Web 应用的开发者——描述项目流,以听取您的想法。那么,让我们踏上想象这个未来将会是什么样子的旅程吧……
结合 Flexbox 和 Grid
Flexbox 有 `flex-flow`。它是以下两个独立属性的简写属性:
flex-direction: row | row-reverse | column | column-reverse;
flex-wrap: nowrap | wrap | wrap-reverse;
Flex-direction 决定内容流动的方向,而 flex-wrap 决定是否换行。
CSS Grid 有 `grid-auto-flow`。
grid-auto-flow: row | column | dense;
它在一个属性中同时决定了内容流动的方向以及是否使用紧密打包。它没有独立的属性。
那么,我们如何统一这些功能呢?我们建议将新的简写属性命名为 `item-flow`,并带有四个新的独立属性:
item-direction
item-wrap
item-pack
item-slack
(重要的是,所有这些都是全新的、刚刚萌芽的想法。它们将在浏览器中成为现实之前经过长时间的讨论和修改。为了使本文易于阅读,我们忽略了当前的争论,只为每个属性/值列出了一个名称,而没有列出 CSSWG 提出的所有变体。)
项目方向
`item-direction` 属性将决定内容如何流动。内容应该按行还是按列流动?是正常方向还是反向?这将与 `flex-direction` 以及 `grid-auto-flow` 的相应部分完全相同。
item-direction: row | column | row-reverse | column-reverse
这很容易理解,因为它与我们多年使用 Flexbox 和 Grid 的预期效果一致。关于“行”和“列”在瀑布流式布局中的含义存在争议——我们将在下一篇文章《项目流 – 第 2 部分:瀑布流的下一步》中深入探讨。
项目换行
`item-wrap` 属性将决定内容是否换行,如果换行,则决定换行方向。新的默认值将是 `auto`——在 Flexbox 中解析为 `nowrap`,在 Grid 中解析为 `wrap`,与当前的初始值匹配。
这是我们熟悉的领域,但创建一个新属性让我们有机会清理这些值,使它们更合理。我们可以添加一个新的 `reverse` 关键词,这样 `[ nowrap | wrap ]` 是一个选择,而 `[ normal | reverse ]` 是第二个独立的选项,改进了 Flexbox 中目前的工作方式。(我们也可以保留 `wrap-reverse` 以支持旧有习惯。)这将给我们带来:
item-wrap: auto | nowrap | wrap | normal | reverse
但 CSS Grid 之前并没有 `nowrap` 的概念!那会是什么呢?这里有一个想法……
对于 Flexbox,有许多用例是将项目布局在一行中,不进行任何换行。也许它是一个导航栏,其中每个项目是不同长度的短语。每个项目的空间量由该项目的大小决定——您将获得灵活的盒子。
如果您想将所有内容布局在一行中,但每个项目都获得相同的空间量——即每个项目都宽为 `1fr` 呢?由于列的大小是外部确定的,CSS Grid 是完成这项工作的正确工具。但由于 Grid 默认会换行,开发者通常会转而使用 Flexbox,却没有意识到今天通过 `grid-auto-flow: column` 也能达到期望的效果。
在未来拥有项目流的世界中,如何使用 `nowrap` 告诉 Grid 将所有内容放在一行中将会非常明显。
.container {
display: grid;
grid-auto-columns: 1fr;
item-wrap: nowrap;
}
现在,如果有九个项目,我们就得到九列。如果有五个项目,我们就得到五列,像这样:

项目打包
`item-pack` 属性将允许我们在不同类型的打包之间切换。
紧密打包
在 Grid 中,我们目前可以通过 `grid-auto-flow: dense` 切换到紧密打包模式。在新的集成系统中,相同的功能将通过 `item-pack: normal | dense` 提供——首次为切换回非紧密模式添加了显式值。
Flexbox 目前没有紧密打包的概念。那么我们可以在这里添加什么功能呢?
.container {
display: flex;
item-pack: dense;
}
目前已经讨论了两个想法:
- 紧密打包可以意味着 Flexbox 将尝试在开始新行之前,将一个额外的项目塞入当前行(或列)。它会仅将当前行上的项目缩小到足够程度,而不是留下额外空间并将其拉伸。
- 或者,为了像 Grid 一样行事,紧密打包可能意味着 Flexbox 会回溯到布局中的先前位置,并将较小的项目放置在任何可用空间中。
要理解这些选项的含义,让我们回顾一下 Flexbox 目前的工作方式,然后看看 `dense` 可以做些什么。
今天,当 Flexbox 开始布局内容时,它会用适合可用空间的项填充一行。当空间不足时,它会换行(如果允许换行),并填充下一行……直到所有项都有了归宿。然后,它根据 `flex` 和对齐属性分配所有额外的空间。(请参阅插图左右两侧的这两个步骤。)

根据上面列出的第一个想法,Flexbox 中的紧密打包可以改为仅仅将项目缩小到足以容纳更多项目在当前行上,而不是分配额外空间。

根据第二个想法,为了更像 Grid 的紧密打包行为,Flexbox 可以重新排列项目的顺序,并用较小的项目回填较小的空间。在这种情况下,项目 6 和 10 最终脱离了正常顺序,被放置在之前的行中。

第二个想法会导致较小的项目堆积在右侧(结束边缘)。因此,有些人想知道这在实践中是否会有用。
您希望“dense”对 Flexbox 意味着什么?您更喜欢第一个还是第二个想法——或者您有第三个想法?
平衡打包
`item-pack` 属性还可以允许尚不存在的新型打包。例如,我们可以创造 `item-pack: balance`。
目前,Flexbox 在每行上放置项目时,不考虑其他行上发生的情况。这通常会导致最后一行只有极少数项目。

一个新的 `balance` 值可以告诉浏览器以某种方式平衡项目,类似于 `text-wrap: balance`,确保最后一行不会显得奇怪。
.container {
display: flex;
item-pack: balance;
}

这可能非常有用,并解决 Flexbox 长期以来的一个痛点。
折叠打包
`item-pack` 属性还可以为瀑布流式布局的触发器提供一个新的归宿。这与之前的瀑布流提案有所不同:
提案 | 瀑布流布局触发器 |
---|---|
只用 Grid | grid-template-rows: collapse |
全新瀑布流布局 | display: masonry |
项目打包 | item-pack: collapse |
朝这个方向发展意味着我们可能不会使用来自“只用 Grid”或“全新瀑布流布局”的任何一个瀑布流布局触发器。我们可能放弃两者,转而采用这个新的触发器。
我们将在下一篇文章《项目流 – 第 2 部分:瀑布流的下一步》中详细介绍折叠项目打包如何适用于瀑布流布局。
项目松弛
`item-slack` 属性将允许您控制布局引擎在确定将下一个项目放置在哪里时有多挑剔。
对于瀑布流式布局,这将是之前讨论过的 `grid-slack` 或 `masonry-slack` 属性的新名称。默认的 `1em` 松弛意味着,“嘿,当你在寻找哪一列的顶部最近有空位(当你决定放置下一个项目时),如果差异在 1em 或更少,就不用担心。只需将它们视为打平。”
对于 Flexbox,这可以设置一个点,从松散打包切换到塞入额外项目——目前设置为零。值 `1em` 意味着,如果它在 1em 的范围内可以容纳,就塞进去,否则换到下一行。这个想法可以与 Flexbox 紧密打包的第一个想法(如上所述)结合,或者完全取代它。
也许 `-slack` 不是最好的名字。您可以在CSSWG 问题中提出其他词。例如,也许 `item-tolerance` 会更好——或者 threshold(阈值)、strictness(严格度)、adjust(调整)、sensitivity(敏感度)。
总体计划
将所有这些想法整合在一起,您将得到这个矩阵。

如果不清楚,Web 开发者将能够根据需要使用独立属性或简写属性。例如,您可以这样定义一个 Flexbox 上下文:
.container {
display: flex;
item-direction: column;
item-wrap: nowrap;
}
或者通过以下方式获得完全相同的结果:
.container {
display: flex;
item-flow: column nowrap;
}
当然,项目流的整个概念仍在进行中。大的改动仍然可能发生。这些名称是临时的。本文对工作原理的描述只是初稿。还有许多对话和决策需要做出。
您怎么看?
我们很想听听您对项目流的看法。
- 将 `flex-flow` 和 `grid-auto-flow` 组合成一个统一的系统是个好主意吗?
- 作为开发者,您会使用新语法来完成您今天用 `flex-flow` 和 `grid-auto-flow` 所做的事情吗?
item-direction: row | column | row-reverse | column-reverse
item-wrap: auto | nowrap | wrap | normal | reverse
item-pack: normal | dense
- 您还有什么其他想法可以将 Flexbox 和 Grid 中现有功能组合成一个统一系统?
- 您对为 Grid 和 Flexbox 添加新功能的可能性感到兴奋吗?哪些功能最有潜力帮助您完成工作,并解锁新的设计?
- Grid 不换行:`item-wrap: nowrap`
- Flexbox 紧密打包:`item-pack: dense`
- Flexbox 平衡换行:`item-pack: balance`
- 调整 Flexbox 中内容何时被塞入/不被塞入:`item-slack: <length-percentage>`
- 您还希望在这个统一系统中添加哪些新想法?
我们很想听听您的想法。在 Bluesky 或 Mastodon 上找到 Jen Simmons,分享您的反馈。或者更好的是,在博客文章中发布您的想法并分享链接。探索示例。解释想法。并让我们知道您是否喜欢这个方向。虽然 CSS 工作组已决定推行项目流,但一旦我们听取了 Web 开发者意见,如果需要,仍有充足时间转向另一个方向。
敬请关注《项目流 – 第 2 部分:瀑布流的下一步》,了解所有这一切如何影响瀑布流布局的计划,以及更多关于 `item-flow` 语法具体争论的细节。