日历、表格、导航的那条分隔线,你还在给每个子元素挂 border 吗——Chrome 和 Edge 149 把 gap 装饰做进了 CSS

Chrome 和 Edge 从 149 版开始支持一组新的 CSS 属性,专门用来画布局之间的分隔线。它把用了很多年的 column-rule 从多列布局里放出来,同时补了一个 row-rule 管另一个轴。也就是说,以前你要靠 border 和伪元素硬凑的那些线,现在浏览器自己会画。

先说清楚这个功能解决的具体痛点。做日历、数据表格、卡片列表、多列导航的时候,你几乎一定写过这两种代码之一:给每个子元素挂 border-right,然后祈祷最后一个子元素别多出那条线;或者用 ::before 在留白里画一条绝对定位的线,再默默计算容器宽度变了之后它会不会错位。

这两套写法的问题不在丑,而在于——它们把「视觉装饰」和「结构」绑死了。多一个 border 会让盒模型多两像素、多一个伪元素会多一份层叠上下文,屏幕阅读器也可能被多余节点干扰。而且容器一换列数、一换行数,你补的那条线就得跟着重算。

核心:column-rule 和 row-rule

旧写法里唯一能用的官方属性是 column-rule,可它只认多列布局(columns),你把它写进 grid 或 flex 里完全不生效。gap decorations 做的事,就是让同一个属性在所有布局类型里都生效:

.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 2rem;
  column-rule: 1px solid #ddd;   /* 竖线,画在列间距里 */
  row-rule: 1px solid #eee;      /* 横线,画在行间距里 */
}

关键特性有三个:

第一,装饰是纯视觉的,不影响布局。它不占空间、不改变 gap 宽度、不触发 reflow 的连锁反应,所以往现有项目里加基本是零风险的。

第二,支持 repeat() 语法。你可以像写 grid 轨道一样,让不同的间距用不同的线,多余的值自动循环。比如日历里每小时用实线、每半小时用虚线:

.calendar {
  row-rule-width: 2px, 1px;
  row-rule-style: solid, dashed;
  row-rule-color: #e8e4df, #f0ece7;
}

第三,有一个 rule 简写,两个轴一起设:

.nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: 2rem;
  rule: 2px dashed #666;
}

细粒度控制:交点、端点、层叠顺序

这是这个特性真正有意思的地方,也是它在试验期改过名字的部分。

rule-break 控制装饰在行线和列线交叉处怎么表现:none 表示穿过交点连成一片,intersection 表示在交点处断开。默认值 normal 的行为取决于容器类型。

rule-inset 控制线在间距内两端各缩进多少。它下面还有一组 longhand,让你分开控制不同的端点:

  • rule-inset-cap:只作用于端点(线碰到容器外边缘的那一头)
  • rule-inset-junction:只作用于交点(横线和竖线交叉的地方)
  • rule-inset-start / rule-inset-end:分别钉住起点和终点

举个例子,页脚链接矩阵想要竖线贯穿、横线两端留白收进去:

.links ul {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1.5rem 2.5rem;
  row-rule: 1px solid #c9a96a;
  column-rule: 3px solid #6a707a;
  rule-overlap: column-over-row;   /* 交点处竖线压横线 */
  column-rule-inset-cap: 1.75rem;  /* 只把竖线的两头收进去 */
}

这里 rule-overlap 也是改过名的,旧名是 gap-rule-paint-order,现在它决定横竖线交叠时谁在上层。还有个 rule-inset: overlap-join 关键字,能让线在拐角处自动接上。

空单元格旁边那条悬空的线

最后一个实用属性是 rule-visibility-items。网格的最后一排如果没填满,默认可能会在空位旁画出一条莫名其妙的线。把它设成 around,浏览器只在至少一侧有内容时才画这条装饰:

.posts {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 3rem;
  rule: 2px solid #999;
  rule-visibility-items: around;
}

一个必须记住的改名坑

如果你在早期试验版(Chrome 139)就试过这个功能,留神:row-rule-outsetcolumn-rule-outset 已经被改名成 row-rule-inset / column-rule-inset,而且语义反转了——以前是往外扩,现在是往内缩。照抄旧文章里的代码,线的位置会正好差一倍。

兼容性与今天能做的

支持情况是 Chrome / Edge 149+,以及同步 149 版本的其他 Chromium 浏览器。Firefox 和 Safari 尚未实现,不过都在标准讨论中,MDN 上也有对应的 polyfill 在开发。

所以上线策略很清楚:把分隔线当渐进增强用。线只在支持的浏览器里出现,不支持就什么都不画,页面布局不会崩——这本来就是这个特性设计的友好之处。如果你必须保证每个浏览器都有线,那就用 polyfill 或保留旧的 border 方案兜底。

三件今天就值得做的事:

  1. 在项目里搜 border-right / border-bottom 专门用来画分隔线的写法,列个清单——这些是 gap decorations 的直接替换目标。
  2. 挑一个隔离的组件(比如页脚链接网格、日历)先试点,用 @supports (row-rule: 1px solid red) 包一层,验证渲染效果。
  3. 如果清单里有跨行跨列的复杂网格,重点测 rule-breakrule-overlap 两个值——交点行为是多列时代从没处理过的场景,也是这个特性最容易出意外的地方。

一条线的写法,从「给元素挂 border 再擦屁股」,变成了「告诉浏览器间距该怎么装饰」。布局和装饰,终于分开了。

评论区

0 条评论

登录后可评论。