必须写全 font-family 堆叠链并配合 font-size-adjust 和 line-height 微调:font-family 需显式列出 -apple-system、segoe ui、roboto 等跨平台等效字体;font-size-adjust 按 x-height 比值校准 fallback 字体视觉大小;line-height 用无单位数值并依中英混排调整;尺寸测量须待 document.fonts.load() 完成后执行。

font-family堆叠链必须写全,不能只靠“Helvetica Neue, sans-serif”这种简配
Mac 和 Windows 的系统字体天然不兼容:“Helvetica Neue”在 Windows 上根本不存在,“Segoe UI”在 macOS 上也加载失败。如果只写 font-family: "Helvetica Neue", sans-serif,Windows 会跳过第一项直接用 sans-serif 泛型,而这个泛型在不同系统映射的实际字体(macOS 是 .SF NS,Windows 是 Segoe UI)字宽、x-height、ascender 高度差异明显——动画中文字重排、容器抖动、行高错位全是它惹的祸。
正确写法是显式列出跨平台等效链:
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
-
-apple-system和BlinkMacSystemFont覆盖 Safari / Chrome on macOS -
"Segoe UI"是 Windows 10+ 默认,优先级高于Roboto -
Roboto作为 Android 和部分旧 Windows fallback,比Arial更现代、字宽更接近 - 末尾保留
sans-serif是兜底,但别指望它能“自动对齐”,它只是最后一道保险
font-size-adjust 是解决 fallback 字体视觉大小不一致的唯一可控手段
当指定的字体加载失败、回退到下一个时(比如 “PingFang SC” → “Microsoft YaHei” → “Arial”),不同字体的 x-height 差异会让同样 font-size: 16px 的文字看起来一个大一个小——尤其在标题或按钮文字里特别扎眼。这不是 bug,是字体度量天然是不同的。
font-size-adjust 不是锦上添花,而是必要校准:
- 值不是随便写的,它是目标字体 x-height / font-size 的比值,常见中文字体约
0.5,西文字体如 Inter 约0.48 - 写成
font-size-adjust: 0.5后,浏览器会按比例缩放 fallback 字体,让视觉字号基本对齐 - 必须配合
font-size使用,单独设无效;且不能和font-optical-sizing: auto冲突(后者只对支持的可变字体生效) - 实测中,未加该属性的混排段落在 Windows 上常比 macOS 小 10%~15%,加了之后偏差收窄到 ±2%
line-height 不能只设数值,要匹配字体实际渲染基线
中文排版里最常被忽略的坑:把 line-height: 1.6 当万能解,结果在 Windows 上文字发虚、baseline 上浮,在 macOS 上又显得松散。原因在于 ClearType 和 Quartz 渲染引擎对 baseline 的锚点计算方式不同,而 line-height 数值是基于字体度量(em-box)算的——如果 fallback 字体 em-box 高度不一致,同一数值表现就分裂。
- 优先用无单位数值(如
1.5),避免line-height: 24px这类绝对值,否则响应式缩放时彻底失效 - 中英混排正文建议从
1.5起调,不是1.6:前者对微软雅黑/PingFang 的 ascender/descender 更友好,后者更适合纯英文环境 - 若用了自定义字体,必须检查其 OpenType 度量参数(尤其是
typoAscender和winAscent),差距过大时需手动微调line-height或改用padding-top/bottom补偿 - Flex 容器里别依赖
align-items: center居中文字——它对齐的是 flex item 边框,不是文本基线,跨系统误差更明显
字体加载完成前禁止读取尺寸,document.fonts.load() 是唯一可靠信号
动画启动瞬间调用 el.offsetHeight 或 getComputedStyle(el).fontSize,拿到的极大概率是 fallback 字体的尺寸,而非你指定的 Web Font。Mac 和 Windows 加载速度、字体缓存策略、渲染管线触发时机都不一样,导致首帧错位、容器跳变、文字闪烁——这不是代码写错了,是时机没卡准。
- 别用
DOMContentLoaded,它只管 HTML 解析完,不管字体 - 必须用
document.fonts.load("16px 'Inter'", "a")(第二个参数是测试字符,提升检测精度) - 加载成功后才执行动画初始化或尺寸测量,否则一律 fallback 到预估值(比如按
font-size: 16px; line-height: 1.5估算高度) - 如果页面有多个字体,
document.fonts.load()支持 Promise.all,但注意不要阻塞主线程太久,可加 timeout 降级
真正麻烦的不是怎么写,而是字体加载、系统渲染、CSS 计算三者的时间差在不同设备上不可预测。哪怕堆叠链写对了、font-size-adjust 加上了、line-height 调准了,只要尺寸读取早于字体就绪,所有努力都会在首帧崩掉。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











