每次给按钮加等宽 padding,文字都偏下——今天 CSS 悄悄把这件事接上了

配过图标的工程师大概都踩过这个坑:给一个按钮写 padding: 12px 24px,视觉上总觉得文字偏下而不是居中。调成 10px 12px 14px,眯眼微调几次,勉强过关。等设计师换了个字体,之前调好的数值又废了。

这个问题的根子,在 CSS 里藏了将近 30 年。

那个一直在作祟的 half-leading

每行文字在浏览器里并不是贴着 line-height 的中心,而是被均分成上下两半——一半在文字上方,一半在文字下方。这半个 line-height 的空间叫 half-leading,是 1998 年 CSS 2.1 规范里定下来的行为,至今没变过。

也就是说,line-height: 1.5 时,浏览器在每个文字行上下各留了 0.75em 的空白。这部分空白是给重音符号和下伸部预留的,但浏览器不会因为你的文字里没有这些东西就把它去掉。

结果:

/* 你以为这行代码实现了等宽 padding */
.button {
  padding: 12px 24px;
  line-height: 1.5;
}

/* 实际效果:文字视觉上偏下,因为 half-leading 在上面加了空白 */

按钮越高,这个偏差越显眼。你写的 padding: 12px,实际上视觉等效于 padding-top: 12px - 0.75em * line-height_ratio,而这行计算从来没出现在任何设计稿里。

三个属性把这件事彻底说清楚

CSS Text Level 4 新增了三个相关属性:

text-box-trim:告诉浏览器裁掉哪边——trim-start 裁上面,trim-end 裁下面,trim-both 两边都裁,none 不裁(默认行为)。

text-box-edge:告诉浏览器裁到哪个字体度量线——cap 是大写字母顶,ex 是 x-height,alphabetic 是基线,text 是字体自己定义的文本边缘。

text-box:上面两个的简写。

最常用的组合:

/* 上面裁到大写字母顶,下面裁到基线 */
.text {
  text-box: trim-both cap alphabetic;
}

/* 只裁上面 */
.heading {
  text-box: trim-start cap;
}

这行声明的意思是:计算 line box 高度时,把上边缘定在大写字母顶部,把下边缘定在基线,不再是原来那个包含 half-leading 的默认边缘。

Before/After 一眼看清楚

修复前(等宽 padding,文字视觉偏下):

.button-old {
  padding: 12px 24px;
  line-height: 1.5;
  border: 1px solid #ccc;
  background: #f5f5f5;
}

修复后(text-box 裁掉了 half-leading,文字视觉真正居中):

.button-new {
  padding: 12px 24px;
  line-height: 1.5;
  text-box: trim-both cap alphabetic;
  border: 1px solid #ccc;
  background: #f5f5f5;
}

两种写法 padding 值完全一样,区别只在有没有加 text-box。Safari 18.2 实测:加了这行之后,文字从视觉偏下变成真正的垂直居中,换任何字体都成立,不需要再微调数值。

另一个典型场景:文字和图标对齐

图标和文字并排时,文字总是比图标低一截,因为图标的 line box 是完整的,而文字的 line box 虽然等高,但 half-leading 把内容往上推了。

/* 之前:靠 eyeballing 调 margin-top */
.nav-item {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: -2px; /* 手工补偿,看运气 */
}

/* 现在:直接告诉浏览器文字的边界在哪 */
.nav-item {
  display: flex;
  align-items: center;
  gap: 8px;
}
.nav-item span {
  text-box: trim-start cap;
}

加了 text-box: trim-start cap 之后,文字的上边缘对齐大写字母顶部,图标和文字自然并排,不需要手工补偿。

三步接入

第一步:按需选值。

/* 按钮/标签——两边都裁 */
.btn {
  text-box: trim-both cap alphabetic;
}

/* 标题配图标——只裁上面 */
.heading {
  text-box: trim-start cap;
}

/* 低多小字——用 x-height 更协调 */
.caption {
  text-box: trim-both ex alphabetic;
}

第二步:调完 text-box 之后,padding 要重新给。

text-box 裁掉的是 half-leading 带来的额外空白,不是 padding 本身。如果你的按钮原来靠 padding: 10px 12px 14px(上下不等)来补偿视觉偏下,加了 text-box 之后应该恢复等宽 padding:

.btn {
  text-box: trim-both cap alphabetic;
  padding: 12px 24px; /* 恢复等宽,因为文字现在真正居中了 */
}

注意:裁掉半 leading 会让元素的 visual box 变小。如果按钮的 tap target 刚好卡在 WCAG 2.2 的 24×24px 最低线,加上 text-box 之后要重新测一下高度。

第三步:渐进增强。

.btn {
  padding: 12px 24px;
}
@supports (text-box: trim-both cap alphabetic) {
  .btn {
    text-box: trim-both cap alphabetic;
  }
}

不支持 text-box 的浏览器直接忽略这行,用原来的 half-leading 行为。不会破坏布局,只是没有裁切效果。

浏览器支持

浏览器 版本
Safari 18.2+
Chrome 133+
Firefox 154+(2026-08 稳定)
Edge 133+

三个主流引擎现在全部就位,可以直接用。不需要任何 polyfill。

评论区

0 条评论

登录后可评论。