这篇文章是在我和 Emilio Cobos(Mozilla 的资深开发人员,也是 CSSWG 成员)谈论 最近的 CSSWG 会议 时产生的。我想知道他认为他们上次会议上讨论的最激动人心的主题是什么,而 2024 年充满了许多新的或即将出现的炫酷事物,例如砌体布局、if()
条件语句、锚点定位、视图过渡等等,我认为他的答案一定包含在其中。
他承认,我列出的亮点确实反映了社区的主流,尤其是从作者的角度来看。然而,令我惊讶的是,他最喜欢的讨论是一个完全不同的东西:letter-spacing
属性在不同浏览器上的渲染方式存在错误。 这是一个在网络上根深蒂固的缺陷,浏览器多年来一直忽视 CSS 规范,由于缺乏更好的选择和兼容性问题,这个问题无法轻易解决。
Emilios 的答案是有道理的——他在 Gecko 上工作,渲染字体本身就是一门艺术。尽管如此,我仍然不明白问题究竟在哪里,为什么他觉得它如此有趣,甚至为什么它会存在,因为 letter-spacing
就像 CSS 一样古老。直到我深入研究 letter-spacing
的兔子洞时,我才了解到这个问题有多么复杂,我希望您也能像我一样对这个(并不那么)简单的属性感兴趣。
什么是字间距?
这个问题似乎很简单:字间距就是字母之间的间距。太棒了!这很容易,对人类来说。对于计算机来说,如何渲染字母之间的间距这个问题要复杂得多。人类只需在不加思索的情况下写下下一个字母。另一方面,计算机需要一种策略来渲染这个间距:它们应该在字母的开头添加整个间距,在结尾添加,还是将间距减半并在字母的两边添加?它是否应该与英语等从左到右 (LTR) 语言以及希伯来语等从右到左 (RTL) 语言不同?这些问题至关重要,因为选择其中一个作为标准会影响文本测量和换行在整个网络上的工作方式。
网络上使用了哪三种策略中的哪一种?取决于你问谁。CSS 规范中的实现与浏览器的实现完全不同,甚至浏览器渲染引擎之间也存在不兼容,例如 Gecko(Firefox)、Blink(Chrome、Brave、Opera 等)和 WebKit(Safari)。
CSS 规范怎么说
让我们稍微回溯一下,首先了解 规范 如何说明字间距应该如何工作。在撰写本文时,letter-spacing
指定印刷字符单位之间的额外间距。值可以为负数,但可能存在实现相关的限制。
正式规范中还有更多内容,但这足以让我们理解 CSS 规范希望 letter-spacing
如何表现。关键词是之间,这意味着字间距只应该影响字符之间的间距。我知道,听起来很明显。
因此,如规范给出的示例所示,以下 HTML
<p>a<span>bb</span>c</p>
…以及以下 CSS
p {
letter-spacing: 1em;
}
span {
letter-spacing: 2em;
}
…应该在两个“b”字母之间产生相等的间距
![Letter spacing on paper. The letter spacing is only applied between the letters "b"s](https://i0.wp.com/css-tricks.com/wp-content/uploads/2024/07/how-letter-spacing-works-on-paper.png?resize=1920%2C1080&ssl=1)
但是,如果我们在任何浏览器(例如 Chrome、Firefox 或 Safari)上运行相同的代码,我们会发现间距并不局限于“b”字母之间,而是在整个单词的末尾也有间距。
![Letter spacing on browsers. The letter spacing is applied between the letters "b"s and on the right-hand side of the last letter "b"](https://i0.wp.com/css-tricks.com/wp-content/uploads/2024/07/how-letter-spacing-works-on-browsers.png?resize=1920%2C1080&ssl=1)
浏览器怎么做
我以为 letter-spacing
在字符末尾添加间距是正常的,并且不知道规范中说的是相反的。但是,如果你仔细想想,目前的做法确实有点不对劲……只是我们已经习惯了而已。
为什么浏览器在这方面不遵循规范呢?
正如我们之前所见,字间距对于计算机来说并不简单,因为它们必须遵循一种策略来确定间距的应用位置。在浏览器的情况下,标准一直是在每个字符的末尾添加一个单独的间距,而不考虑该间距是否超过整个单词。这可能不是最好的选择,但网络已经习惯了这种方式,现在改变它会导致网络上所有文本和布局发生各种变化。
这会在具有更大字间距的元素末尾留下一个间距,对于 LTR 文本来说是可以接受的,但它会在 RTL 书写模式下文本的开头留下一个空白。
这个问题在居中对齐的文本中更为明显,其中结尾的间距会将文本从元素的绝对中心推开。您可能至少一次遇到过需要在元素的另一侧添加padding
来弥补对文本应用的任何letter-spacing
,例如在按钮上。
正如您所见,蓝色突出显示创建一个对称的锥体,而我们的文本却没有遵循。
更糟糕的是,“每个字符的末尾”对于不同的浏览器来说意味着不同的东西,尤其是在 RTL 书写模式下。Chrome 和 Safari(Blink/WebKit)表示字符的末尾始终在右侧。另一方面,Firefox(Gecko)在“阅读”末尾添加空格——在希伯来语和阿拉伯语中是左侧。亲自看看区别
![Side-by-side comparison of letter spacing on Gecko and Blink/Webkit](https://i0.wp.com/css-tricks.com/wp-content/uploads/2024/07/how-letter-spacing-renders-on-different-browsers.png?resize=1920%2C1080&ssl=1)
可以修复吗?
第一个想到的想法是简单地遵循规范中的说明,修剪掉结尾字符处不需要的空格,但这种(反)解决方案会导致兼容性风险,这些风险实在是太大了,以至于根本不值得考虑;文本测量和换行会发生变化,可能会导致许多网站出现故障。 使用解决方法删除了额外空格的页面 可能是通过抵消元素的padding
/margin
来实现的,这意味着改变目前的行为会使这些抵消变得无效或导致故障。
针对 letter-spacing
,有两个真正的解决方案:重新设计空格在字符周围的分布方式,或者允许开发者选择我们想要将结尾空格放置在何处。
选项 1:重新设计空格分布
第一个选项是更改当前的 letter-spacing
定义,使其类似于以下内容
指定应用于每个印刷字符单位的额外间距,但不包括那些前进值为零的单位。额外间距在印刷字符单位的内联开始侧和结束侧之间平均分配。值可以为负数,但可能存在实现相关的限制。
简而言之,浏览器不再在字符末尾添加额外间距,而是将其平均分配到开头和结尾,从而产生对称的文本。这也会改变文本测量和换行,尽管程度较小。
![Letter spacing with the symmetrical fix. The letter spacing is equally applied around the letters "b"s](https://i0.wp.com/css-tricks.com/wp-content/uploads/2024/07/how-letter-spacing-would-works-with-symmetrical-fix.png?resize=1920%2C1080&ssl=1)
现在居中对齐的文本正确地对齐到中心
![Different examples of letter spacing being distributed between letters and achieving a symmetrical look](https://i0.wp.com/css-tricks.com/wp-content/uploads/2024/07/letter-spacing-symetrical-fix.png?resize=1920%2C1080&ssl=1)
选项 2:允许开发者选择
即使偏移量减半,它仍然会导致布局发生变化,这对于一些人来说仍然是(合理的)不可接受的。这是一个难题:大多数页面需要,或者至少会从保持 letter-spacing
原样中受益,而新页面会享受对称的字间距。幸运的是,我们可以通过让开发者选择空格如何应用于字符来做到这两点。语法还待商榷,但我们可以有一个新属性来选择空格放置的位置
letter-spacing-justify: [ before | after | left | right | between | around];
每个值都代表应该添加空格的位置,并考虑到文本方向
before
:空格在字母的开头添加,遵循语言的方向。after
:空格在字母的结尾添加,遵循语言的方向。left
:空格在字母的左侧添加,忽略语言的方向。right
:空格在字母的右侧添加,忽略语言的方向。between
:空格添加到字符之间,遵循规范。around
: 字母周围的间距。
从逻辑上讲,当前的行为应该是默认不破坏任何东西,而letter-spacing
将成为这两种属性(长度和放置)的简写。
letter-spacing: 1px before;
letter-spacing: 1px right;
letter-spacing: 1px around;
letter-spacing: 1px;
/* same as: */
letter-spacing: 1px before;
还有第三种选择吗?
当然,第三种选择是保持现状。我想说这不太可能,因为CSSWG 决定对这个问题采取行动,如果我不得不把口袋里的五分硬币押注的话,他们可能会选择第二个选项。
现在你知道了letter-spacing
是坏的……我们不得不忍受它,至少现在是这样。但有一些选择可能会帮助在未来纠正这个问题。
等于什么?
您好!根据规范,它等于
letter-spacing
属性中定义的空间,仅此而已。“b”之间的间距也应该相等。您说,修复这个将不是理想的,因为…
简而言之,您的两个主要观点是,这种修复会
影响“许多网站”
风险很大
我不确定“很多”是什么意思。我们需要找出有多少网站正在使用 letter-spacing,特别是以修复它实际上会破坏网站的方式使用。
为此,我们需要考虑“破坏网站”意味着什么。因为我们说的是一些不应该(真正)影响功能的东西,而仅仅是外观。
然后我们可以讨论您所说的风险很大是什么意思。
风险到底是什么?
最坏的情况是,一些 UI 可能会真的崩溃。标题可能会溢出下面的内容,覆盖重要的链接或按钮。
我的猜测是,与更无害的可读性问题相比,这种情况很少见,例如单词或段落比预期更靠近彼此。
我理解这不是理想的,但也不理想的是要学习另一种不一致且有缺陷的行为,以及另一种 CSS 属性来解决它。
我喜欢 Web 平台想要保持向后兼容性的想法,但我们已经到了许多网站已经崩溃,很多东西没有按照预期渲染,过去发生过更严重的崩溃和变化。
您好!感谢您的评论。
我无法告诉您确切的数字,说明如果“修复”
letter-spacing
,有多少网站可能会崩溃,或者至少看起来更糟糕。如果我们想实施“修剪”或“周围空间”解决方案,那么这应该进行调查。然而,正如这个CSSWG 小组问题 中提到的:一位 Blink 开发人员表示,“对于 Blink 来说,和以前一样;崩溃程度是我们无法接受的”。在 Gecko 方面,崩溃程度超出了预期。因此,还没有明确的结论。
如果我们没有得到任何明确的结论或“了解风险”,那么更改浏览器处理
letter-spacing
的方式将是鲁莽的。特别是因为letter-spacing
在网络上超过 60% 的地方使用。最重要的是,我理解需要更好地汇总关于这个问题的数据。
感谢您的阅读!
周围的空间将是最好的,因为它适用于 LTR 和 RTL。如果你是 - 之后还是之前,那么你需要根据两种布局来调整末尾字符的间距,这将很复杂。
此外,还应考虑负值 (-1em) 和更大的字母间距 (10em)。您不希望在屏幕边缘和字母之间出现 10em 的间隙。
当前的字母间距行为在阿拉伯语中存在问题;它会切断字母之间的联系,添加空空格,而不是扩展它们。这些联系是书写单词的组成部分,不应该被切断。这是一个示例,说明它应该是什么样子!
اهـلـاً عـا لـم !
我还认为,字母间距可以从乘数单位中获益良多。而不是添加固定空间,而是将其乘以当前空间。这会保持固有的字母间距比率。
非常感谢您的见解!我认为像 InDesign 或 Illustrator 这样的专业布局应用程序只在字母之间添加空格。我不太理解为什么浏览器表现得不同,或者在我看来,作为一个布局,它只是一个实现错误或对排版的误解。CSS 属性在错误的位置进行校正。
专业字体也具有字距表,这些字距表始终用于专业布局应用程序中的排版。Adobe 应用程序还会将这些针对不同字母对的值计入字母间距。
增加字母间距的最重要的用例是深色背景上的浅色文本,因为字体是为正向排版设计的,大写排版(字体是为混合文本转换而设计的)以及从远处阅读的文本。
减少字母间距更多地用于艺术目的,但为了可读性。
只是想添加我的两分钱 ;-)