字体一加载就抖一下?CSS font-size-adjust 让回退字体自己对齐尺寸

自定义字体没加载出来的那一瞬间,你的页面会「抖」一下——不是网络慢,是回退字体和正主根本不是一个尺寸。CSS 里一直有个属性专门治这件事,叫 font-size-adjust。它以前只认小写字母高度,现在 Level 5 把它扩成了五档尺度,连中文字体都能对齐。

先把结论放前面:字体回退导致的布局抖动(CLS),不需要换字体、不需要改架构,一行 font-size-adjust 就能压掉大半。前提是你要选对「按哪个尺子对齐」。

问题:同一段文字,换个字体就胖一圈

网页字体(web font)没加载完时,浏览器只能先用回退字体顶上。麻烦在于,同样是 font-size: 16px,Verdana 的小写字母明显比 Times 大一圈:

  • Verdana 的纵横比(x-height / font-size)约 0.55
  • Times New Roman 约 0.45
  • Georgia 约 0.48

差 0.1 是什么概念?16px 字号下小写字母高度差近 1.6px。一行看不出来,一整屏正文累积起来,就是文字块整体变高、按钮被往下推——Lighthouse 里那个红色的 CLS 就是这么来的。

真实案例里,一个营销页的 CLS 是 0.34,性能分只有 62。排查掉图片和广告位之后,罪魁祸首就是 Web Font 替换回退字体时的整段 reflow。修复后用指标对齐的回退字体,CLS 直接掉到 0.02;另一个站点的单次字体替换从 0.135 → 0.000

方案:告诉浏览器「按哪个尺子对齐」

font-size-adjust 的语义是:不管实际用哪个字体,都让指定的那个度量等于 设定值 × font-size。规范里给出的计算公式很直白:

u = ( m / m′ ) × s
  • s:计算出的 font-size
  • m:你在 font-size-adjust 里设定的目标比例
  • m′:实际用到的那款字体自己的度量
  • u:最终调整后的字号

也就是说,浏览器会反过来缩放回退字体,让它的度量去贴合你指定的比例。

CSS Fonts Level 5 把这个属性从「只能调 x-height」扩成了 五档度量

font-size-adjust: ex-height 0.53;  /* 小写字母 x 高度,默认值 */
font-size-adjust: cap-height 0.72; /* 大写字母高度 */
font-size-adjust: ch-width 0.55;   /* 字符 "0" 的水平宽度 */
font-size-adjust: ic-width 1.0;    /* 汉字 "水" 的水平宽度 —— CJK 专用 */
font-size-adjust: ic-height 1.0;   /* 汉字 "水" 的垂直高度 —— CJK 专用 */

最省事的写法是 from-font:让浏览器自己读取首选字体的度量,然后拿它去校准回退字体。

body {
  font-family: "Inter", system-ui, sans-serif;
  font-size-adjust: from-font; /* 首选字体加载不出来时,按 Inter 的度量对齐 */
}

最后一行的 none 是默认值,等于完全不调整。还有一点容易踩:如果同一个度量已经在 @font-face 里被 size-adjust 改写过,那么 font-size-adjust 计算时用的是改写后的值——两者一起用时,size-adjust 会被抵消掉,别以为它们在叠加。

为什么中文字体特别需要它

ic-widthic-height 用的是汉字「水」(U+6C34)作为基准字。这个设计对中国开发者格外友好:中文排版里,回退链的首选字体常常是思源黑体 / 苹方,回退到系统默认宋体或 Arial,汉字块的整体尺寸差异比拉丁字母更明显——因为一个汉字占满整个 em 方框,度量稍有偏差,行高就整体变了。

给中英混排的正文加上:

.article-body {
  font-family: "Noto Sans SC", "PingFang SC", sans-serif;
  font-size-adjust: ic-width 1.0; /* 让回退字体的汉字宽度对齐首选字体 */
}

这里的 1.0 意思是:让汉字宽度严格等于 font-size(标准全角宽度)。选 1.0 是个稳妥起点,因为主流中文字体的全角字宽本来就接近 1em。

浏览器支持

  • Chrome / Edge 127+
  • Safari 17+(含 iOS)
  • Firefox 3+(它是这个属性的老东家,最早实现;118 之后短暂移除,后来又补回)
  • 移动端:Android Chrome 151 / Android Firefox 153 起

不支持的浏览器会把这条规则直接忽略,字号维持原样——属于纯渐进增强,不会把页面弄坏,这也是它比一整套餐 @font-face 度量覆盖更省心的原因。

落地建议

  1. 先量数据,再调值。用 font-size-adjust: from-font 起步,让浏览器自己算;如果兼容性有顾虑,再手填首选字体的纵横比(Inter ≈ 0.53,Arial ≈ 0.52)。
  2. 中英混排优先用 ic-width,纯拉丁正文用 ex-height
  3. size-adjust 别叠着用,选一个即可,否则白忙。
  4. 改完用 Performance 面板复测 CLS,对齐对了,Layout Shift 条会明显变短。

一句话总结:字体抖动这件事,过去大家都靠 @font-face 手写一堆 ascent-override / descent-override / size-adjust 硬扛,现在一行 font-size-adjust 就能把「按哪个尺子对齐」说清楚。中文项目尤其值得试 ic-width——标准早就在那儿了,只是没人告诉你。

评论区

0 条评论

登录后可评论。