写了三年排版,每次调整字体大小都要重新算一遍负 margin——今天 Firefox 154 把这件事彻底原生化了

写了三年排版,每次调整字体大小都要重新算一遍负 margin——今天 Firefox 154 把这件事彻底原生化了。

对,说的就是 text-box-trim。这个 CSS 属性 Firefox 154 上周刚发布,但大多数前端团队可能还没注意到——因为它解决的那个问题,大家早就习惯了,习惯了到觉得那是理所当然的。

字体的「呼吸」问题

每套字体都有自己的上下留白。这部分空间是真实存在的,它被刻进字体的度量数据里。你写 margin-top: 24px,标题实际上不会距离上方元素 24 像素——字体自己的上边界要吃掉一部分。更要命的是,不同字体这套留白完全不一样,一行文字里混了两套字体?间距就彻底乱了。

这导致了一个几乎每个前端项目都有的代码:

.card-title {
  margin-bottom: -0.2em; /* 这个数字是怎么来的? */
}

这个 -0.2em 从哪来的?量出来的。换了一套字体,或者 webfont 加载前后切换了 fallback 字体,这个数字就废了。大多数项目的代码库里,这段注释不存在,解释这段数字的文档也不存在,发现问题的人只能靠猜。

text-box-trim 就是来解决这个的。

三行 CSS,删掉整个 workaround

h1 {
  text-box-trim: both;
  text-box-edge: cap alphabetic;
}

text-box-trim 指定要裁掉哪一边——overunderboth 或者 nonetext-box-edge 指定裁到哪个度量边界——cap heightex heightalphabeticideographic……两者配合,CSS 直接告诉你:「这段文字的实际边界到这里为止」。

结果是:你在 CSS 里写的 margin: 24px,真正落在 24 像素。不需要负 margin,不需要 JS 量尺寸,不需要 font-metrics-api

/* 之前:负 margin 续命 */
.card-title {
  margin-bottom: -0.25em;
}

/* 之后:原生支持 */
.card-title {
  margin-bottom: 1em;
  text-box-trim: under;
  text-box-edge: alphabetic;
}

而且它是声明式的,不是 hack——换字体、换字号、在 webfont 加载过程中切换 fallback,字号关系全部自动保持正确。

另一个被删掉的 pattern:列表序号

text-box-trim 之外,Firefox 154 还给了另一个礼物:sibling-index()sibling-count()

做列表动画时,需要「第几个」来算 stagger 延迟是常见需求。现在的做法通常是:

items.forEach((item, index) => {
  item.style.setProperty("--i", index);
});

然后在 CSS 里:

.item {
  animation-delay: calc(var(--i) * 80ms);
}

换句话说:列表的「视觉行为」由 JS 决定,而不是 CSS。

sibling-index() 把这件事还给了样式表:

.item {
  animation-delay: calc((sibling-index() - 1) * 80ms);
}

item:nth-child(3)sibling-index() 就是 3,从 1 开始计数,可以在 calc() 里直接用。同家族的 sibling-count() 返回同级元素总数,两者和 text-box-trim 一样,可以删掉一整个 JS 工作量。

能删多少代码?

根据 PeopleAreGeek 的分析,这两个 CSS 新特性在 Firefox 154 里出现,最直接的影响是:

可以删掉:

  • 所有依赖字体度量算出来的负 margin(通常是 em 单位,查文档都不知道为什么是这个数)
  • 所有用 JS 给列表元素注入 --i 变量的循环代码
  • 如果你的项目里用了 counter-reset / counter-increment 配合 nth-child 做的序号功能,部分可以换成 sibling-index()

这是浏览器原生帮我们删掉那些「大家都这么写所以我也这么写」的 legacy code。

下一步

Firefox 154 已在 2026 年 8 月 18 日发布稳定版,Chrome 和 Edge 目前尚未支持这两个属性。text-box-trimtext-box-edge 属于 Firefox 独有——如果你的项目面向多浏览器,用 @supports 做渐进增强即可:

h1 {
  margin-bottom: 1em;
}

@supports (text-box-trim: under) {
  h1 {
    text-box-trim: under;
    text-box-edge: alphabetic;
  }
}

这可能是最近一段时间里,最值得写进设计系统的 CSS 新特性之一——因为它直接解决的是那种「每个人都遇到过但每个人都懒得修」的长期问题。

评论区

0 条评论

登录后可评论。

小鹿·界面实验室 239 阅读