跳到正文

用 font-synthesis 补齐缺失的粗体

博客换成悠哉以后,深色正文里的加粗词很容易扫过去。我用字体扩展给 GitHub 也换了悠哉,那里的粗体却很显眼。两边都请求较高的字重,我就用一句话比较这五个加粗的字。

雨停以后,窗边的书页被风轻轻翻过。

悠哉的四种字重组合。左侧浅色,右侧深色,每组只切换「窗边的书页」的字形。

浅色和深色都用 16px,每种主题的正文和粗体用同一个颜色。Light 搭配 Medium 或 Regular 时,差别偏轻。用 Regular 合成粗体后,「窗」和「页」的笔画间隙更紧,但这五个字也容易从句子里看出来。正文换成 Regular 后,整句的粗细更接近博客原来的阅读节奏,所以我选了最后一组。可放大查看 Windows Chrome 的 2× 原图。

先匹配字重,再决定是否合成

悠哉 v0.868 提供 Light、Regular、Medium,没有单独的 Bold。我原先把三份文件登记在同一个家族里,并用 @font-face 把 Medium 声明为 500 900。这个声明告诉浏览器,范围内的字重都能用同一份 Medium 提供。于是粗体请求 700 时,浏览器直接画出 Medium 的轮廓。字面文件见 悠哉 v0.868,匹配规则见 CSS Fonts 4。

我在 Windows Chrome 打开 GitHub 的 Markdown 页面,按字体扩展的规则,把本机悠哉放到中文样句的字体列表最前面。页面对强调文字请求 600,样字实际用的是 Yozai-Regular。加上 font-synthesis-weight: none,计算值还是 600,笔画却回到了 Regular 的粗细。再打开合成,浏览器会把这个字面加厚。测试页面是 GitHub Markup 的 README。

font-synthesis-weight 控制浏览器能否合成缺少的粗体,初始值是 auto,也会被后代继承,定义见 CSS Fonts 4 的合成粗体规则。如果页面本来就允许合成,700 仍然会命中声明覆盖该值的 Medium。要从 Regular 合成,给 strong 选一个只登记 Regular 的家族,同时继续请求粗体。

用 Regular 做正文和合成粗体的底稿

下面是博客字重配置的最小写法。站点的常用字子集和完整字体沿用同样的字重声明,按各自的 unicode-range 加载。

@font-face {
  font-family: 'Yozai Prose';
  src: url('/fonts/yozai-regular.c7e86c37a9c5.woff2') format('woff2');
  font-style: normal;
  font-weight: 400;
}

.markdown-body {
  font-weight: 400;
  font-synthesis-weight: auto;
}

.markdown-body strong {
  font-family: 'Yozai Prose', sans-serif;
  font-weight: 700;
}

Markdown 的 **…** 会生成 <strong>。这段 CSS 让普通字请求 400,强调字请求 700,两者沿用各自所在段落的颜色。可以在 DevTools 选中强调的汉字,先看 Computed 里的 font-weight,再看 Rendered Fonts 的字面名称。在同一个元素上临时切换 auto 和 none,若计算值与字面相同而笔画粗细有变化,就能看出合成在哪一步起作用。

什么时候该保留合成

合成能补出粗体效果,浏览器决定具体怎样加厚。用只登记 400 的悠哉 Regular 在 Chrome 157 实测,请求 100–500 得到相同的 Regular,请求 600–1000 则得到相同的合成粗体。需要的是稍粗一点的 500 时,还得看字体是否提供相应字面。系统字体遇到这种情况的匹配规则见 系统字体的 font-weight 500 兼容性处理。

如果只想调整粗体回退,用 font-synthesis-weight 就够了。简写 font-synthesis 还控制斜体等合成选项,写成 none 会一并关闭。对于正文里的重点,我会默认允许合成粗体,让读者先看出强调。如果项目要求保持特定的笔画和留白,就选一款提供所需粗体的字体并准确声明字重。浏览器匹配到真实粗体时,会直接使用字体提供的字形。