Highlight
Safari 26.4 引入
display: grid-lanes,用三行 CSS 替代 JavaScript 瀑布流库,并通过flow-tolerance属性解决 DOM 顺序与视觉顺序不一致导致的键盘焦点跳跃问题。
核心内容
瀑布流布局的困境
做过图片墙、卡片流的开发者都经历过这个场景:图片高度参差不齐,用 Flexbox 会留下大片空白,用 CSS Grid 又必须把每张图塞进等高的单元格。三种妥协方案都不理想:拉伸会变形,缩放会溢出,裁剪会丢失内容。最后大家只能引入 JavaScript 库,在客户端计算每张图的高度再动态定位。这套方案能跑,但额外增加了脚本体积和运行时开销,在移动端滚动时尤其吃力。
(00:57)
Grid Lanes 的定位
CSS 现有两种布局模式回答了同一个问题:元素放在哪里、占多少空间。Flexbox 只控制一个轴,内容沿单条轨道流动,换行时继续沿同一方向排列。Grid 控制两个轴,把元素放进行列交叉的单元格里。Grid Lanes 卡在两者之间:只锁死一个轴,另一个轴完全交给内容自己撑开。
(01:13)
结果是元素被分发到多条轨道里,每条轨道独立计算高度。浏览器把每个新元素放进当前最短的列,后续元素依次填补下方空隙。这种”最短优先”算法天然实现了紧密排列,同时保留每个元素的自然比例。
(03:07)
从瀑布流到砖墙,只改一个属性
Grid Lanes 支持两种视觉形态。默认是瀑布流:定义列轨道,内容自上而下填充各列。把 grid-template-columns 换成 grid-template-rows,布局立刻变成横向砖墙,元素从左到右逐行流动。两种模式共用同一套语法,切换成本极低。
(04:31)
flow-tolerance 解决无障碍痛点
Grid Lanes 被放进 Accessibility & Inclusion 轨道,核心原因是 flow-tolerance 属性。传统”最短优先”算法有个副作用:当相邻两列高度接近时,下一个元素可能跳到右侧较短的列,导致视觉顺序和 DOM 顺序错位。键盘用户按 Tab 键时,焦点在屏幕上左右横跳,屏幕阅读器读出的内容顺序也是乱的。
flow-tolerance 的默认值是 1em。它的规则是:如果较高列与最短列的高度差在容差范围内,浏览器优先把元素放入 DOM 顺序更靠前的列,而不是死磕绝对最短列。这个微小的视觉妥协,换来了线性的阅读顺序和稳定的焦点路径。
(07:15)
详细内容
创建 Grid Lanes 容器
(03:58)
最简声明只需要一个属性:
.container {
display: grid-lanes;
}
加上列定义和间距:
.container {
display: grid-lanes;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
}
关键点:
display: grid-lanes启用新布局模式,语法与display: grid平行repeat(3, 1fr)把容器宽度分成三等份,fr是 fractional unit 的缩写gap: 10px的写法与 CSS Grid 完全一致- 只能定义
grid-template-columns或grid-template-rows中的一个,不能同时写两者
砖墙变体
(04:33)
把列定义换成行定义,瀑布流变成砖墙:
.container {
display: grid-lanes;
grid-template-rows: repeat(3, 1fr);
gap: 10px;
}
关键点:
grid-template-rows替代grid-template-columns- 元素沿行方向流动,形成横向排列
- 方向二选一,不能混用
响应式列宽
(05:10)
让浏览器自动决定塞下几列:
.container {
display: grid-lanes;
grid-template-columns:
repeat(auto-fill, minmax(200px, 1fr));
gap: 10px;
}
更复杂的重复模式:
.container {
display: grid-lanes;
grid-template-columns:
repeat(auto-fill,
minmax(8rem, 1fr)
minmax(14rem, 2fr));
gap: 10px;
}
关键点:
auto-fill根据容器宽度自动创建尽可能多的列minmax(200px, 1fr)保证每列至少 200px,剩余空间均分- 可以定义宽窄交替的重复模式
控制单个元素
(05:45)
让某个元素跨两列:
.container {
display: grid-lanes;
grid-template-columns: 1fr 1fr 1fr;
gap: 10px;
}
.item {
grid-column: span 2;
}
精确指定起始位置:
.item {
grid-column: 2 / span 2;
}
关键点:
grid-column: span 2沿列方向跨越两格grid-column: 2 / span 2从第 2 列开始,跨 2 列- 可以控制列位置,但行位置由 Grid Lanes 自动计算
与 Subgrid 集成
(06:22)
一个跨两列的卡片,内部元素与父级网格对齐:
.container {
display: grid-lanes;
grid-template-columns: 1fr 1fr 1fr;
gap: 10px;
}
.item {
display: grid-lanes;
grid-template-columns: subgrid;
grid-column: span 2;
}
也可以嵌套普通 Grid:
.item {
display: grid;
grid-template-columns: subgrid;
grid-column: span 2;
}
关键点:
subgrid让子元素继承父级的列轨道定义- 图片占一列,文字占另一列,各自按内容自适应
- Grid Lanes 和普通 Grid 可以互相嵌套
flow-tolerance 调优
(08:37)
默认行为:
.container {
display: grid-lanes;
grid-template-columns: 1fr 1fr;
gap: 10px;
flow-tolerance: normal;
}
自定义容差:
.container {
display: grid-lanes;
grid-template-columns: 1fr 1fr;
gap: 10px;
flow-tolerance: 2.1em;
}
关键点:
- 默认值为
1em - 浏览器比较各列高度,如果高度差在容差内,优先按 DOM 顺序填充
- 容差越大,视觉顺序越接近 DOM 顺序,但列高差异可能更明显
- Safari 开发者工具提供 Grid Lanes 专用 Overlay,显示列线、元素顺序编号和间隙
核心启发
-
做什么:把 App 内的图片瀑布流从 JavaScript 库迁移到原生 CSS 为什么值得做:减少脚本体积,消除运行时高度计算,WKWebView 内滚动更流畅 怎么开始:用
@supports (display: grid-lanes)做渐进增强,支持的浏览器走原生,不支持的继续用旧方案 -
做什么:给内容型网站加砖墙式时间轴布局 为什么值得做:Grid Lanes 的横向流动模式天然适合展示时间线或步骤流程 怎么开始:把
grid-template-columns换成grid-template-rows,配合span控制重点卡片 -
做什么:用
flow-tolerance修复现有瀑布流的无障碍问题 为什么值得做:很多第三方瀑布流库都存在 DOM 顺序与视觉顺序不一致的缺陷 怎么开始:把flow-tolerance: 1em加到 Grid Lanes 容器上,再用 Safari 开发者工具的 Overlay 检查 Tab 焦点路径 -
做什么:在跨列卡片里用
subgrid对齐图文 为什么值得做:大图配文的卡片经常需要内部网格与外部网格对齐 怎么开始:卡片加grid-column: span 2和grid-template-columns: subgrid,图片和文字各自占一列
关联 Session
- What’s new in WebKit for Safari 27 — Safari 27 全部新特性总览,Grid Lanes 只是其中一部分
- Design principles for spatial user interfaces — 空间界面设计原则,与响应式布局理念相通
- SwiftUI essentials — SwiftUI 的 LazyVGrid 与 Grid Lanes 解决的是同一类问题,可以对比两种平台的实现方式
评论
GitHub Issues · utterances