前端开发者学堂 - fedev.cn

CSS的leading-trim给排版带来的变化

发布于 大漠

今天要和大家聊的leading-trim既是一个简单的属性,但又是一个复杂的体系。为什么这么说呢?如果我们从CSS属性的角度出发,那他是一个简单的属性,我们只需要知道他有哪几个值,每个值的作用是什么,这样就有实现需要的效果。如果我们从排版的角度出发,那他又是一个复杂的体系,因为要彻底的掌握他们,我们需要掌握的东西太多,有设计方面的,有文字排版方面的,也有CSS的其他属性等。但是,leading-trim的确能帮助我们Web开发者解决一些排版上的烦恼,特别是行与行间距方面的烦恼。如果你对这方面感兴趣的话,欢迎继续往下阅读。

背景:间距给我们带来的烦恼

不知道你们是不是也跟我一样碰到这样的排版烦恼,特别是在UI还原的过程中,文本框之间或者说文本框和其他对象之间的间距总是和最终需要的诉求有所偏差。在视觉走查过程中,CSS实现的效果和设计师想要的效果总是有一定的偏差,即 有额外的空间存在

就拿标题和下面的矩形框为例:

<div class="container">
    <h2>初一赛事报告</h2>
    <div class="box"></div>
</div>

实际效果,标题<h2>div.box之间的间距和设计稿还是有一定差异:

就上面示例而言,<h2>的行高是line-height: 1.15,如果你将其line-height设置为1,可以看到他们之间的差异:

换句话说,当使用边界框来度量空间时,它最终会比我们预期的要大。而且line-height越大,这样的问题越大。如果上面的示例还不够明显的话,我们来看下面这个例子,设计是通过测量边框之间的空间来创建的。当所有的间距被设置为32px时(在第一张卡片中),所有的垂直间距实际上比32px大得多(正如在第二张卡片中所示),即使你将它们全部设置为32px

这是一个跨越工具和平台的常见问题。这也是众多Web前端开发者在还原UI时最易于碰到的问题,这也是在视觉走查过程中最易于和视觉设计师有冲突的问题。而且这个现象对于Web开发者都在寻找易于解决的方案,直到去年6月份,CSS的规范(CSS Inline Layout Module Level 3)中提出了leading-trim属性,它可以帮助我们解决上述提到的问题。

造成这种现象是有一定的历史原因

在Web排版上来说,这是一直存在的一种现象,造成这种现象是有一定原因的。如果我们要说清楚造成这种现象真正的原因,那就是需要从字体设计开始说起。

在铅字由金属制成的年代,事情要简单得多。那个时候只有两个角色:类型设计师(Type Designer)和 排字工人(Typesetter)。他们的工作受到物理宇宙规则的限制。

到了19世纪末期,制造行业已经掌握了大部分的基本技术,这也起到了推波助澜的作用。字体的生命从纸上开始,字体设计师将花费数周或数月的时间来勾勒出所有必要的字体形式。完成之后,字体的图纸就变成了一种字体:

实际上就是铅块

你需要为每个字母购买一个或多个这样的字母块。你还需要炎不同大小的文本购买额外的金属块。但是字体大小并不是用字母的大小来定义的,而是用金属块的高度来表示,用一个叫做点的单位来表示(每个点是1/72英寸,也就是0.4毫米)。块的高度是已知的,但在其中,类型设计师可以做任何他们想做的事情:相同大小的字体可以更大或更小,它们的基线(baseline),即每个字母所在的行,可以更高或更低,等等。

当字体完成和字体制作好后,雇佣设计师的铸造厂将把它们卖给印刷厂。反过来,他们会雇佣排字工,他们的工作是把这些金属块排列成单词,然后是名子,然后是段落,然后是页面。

排字工人可以把一行行的铅块一个接一个地排上去,这个过程叫做整型,因为它会产生整块的、同有间隔的铅块。不过,通常他们会在文本中插入额外的窄金属片,以留出空间,让文本更流畅,让读者的眼睛更容易从一行跳到另一行。

由于间隔条是用铅做的,添加空格的做法被称为“前置”,即Leading。下面是一个16pt类型和4pt前置的示例,它为我们提供了合并行高为20pt的文本。

找到合适的铅是它自己的艺术。它需要根据字体大小和行长度有所不同。即使在处理相同的字体大小和相同数量的前置时,也会使一种字体看起来很局促,而另一种字体则是浮动的。

这是一个相对简单的系统,它有着明确的角色和规则。字体来自铸造厂,作为一个整体,不可改变的块,行高只能增加,不能删除。是的,你也可以用一个不寻常的行高内建的字体,但那是不寻常的。你的工作就是把东西放好,让它们按你的意愿摆放好。

但是进入到电脑世界中,这些铅字印刷排版的技术就毁了。

字体不再是铅块;相反,它们以打包成文件的数字集合的形式出现。Type Designer或Type Foundry也必须准备不同文件格式的字体,以适应早期图形平台的要求,比如Windows、MMacintosh和现在已被遗忘的OS/2。

当然,并不是每件事都是那么可怕的。计算机给了打字设计师和排字工人前所未有的自由。像素几乎不受金属(铅字印刷)的限制,事物可以随意重叠,或者从它们曾经僵硬的盒子中伸出来。作为一个排字工人,你可以添加任意多的前置(“leading”),而不需要任何实际的前置(“leading”)。或者,你可以删除所有前置。

在物理世界中,一个盒子需要有最小点的实际维度,因为前置只能被添加。但在数字类型中,字体的默认行高可以设置为一个完全任意的数字,而且它通常会比字体大小更高,读起来更舒服。比如下面这四种字体,字体大小都是16pt和行高为100%时的默认效果:

话又说回来,事物是会随着时间和科学技术不断向前演进的。排版的技术也是如此。随着电脑屏幕的普及,更多的工作是针对屏幕本身设计的,因此产生了不同的需求。特别是在用户界面设计中,在图标或头像旁边小心地将文字垂直居中变得更加重要,而这个问题在印刷的世界中并不那么重要。

与此同时,一些古老的技术就会慢慢的消失。字体和线高开始更多地用像素表示,而不是点。随着铅(铅印)的消失,“铅”一词慢慢被抽象的术语“行间距”所取代。

所有竞争的字体标准也融合在一起。业界发明了 OpenType ,一种可以在任何地方使用的统一字体。这有点像一种错觉,在字体文件中,仍然有三组不同的值,不同的平台和程序只会选其中一组。

随着1989年,Web的诞生,这个挑战也随之加剧。甚至说这种痛苦一直延续到现在。

人们把早期的Web构建块放在一起,做了两个决定,改变了 行高 的性质。首先,他们在每条线上和线上都分配了额外的空间,这些空间曾经是一条铅条。他们给新体制起了一个新名字,叫做“半前置”(Half-leading)。

而CSS中又引入了另一个变化。行高的100%被重新定义为字体大小的100%。以前,字体设计人员可能给16px的字体默认行高为20px。但在Web上,无论最初的设计者是怎么规定的,100%16px字体的行高都恰好是16px

原因很简单,知道字体的默认行高需要加载该字体,这在早期的互联网上可能会很慢。另一方面,将行高乘以字体大小可以立即完成。CSS的创造者之一@Hakon Wium Lie提到过:

我们想在不加载字体的情况下做尽可能多的计算

行高不再能理解里面的字体。幸运的是,字体不需要符合物理框的大涉,所以这不是一个大问题。

因此,同样的字体将有一个16px的大小,但行高20px现在将表示为125%1.25,因为16×1.25正好是20

另外,Web还剥夺了排字工人的一些控制。在印刷时代绝对的规则,现在变成了建议。将文本框精确地定位到你想要的位置不仅变得更加困难,而且常常会被阻止。毕竟,Web浏览器可以在不同的电脑上找到,每台电脑都有不同的屏幕和安装的字体。

浏览器,就像之前的平台一样,现在必须承担一些类型渲染和排版的责任。但是可以想象,它们也有自己的特性和相应存在的缺陷。无论是对齐方式,还是像素四舍五入的计算,甚至是CSS不同属性的渲染结果等都有稍微的不同。

这也就是造成我们文章开头提到的现象。简单地说,Web上各个元素之间的间距,特别是垂直间距,一直是Web设计人员(相当于铅印时代的“类型设计师”)和开发人员(相当于铅印时代的“排字工人”)经常争论的问题。设计者坚持认为他们在浏览器中看到布局和他们最初设计的布局完全不一样。开发人员回答说,样式表中的所有间距和设计稿中的间距是完全匹配的。那么谁对的呢?棘手的是:在某种程序上,他们都是对的。

排版术语的概述

从上一节的内容中,我们或多或少知道从铅印术到Web的排版之间变化以及差异。但对于众多Web开发者来说,他可以对这些并不感兴趣,甚至不知道这个历史的发展过程。而他们更关心的是为什么会有这样的现象?也就是说,我们抛开其他的一切,仅仅从Web排版的角度来说,造成文章开头提到的现象,其最大的原因就是:“字体的行高引起了垂直间距的偏差”!

也就是说,了解了line-height这一切都变得好说,但是在Web排版中,或者说在CSS的世界中,CSS的line-height是一个非常复杂的技术体系,它涉及到的东西特别地多,比如说,字体如何工作就足够我们花很多时间去了解学习。

前置(“leading”)和line-height尽管相似,但有一些重要的区别。要理解这些差异,我们首选要对排版有更多的了解。

在传统的西方字体设计中,一行文字由以下几个部分组成:

  • 基线(Baseline):这是类型所在的假想线。当你在横线笔记本上写字时,基线就是你写字的那条线
  • 上降线(Descender):这条线正好位于基线之一,它是一些字符(比如小写的gjqyp)触及基线以下的一行
  • x-height:这是一行文本中标准小写x的高度。一般来说,这是其他小写字母的高度,尽管有些字符的某些部分可能会超过x高度。对于所有的意图和目的,它允当小写字母的感知高度
  • 帽高(Cap-height):这是在给定行的文本中大部分大写字母的高度
  • 上升线(Ascender):通常出现在帽高之上的一条线,其中一些字符如小写字母hb可能会超过正常的帽高

上面描述的文本的每个部分都是字体本身固有的。设计字体时要考虑到这些因素,然而,排版的某些部分是留给排版者而不是设计者的。其中一个就是前置(“leading”)。

前置定义为一组类型中两个基线之间的距离。

CSS开发人员可能会想,“好的,前置(leading)是行高,那我知道了”。但事实上,他们还是有很大差异的。

如果作为开发者要彻底掌握这些,还是需要对字体度量有深度的理解,而且字体度量很多东西只对设计师暴露,对Web开发者是不暴露的。从排版的术语中可你能已经感知到了,其中line-height是对Web开发者影响最大的。如果你想深入的了解这方面的内容,建议你阅读下面这些资料:

有了这些基本概念,我们开如来聊聊今天要聊的CSS规范中的leading-trim

leading-trim简介

leading-trim试图改变我们使用了24年的标准

leading-trim是CSS新引入的一个属性,其主要作用就是用来 改进文本布局 (也就是解决我们文章开头布局碰到的问题)。

leading-trim也称为“铅剪切”。在铅印刷术中,为了让铅字块(排成行)之间有一定的间距,排字工人会在行与行之间垫一些铅条,让阅读变得更舒服一些。在那个时代,锅条一般都放置在铅块下方。同样的,在Web排版上也有铅条的身影,只不过他分面上下两个部分。在CSS中引入该特性,它的工作原理就像一个文本框剪切刀。去掉文本框上下之间多余的空间(这个空间其实就是铅条高度的一半):

CSS中使用很简单:

h1 { 
    text-edge: cap alphabetic;
    leading-trim: both;
}

首先使用text-edge来告诉浏览器想要的文本边缘是帽高(Cap-height)和字母基线。然后用leading-trim从两边修剪。注意,边框只影响文本边框,它不会切断其中的文字。

这两行简单的CSS创建了一个包含文本的干净文本框。这有助于实现精确的间距(垂直方向方本块与其他元素之间的间距),并创建更好的视觉层次结构。

内联框边度量:text-edge

先来看text-edge属性,该属性的值主要有:

text-edge: leading | [ text | cap | ex | ideographic | ideographic-ink ] [ text | alphabetic | ideographic | ideographic-ink ]?

其中leading是其默认值,每个值具体含义如下:

  • leading:使用上升(Ascent)或下降(Descent)加上任何正的半前置(Half-leading)。为了调整行框的大小,忽略了外距(margin)、内距(padding)和边框(border
  • text:使用文本在基线之上、文本在基线之下
  • cap:使用帽高(Cap-height)基线
  • ex:使用x高度(x-height)基线
  • ideographic:使用ideographic-over基线
  • ideographic-ink:使用ideographic-ink-over或ideographic-ink-under基线
  • alphabetic:使用字母基线

注意,text-edge所有值都均为半置前导(Half-leading)。

除非text-edgeleading,否则行内框的marginpaddingborder也会影响到line-height的大小。

text-edge属性定义使用哪个度量作为内联框的布局边界的上边界和下边界的基础。leading-trim属性可用于将内容边匹配到这些相同的度量。text-edge接受两个值,其中第一个值指定边缘上的文本,第二个值指定边缘下的文本。如果只指定了一个值,则尽可能给两条边分配相同的关键字。

注意,leadingtext值依赖于字体的上升和下降以确保文本匹配。其他值更可能由于高于指定的度量值而导致重叠或溢出,因此使用这些值的作者需要小心地在行上提供足够的间距。

内联框对其行内框逻辑高度的贡献总是根据其自身的文本度量来计算,如下所述,并由text-edgeline-height控制。子框的大小和位置不会影响它的布局边界。

当计算出的line-height不是标准时,它的布局边界仅从其第一种可用字体度量(忽略其他字体的符号)派生出来,并使用前置(“leading”)来调整有效的AD,使其与所使用的line-height相加。我们可以使用下面的公式来计算主要的L

L = line-height - (A + D)

一半的前置(Half-leading)添加上面的第一个可用的字体,和下面的另一半D的第一个可用字体,使用一个有效的提升上面的基线,该基线可以按下面的公式计算:

 A′ = A + L / 2

一个有效的:

D′ = D + L / 2

但是,如果text-edge不是leading,这也不是根行内框,如果半前置(Half-leading)是正值,则将其视为0。布局边界准确地包含了这个有效的 A'D'

此外,text-edge不是leading时,布局边界会因每边的marginpaddingborder的总和而膨胀。

leading-trim控制半导前置(Half-leading)

为了确保在运行文本的基本情况下保持一致的间距,CSS行布局引入了在每行文本内容的上面和下面同时具有前置(Half-leading),换句话说,铅印术中的铅垫片一分为二,放在了文本的上面和下面。此外,上升(Ascent)和 下降(Descent)字体度量本身包括在最常见符号大小的上方和下方的额外空间,以便容纳上升或下降超出典型边界的偶发字符和变音符号。这可以防止后续的文本行相互重叠。然而,所有这些额外的间距都干扰了视觉对齐和有效(视觉上的)间距的控制。

CSS的leading-trim属性允许控制块的第一行和最后一行之上和之下的间距。它允许精确控制间距;此外,通过依赖字体度量而不是硬编码的长度,它允许内容调整大小、重新包装和以各种字体渲染,同时保持间距。

我们来看一个常见的问题,即垂直居中。将文本容器和图标在垂直方向居中对齐,从CSS的角度来看,实现这样的效果似乎并不复杂,但是因为拉丁文本的视觉边界是帽高(Cap-height)和字母基线(Baseline),而不是上升和下降,所以这通常不会产生预期的视觉效果。

测量到文本的顶部、底部可能会产生相同的结果,但是测量到视觉边界表明它在视觉上没有居中

为了在视觉上使文本居中,有必要假设帽高(Cap-height)和字母基线(Baseline)分别作为文本的上边缘和下边缘。

测量帽高度和字母基线,而不是上升和下降,并在视觉上平衡这些距离使文本处于中心位置

通过使用leading-trim来去除帽高以上和字母基线以下的间距,方框的中心实际上是文本的中心;并且无论使用什么字体来渲染它,都能可靠地做到这一点。

CSS的leading-trim可以接受的值主要有:

leading-trim: normal | start | end | both

其中normal是其默认值。具体每个值的含义是:

  • normal:当应用于块容器时,对第一行、最后一行框没有特殊处理。应用于内联框时,指定内容的上、下边缘与文本上、下基线一致,而不管text-edge如何
  • start:对于块容器,将第一个格式化行的块开始端修剪为根行内框对应的文本边缘度量。如果没有这样的行,或者中间有非零填充(padding)和边框(border),则没有效果。对于内联框,修剪框的块端的一侧,使其内容边与由文本边指定的度量相匹配
  • end:对于块容器,将最后格式化行的块端部分修剪为根行内框对应的文本边级度量。如果没有这样的行,或者中间有非零填充(padding)或边框(border),则没有效果。对于内联框,修剪框的块端的一侧,使其内容边与由文本边指定的度量相匹配
  • both:同时具有startend特性

在内联框上,指定是否修剪内容框以匹配其对应的text-edge指标。

在块容器上,指定是否将其内容的开始、结束部分的前导部分修剪为对应的text-edge指标,以便更好地匹配方框的内容边缘与其文本内容。

text-edge值是leading时,leading-trim属性不起作用。

leading-trim解决对齐问题

使用leading-trim可以用来修复对齐的问题。在Web排版中,容器中的文本垂直居中是一个自古以来难以解决的问题。

在默认行高(line-height)中保留的额外空间导致文本在文本框并没有真的垂直居中。使用leading-trim,可以将文本在按钮框中垂直居中。

另外每种字体的设计也不说相同。它有自己的默认行高,其中的文本可以有不同的大小和基线位置。因此,有时即使字体大小(font-size)、行高(line-height)和文本框位置保持不变,改变字体也会改变文本的对齐方式(特别是文本在一个容器中,始终有时候看上去偏上或偏下,没有垂直居中),就像下图这样:

在第二个示例中,你可以看到引入leading-trim是如何防止这种表况发生,并使文本保持原样。

小结

这篇文章虽然简单介绍了目前还未得到支持的两个CSS属性,即text-edgeleading-trim。不过这两个属性对于我们Web排版来说是非常有用的,特别是解决一些文本块与文本块(首行和末尾行之间的间距),文本块(首行与末尾行)和元素块之间的间距,甚至是容器中垂直对齐,图标和文本的垂直对齐都将非常有的有用。甚至可以说,解决Web开发人员一直以来存在的排版困惑。

可以说,这两个属性是我们一直期待的特性,或许在将来不久就可以运用到项目中。但要彻底了解他们,那么还需要深入的了解字体的设计,排版技术等相关的知识。但它们都已经超出来Web开发者的知识范围,如果你在这方面有经验,欢迎在下面的评论中与我们一起共享。