Chromium 83 中适用于 macOS system-ui 字体的更多可变字体选项

Catalina 为 macOS 带来了新的统一可变系统字体。

Dominik Röttsches
Dominik Röttsches

CSS 字体模块级别 4 规范的“system-ui”部分定义了一个 system-ui 字体关键字,让开发者能够直接在其网站和应用中使用内置、Turbo 优化、本地化、超级高品质、无需下载的默认操作系统字体。

body {
  font-family: system-ui;
}

此排版选择类似于说“使用此用户当前语言区域的默认系统字体”。

在 macOS 上,system-ui 字体是 San Francisco,该字体经过了设计团队的审核、测试,并且…最近进行了升级!首先,我们将介绍 Catalina 中令人兴奋的新可变字体功能,然后介绍一些bug 以及 Chromium 工程师如何解决这些 bug

本文假定您已熟悉可变字体。如果没有,请参阅网页可变字体简介和下面的视频。

浏览器兼容性

在撰写本文时,Chromium(56 版起)、Edge(79 版起)、Safari(11 版起)和 Firefox(43 版起)均支持 system-ui,但需要使用 -apple-system 关键字。如需了解最新动态,请参阅我可以使用可变字体吗?

新能力

从 Chromium 83 开始,Web 开发者可以使用 Catalina 为系统字体带来的新功能。system-ui 字体现在具有更多可变设置:光学字号和 2 种独特的粗细调整:

Mojave
h1 {
  font-family: system-ui;
  font-weight: 700;
  font-variation-settings:
    'wght' 750
  ;
}
Catalina
h1 {
  font-family: system-ui;
  font-weight: 700;
  font-variation-settings:
    'wght' 750,
    'opsz' 20,
    'GRAD' 400,
    'YAXS' 400
  ;
}

在 Mojave 中,system-ui 是一种仅具有 wght 设置的可变字体。而 Catalina 上的 system-ui 是一种具有 wghtopszGRADYAXS 设置的可变字体。

我认为这是一个不错的渐进式增强设计机会!如果您愿意,也可以深入了解系统字体的微妙之处。

wght

接受介于 0900 之间的字体粗细,并对所有字符均等地应用。

/* 0-900 */
font-variation-settings: 'wght' 750;

opsz

光学调整类似于调整字间距或字母间距,但间距是由人眼而非数学计算得出。19 或以下值用于表示文本和正文的间距,而 20 或以上值则用于表示显示标题和标题的间距。

/* 19 or 20 */
font-variation-settings: 'opsz' 20;

GRAD

与粗细类似,但不涉及水平间距。该属性接受 4001000 之间的值。

/* 400-1000 */
font-variation-settings: 'GRAD' 500;

YAXS

垂直拉伸字形。该属性接受 4001000 之间的值。

/* 400-1000 */
font-variation-settings: 'YAXS' 500;

组合选项

只需几行 CSS 代码,我们就可以将字体设置调整为我们选择的粗体,或尝试其他有趣的组合:

font-weight: 700;
font-weight: bold;
font-variation-settings: 'wght' 750, 'YAXS' 600, 'GRAD' 500, 'opsz' 20;

这样一来,macOS 上的 Chromium 用户就会看到您升级的自定义 750 字号,以及一些其他有趣的调整 👍?

游乐场

点击下面 Glitch 中的 Remix to Edit(混剪以修改),获取可修改的 Glitch 副本,然后修改新的 font-variation-settings 选项,看看对字体有何影响。请注意,只有在您使用 macOS Catalina 设备时,此 Glitch 才有效。

macOS 10.15 为其系统字体添加了新功能,并且在 macOS 10.15 中,Chromium bug 跟踪器中记录了一个棘手的 system-ui bug。我想知道它们是否有关联?!

附录:system-ui 回归

这个故事的开头部分是另一个错误:#1005969。此问题是在 macOS 10.15 中报告的,因为 system-ui 字体间距看起来较窄且比较拥挤。

对 Facebook 群组页面上的两段文字的对比。左侧是 Chrome,右侧是 Safari,Chrome 不太好用,但间距略紧一些
左侧为 Chrome(跟踪更紧密),右侧为 Safari(光学间距更好)

背景

您是否注意到,在 macOS 10.14 中,当段落或标题变大或变小时,段落或标题会“贴靠”为其他外观的字体。

在 Mojave (macOS 10.14) 上,system-ui 字体会根据目标字体大小在两种字体之间切换。当文本位于 20px 下时,macOS 使用的是“San Francisco Text”。当文本大小为 20px 或更大时,macOS 会使用“San Francisco Display”。光学调整已静态内置到两个单独的字体中。

Catalina (macOS 10.15) 为 San Francisco 提供了新的统一可变字体。无需再管理“文字”和“展示位置”。它还新增了前面介绍的新变体设置 opsz

h1 {
  font-variation-settings: 'opsz' 20;
}

遗憾的是,新 Catalina 字体中的默认 opsz 值为 20,而 Chromium 工程师还没有准备好将 opsz 应用于系统字体。这导致较小的尺寸显示得过窄。

为解决此问题,Chromium 需要正确将 opsz 应用于系统字体。这导致了问题 1005969 得到了解决。胜利!还是…

尚未完成

问题就在于此:Chromium 应用了 opsz,但某些内容看起来仍然不正确。Mac 上的系统字体还有一个名为 trak 的额外字体表,用于调整水平间距。在修复问题时,Chromium 工程师发现,在 macOS 上,从 CTFontRef 对象检索横向指标时,trak 指标已纳入指标结果中。Chromium 的形状调整库 HarfBuzz 需要尚未考虑 trak 值的指标。

列表中显示 system-ui 及其所有字体粗细和变体。其中有一半没有应用权重差异。
左:粗体粗细应用于 19 号及以下字体大小。右侧:字号为 20 或更大的字体会失去粗体样式

在内部,Skia(图形库,而非同名字体)会同时使用 CoreGraphics 中的 CGFontRef 类和 CoreText 中的 CTFontRef 类。由于这些对象之间需要进行内部转换(用于保持向后兼容性并访问这两个类所需的 API),因此在某些情况下,Skia 会丢失粗体信息,并且粗体字体会停止工作。此问题已在问题 1057654 中跟踪。

由于 Chromium 仍支持 macOS 10.11,因此 Skia 仍需要支持 macOS 10.11。在 10.11 中,“San Francisco Text”和“San Francisco Display”字体甚至都不是可变字体。而是每个字体系列都包含适用于每种粗细的单独字体。在某个时间点,它们的字形 ID 彼此不同步。因此,如果 Skia 使用“San Francisco Text”进行文本塑形(将文本转换为可绘制的字形),那么使用“San Francisco Display”绘制时,文本将会是乱码,反之亦然。即使 Skia 只请求了其他大小,macOS 也可能会切换到其他大小。应该可以始终使用其中一种字体,只需对其进行缩放(使用矩阵进行放大,而不是请求更大的尺寸),但 CoreText 存在一个问题,即它不会放大 sbix(彩色表情符号)字形(只会缩小)。情况比这更复杂一些。CoreText 实际上似乎会在应用矩阵后限制垂直范围,这似乎与它无法以 45 度角绘制表情符号有关。无论如何,如果您希望表情符号显示为大号,都需要复制字体以获取大号版本。

因此,为了在内部创建不同大小的 CTFont 对象的副本,同时确保使用相同的基础字体数据,Chromium 会从 CTFont 中提取 CGFont,然后根据 CGFont 创建新的 CTFontCGFont 对象与大小无关,神奇的切换发生在 CoreText 级别)。在 10.154 之前,这一直有效。在 10.15 中,这次往返行程最终丢失了过多信息,导致了权重问题。Flutter 注意到了重量问题,并提供了另一种调整大小的修复方法,以便直接从原始 CTFont 创建新的 CTFont,同时使用 CoreText 中旧的但未记录的属性直接控制光学尺寸。这可确保 10.11 正常运行,并修复了其他问题(例如明确将光学尺寸设为默认值)。

不过,这样可以在字体中保留更多 CoreText“魔力”。其中之一似乎是,它仍然会通过 trak 表(Chromium 已尝试通过另一个未记录的属性抑制其应用)以外的方式调整字形前进。

CGFont 不会执行任何此类“魔法”,因此 Chromium 或许可以从 CTFont 中移除 CGFont,然后仅使用它来获取预付款?遗憾的是,这行不通,因为 CoreText 还会以其他方式干扰字体。例如,这会使小表情符号比您实际请求的表情符号稍大(略扩大其大小)。CGFont 不知道这一点,因此基于 sbix 的表情符号最终会彼此过近,因为您会以一种尺寸进行测量,但 CoreText 会将它们绘制得稍大一些。Chromium 确实希望 CTFont 取得进展,但希望在不跟踪的情况下取得进展,最好也不是任何其他顾虑。

由于间距问题的修复需要一组相互关联的 Blink 和 Skia 修复,因此 Chromium 工程师无法“仅仅还原”来解决此问题。Chromium 工程师还尝试使用其他 build 标志更改 Skia 中的与字体相关的代码路径,这修复了粗体字体问题,但又使间距问题复发了。

解决方法

Chromium 最终当然希望将这两个问题都修复。Chromium 现在会借助 HarfBuzz 内置字体 OpenType 字体指标函数,直接从系统字体字体表中的二进制数据中检索水平指标。这样一来,当字体包含 trak 表时(表情符号字体除外),Chromium 会绕过 CoreText 和 Skia。

列表中显示了 system-ui 及其所有字体粗细和变体。之前无法正常使用的一半屏幕现在看起来很好。

与此同时,我们仍在跟踪 Skia 问题 10123,以便在 Skia 中彻底解决此问题,并恢复使用 Skia 从中检索系统字体指标,而不是通过 HarfBuzz 进行当前修复。