WebKit

Better typography with text-wrap pretty

2025年4月8日 作者:Jen Simmons

目录

text-wrap: pretty 的支持已在 Safari Technology Preview 中发布,为 Web 上的排版带来了前所未有的润色效果。 让我们来看看 WebKit 版本的 pretty 做了什么 —— 它可能比你期望的要多得多。 然后,我们将它与 balance 和其他 text-wrap 值进行比较,以更好地理解何时使用哪个。

关于什么是“好的”排版的想法深深植根于使用金属、木材或墨水手工设置类型的时代。 排版员在决定一个单词是否应该放在一行的末尾、下一行的开头或用连字符分隔时,会非常小心。 他们的努力提高了理解力,减少了眼睛疲劳,并且使阅读体验更加愉快。 虽然美丽可能是主观的,对什么是“更好”存在分歧,但在全球范围内也存在强烈的排版传统,代表着各种语言和文字。 这些传统将人们的文化从一代传递到下一代,穿越几个世纪。

在数字排版中,计算机放置所有的单词,而不是人类。 通常,作为 Web 设计师或开发人员,你正在创建一个模板,以填充不同版本的内容。 在 Web 上没有“手动调整”排版,尤其是在布局是流动的,重新流动以适应不同形状和大小的屏幕时。 那么,我们现在可以做些什么来更好地表达传统排版的质量期望,同时仍然依赖于当今计算机带来的机械化?

一种解决方案是 text-wrap: pretty。 它旨在通过利用基于段落的算法来解决长期存在的问题,从而为 Web 上的类型带来新的润色水平。

Better typography

某些排版传统教会你做几件事:

  1. 避免短的最后一行。 你想避免在段落末尾留下单个单词。 这看起来可能很奇怪,并使段落之间的空间看起来错误地很大。
  2. 避免“bad rag”。 你可以查看文本的参差不齐的边缘(inline-end side),并注意行的总体长度是否有点一致,或者 rag 是否非常锯齿状。 在手工设置时,排版员会移动单词以最小化相邻行之间的视觉差异 —— 以避免 bag rag。 “Good rag” 提高了文本的可读性,并使整个文本块看起来更令人愉悦。
  3. 避免不良的连字符。 对于可以连字符的语言,连字符有助于创建良好的 rag。 它还将一个单词分成几部分,并将这些部分尽可能远地放置在inline dimension中。 这增加了阅读时的认知负荷。 最好尽量减少连字符的使用,并避免连续两行使用连字符。
  4. 避免 typographic rivers 如果你知道要寻找rivers,你可能会开始注意到,有时单词之间的空格在各行之间随机对齐,从而在段落中创建空白路径。 Rivers 可能会分散注意力。

你可以在以下示例中看到这四个问题。 左右两侧的文本相同。

A screenshot of paragraph text that demonstrates a short last line, bad rag, bad hyphenation, and a typographic river.

设计师和排版员经常使用连字符和/或对齐来帮助改善rag,但在 Web 上,两者都不能提供令人满意的结果。 直到最近,几乎不可能对 Web 上的短行、不良的rag或rivers做任何事情。 我们只是忍受了它们。

Web line layout since 1991

30 多年来,Web 只有一种技术来确定在哪里换行文本。

浏览器从文本的第一行开始,然后一个接一个地布局每个单词或音节,直到它没有空间为止。 一旦没有更多空间来容纳另一个单词/音节,它就会换到下一行(如果允许 wrapping)。 然后它从下一行开始,尽可能多地适应所有内容……然后当它没有空间时,它会换行……并开始处理下一行。

它始终只考虑一行。 它会在需要时随时换行,在上一行容纳了最大数量的内容之后。 如果启用了连字符,它将用连字符连接该行中的最后一个单词,在任何时候都尽可能多地将该单词保留在上一行。 没有考虑其他任何因素 —— 这就是 Web 上的文本具有不良的 rag、rivers、短的最后一行和毫无意义的连字符的原因。

这并不是因为文本是由计算机布局的而要求的。 几十年来,Adobe InDesign 和 LaTeX 等软件在决定在哪里结束一行并开始下一行时,已经评估了多行文本。 只是 Web 没有使用多行算法。 直到现在。

我们很高兴首次将此功能引入 Web,在 Safari Technology Preview 216 中。

text-wrap: pretty

现在,Web 能够评估整个文本段落,以确定最佳换行位置。 你可以使用 text-wrap: pretty 要求浏览器执行此操作。 WebKit 不是第一个实现它的浏览器引擎,但我们是第一个使用它来评估和调整整个段落的浏览器。 我们也是第一个使用它来改善 rag 的浏览器。 我们选择在我们的实现中采取更全面的方法,因为我们希望你可以使用此 CSS 使你的文本更易于阅读,更柔和,为你的用户提供更好的可读性和可访问性。 并且,只是为了创造一些美丽的东西。

Safari Technology Preview 216 可以防止短行,改善 rag,并减少连字符的需要 —— 跨所有文本,无论它有多长。 我们尚未进行调整以防止rivers,尽管我们将来很乐意这样做。

虽然 support for pretty 在 2023 年秋季的 Chrome 117、Edge 177 和 Opera 103 以及 2024 年的 Samsung Internet 24 中发布,但 Chromium 版本的实现的功能较为有限。 根据 Chrome 团队的 an article,Chromium 只对段落的最后四行进行调整。 它专注于防止短的最后一行。 如果段落末尾出现连续的连字符行,它也会调整连字符。

CSS 工作组设计的 pretty 的目的是,每个浏览器尽其所能 来改善文本的换行方式。CSS Text Level 4 specification 当前定义如下(其中“user agent”表示 Web 浏览器,强调):

user agent 可能会尝试避免过短的最后一行……但它也应该以其他方式改善布局。 改进的确切集合取决于 user agent,并且可能包括 诸如:减少行之间长度的变化; 避免 typographic rivers; 优先考虑不同类别的 soft wrap opportunities, hyphenation opportunities, 或 justification opportunities; 避免在太多连续的行上使用连字符。 “may”的使用在这里非常有意义。 这是一个明确的声明,即每个浏览器都可以自行决定 pretty 应该做什么。 没有要求每个浏览器做出相同的选择。 事实上,一个浏览器团队可能会在 2025 年决定处理改进这些质量的某些方面,然后在将来更改其实现的执行方式。

由于 Chrome 对 pretty 的实现方式已经被教授,因此许多 Web 开发人员认为该值只应防止短的最后一行。 但这从来不是意图。 事实上,CSS 工作组为此目的定义了一个不同的值。 上周 just renamedtext-wrap: avoid-short-last-lines

Take a look

你今天可以在 Safari Technology Preview 216 中尝试 text-wrap: pretty。 查看 this demo,你可以在其中打开和关闭 pretty 以查看其效果。 你还可以打开或关闭连字符或对齐方式,以尝试任何组合。 “Show guides” 和 “show ghosts” 将帮助你了解正在发生的事情。 或者尝试 text-wrap: balance 以查看差异。 该演示具有英语、阿拉伯语、德语、中文和日语的内容,因此你可以看到在不同书写系统中的效果。

Screenshot of the demo showing a control panel of options that include: text-wrap: pretty, hyphenate, justify, show guides, show ghosts, text-wrap: balance. With a bunch of text on the page which will change as different options are applied. Try this demo in Safari Technology Preview 216.

这是英语文本的示例,没有应用 text-wrap。 这是 Web 多年来一直使用的默认换行算法。 我已打开 “show guides” 以标记文本框的边缘。 绿线标记文本框的inline-end边缘 —— 行布局算法旨在使每行到达的位置。 当文本到达此绿线时,浏览器会换行。

Three paragraphs of text, with a green vertical line marking the inline end edge of the text box.

这是应用 text-wrap: pretty 后相同文本的外观。 绿线已移动。 现在,浏览器改为旨在比文本框的最大限制更早地换行每行。 它在范围内换行,一定在紫红色线之后,一定在红线之前。 这改善了 rag。

Demo of the same text, only now with better rag. The "guides" show three vertical lines about 50 pixels apart — the far right edge is marked with a red line. 45 pixels in from the left is a vertical green line. And another 45 pixels left of that line is a vertical magenta line.

你还可以 “show ghosts” 以查看未经修饰的版本作为背景中的 ghost。

The same text, only now the well wrapped version is in normal color text, with the previous, badly wrapped version directly behind the good version, in a faint cyan color. It's a ghost of the before version, making it clear which lines have changed and how.

你还可以打开和关闭连字符和对齐方式以比较不同的组合。 调整浏览器窗口的大小,并查看在不同文本宽度下会发生什么。

你可能会注意到,由于 Safari Technology Preview 将 pretty 应用于文本的每一行,而不仅仅是最后四行,因此它对文本的影响更大。 文本块变得更像一个坚实的矩形。

你确实必须使用 text-wrap: pretty 来排版正文文本,才能看到它带来的多大差异。 它是微妙的,但非常显着的。 将此与 with paragraph margins of 1lh 结合使用,文本开始看起来非常棒。

那么,为什么每个浏览器不尽其所能使文本看起来更好? 因为性能。

Performance

许多开发人员理所当然地关心 text-wrap: pretty 的性能。 虽然更好的排版在视觉上令人愉悦 —— 但它不能以较慢的页面为代价。 每个浏览器工程团队都必须考虑其用户拥有的硬件和芯片,才能决定如何设置限制。

我们对我们所做的工作感到非常兴奋,以确保 Safari 用户不会遇到负面的性能影响,即使 Web 开发人员将 text-wrap: pretty 应用于 Web 页面上的大量内容。

作为开发人员,要知道的一件事是,text-wrap 的性能不受页面上应用了多少元素的影响。 当 pretty 算法在计算要执行的操作时考虑越来越多的行时,就会出现性能问题。 在基于 WebKit 的浏览器或应用程序中,你的文本元素需要数百或数千行才能看到性能下降 —— 并且这种类型的内容在 Web 上是不常见的。 如果你的内容被分解为典型长度的段落,那么你没有理由担心。 尽可能多地使用 text-wrap: pretty,并依靠我们的浏览器工程师来确保你的用户不会遇到任何缺点。

我们可能会决定添加一种机制,将真正长的段落分解为更合理的块,其中 WebKit 单独评估每个块。 如果我们这样做,那么即使是 1,000 行的段落也不会影响性能。 这是 Adobe InDesign 采用的方法。 它可以改善所有文本行的布局,但它不会一次性评估每个段落中的无限行。 WebKit 团队也可能发现其他方法来平衡美观和速度,确保 pretty 尽可能多地改善你的所有文本,而不会影响用户对我们速度极快的浏览器的体验。

立即在 Safari Technology Preview 216 中测试 text-wrap: pretty,让我们知道你是否可以触发性能影响。 在 bugs.webkit.org 上提交一个问题,以便我们可以在 Safari 本身发布此功能之前考虑此类反馈。

When to use pretty vs balance?

显然,text-wrap: pretty 旨在使正文文本更美观地排版。 但这是它的唯一用例吗? 那么 text-wrap: balance 呢? 我们应该何时使用 prettybalance

有些人会给你一个过于简单的答案,例如 “pretty 用于段落,balance 用于标题” —— 但这可能是错误的建议。 让我们看一下 balancepretty 的对比,以及如何决定在标题、标题、预告片和其他类型的较短的换行文本上使用哪个。

text-wrap: balance

基本上,text-wrap: balance 规则告诉浏览器以这样的方式换行,以使每行都与其他的长度大致相同。 我认为这就像将一张纸折叠成两半、三分之一或四分之一。

例如,这是一个默认值为 text-wrap: auto 的标题。 你可以看到单词 “enough” 最终成为第二行的第一个单词,仅仅是因为在 “with” 之后,第一行没有足够的空间容纳它。 每行都是逐一布局的,没有考虑其他行。 这导致此标题最终在最后一行上只有一个单词 “itself”。

这是应用了 text-wrap: balance 的类似标题。 最后一行上不再有单个单词。 太棒了! 但这还不是全部。 最后一行现在的长度与其他两行大致相同。 第一行明显缩短,因此其长度与其他的 “平衡”。 所有三行基本上都具有相同的长度。

你会注意到,因此,标题的视觉印记 / 页面上的 “ink” 块现在明显比其容器中可用的整体空间(此处用青色线标记)窄。

A balanced headline, where all three lines are about the same length. All of them are about two-thirds as wide as the box they are in, leaving a lot of white space on the right. The first line is actually the shortest line.

这对你的设计来说可能非常棒。 你可以将 balance 应用于标题、标题、预告片、任何较短的副本类型,它将具有相同的效果。 它将堆叠文本行,使它们都具有大致相同的长度 —— 它们是平衡的。 并且一旦文本被换行,它可能不再填充框。 它将比可用空间窄。

有时,这不是理想的效果。 也许你的标题位于预告片图像下方,并且设计要求文本与图像的宽度相同。 你会注意到,在此示例中,第一行最终比其余的短。 平衡文本可能会付出太高的代价。 也许你只想避免短的最后一行。

你可以在这样的标题上改用 text-wrap: pretty。 这将避免短的最后一行,同时仍然在inline direction上填充容器。

A similar headline, with three words on the last line. The first line does stretch all the way across the box, and the second line is a bit shorter so that some of its words can fill in the last line.

你可以在 Safari Technology Preview 126+ 和 Chrome/Edge 130+ 中 try out these examples yourself,以更深入地了解 text-wrap 对长、中和短标题的影响。 拖动框的角以查看它们如何以不同的方式处理换行。

text-wrap: balance 的性能注意事项是什么? 同样,CSS Web 标准将其留给浏览器引擎来决定应实施哪种限制,以确保用户体验不会受到负面影响。 浏览器不必做出彼此相同的选择。

Chromium 的实现将平衡的行数限制为四行,以确保 Chrome、Edge 和其他 Chromium 浏览器仍然很快。 WebKit 的实现不需要限制行数。 每行都将与其他所有行平衡。

因此,如果 “pretty 用于正文文本,balance 用于标题” 过于简单,无法成为好的建议,那么思考这种选择的更好方式可能是什么?

我是这样想的:

不相信 text-wrap: balance 通常在长的文本段落上没有意义? 好吧,你可以在 this same demo 中尝试一下。

The same demo, now with text-wrap: balance applied to the paragraphs. Each paragraph is now wildly different widths from the others. This is not useful for anything.

请参阅如何调整每个段落的整体宽度,以便所有文本行都具有大致相同的长度,而无需考虑它们与容器相比的整体宽度? 这就是平衡所做的事情。 在上面的示例中,这意味着每个段落的宽度与其他段落的宽度都截然不同。 这很奇怪。 仅当你想要该效果时才使用它。 否则,你可能要使用另一个选项。

text-wrap 的其他值是什么? 让我们来浏览一下。

text-wrap: avoid-short-last-lines

avoid-short-last-lines 值是 CSS Text Module Level 4 规范中的最新值。 它尚未在任何浏览器中实现。 它将专注于避免短的最后一行,让 pretty 做更多的事情。

text-wrap: auto

text-wrap 的默认值目前执行 Web 自 1991 年以来所做的事情,其中文本的每一行都是单独布局的,而无需考虑多行。(这通常称为 “first-fit” 或 “greedy” 算法。)

但是,这在将来可能会发生变化! 将来有一天,浏览器可能会决定更改默认设置,并将某种多行行布局算法应用于 Web 上的所有现有内容。 这将改善所有内容,甚至是旧网站,甚至是开发人员不知道 text-wrap: pretty 的网站。

text-wrap: stable

如果你尝试过 text-wrap: stable,你可能会认为 “它什么也不做! 这是什么?” 基本上,现在,stable 执行与 auto 相同的事情。 它使用原始的 first-fit 换行算法,其中每一行都被布局为完全用内容填充该行,并且仅在必要时换行。

当内容本身可编辑时,这是换行算法的特别好的选择。 如果你的用户正在编写文本,你不希望单词/音节跳来跳去,更改他们在键入时的换行。 为了确保你的内容不会因后续行上的编辑而移动,或者在任何你想要 OG 行换行的情况下,请应用 text-wrap: stable

如果你以这样的方式对文本进行动画处理,使其不断重新换行,这也是一个不错的选择。 它将确保始终使用最快的换行算法 —— 如果要快速连续地一遍又一遍地进行计算,这一点很重要。

通过显式选择 text-wrap: stable,你可以确保即使浏览器重新定义 auto 的功能,此内容也将继续使用原始算法换行。

stable 值已经 well supported

text-wrap-mode and text-wrap-style

text-wrap 属性实际上是两个 longhands 的简写形式。 text-wrap-style 属性用于选择使用的换行算法,而 text-wrap-mode 允许你打开和关闭换行。

text-wrap-style: auto | stable | balance | pretty | avoid-short-last-lines
text-wrap-mode: wrap | nowrap 

通过同时具有 text-wrap-modetext-wrap-style 属性,你可以灵活地独立于内容是否换行来更改换行样式,并让这些选择独立级联。

这意味着你还可以使用简写形式来简单地使用 text-wrap: nowrap 关闭换行。 或者,使用 text-wrap: wrap 重新打开换行。 在 this demo 中测试它的工作方式。

Support for text-wrap-modetext-wrap-style longhands,以及 nowrapwrap 值,在 2024 年 10 月成为 “Baseline Newly Available”(也就是,在所有主要浏览器中都可用),当时 Chromium 在 Chrome/Edge 130 中添加了支持。为了确保对使用旧浏览器的用户的换行提供完整支持,你始终可以提供对旧版 white-space: nowrap | normal 的回退。(white-space)。(虽然当你这样做时,也要注意检查你的空白折叠行为,因为它会受到 white-space 的影响。)

What do you think?

立即在 Safari Technology Preview 216 中试用 text-wrap: pretty。 我们很乐意听取你的想法。 在 BlueskyMastodon 上找到我,Jen Simmons,分享你的反馈。 如果你发现错误或问题,请提交 WebKit bug report