深入了解CSS:字体度量、line-height和vertical-align

原文地址:Deep dive CSS: font metrics, line-height and vertical-align

注:翻译中对内容进行了不少简化,力求不陷入繁缛的细节。

文章目录

  • 你真的了解它们吗
  • 首先聊聊font-size
  • line-box 和 inline-element
  • line-height相关的问题
  • vertical-align一个属性将它们串起来
  • CSS真奇妙
  • 知识点总结

你真的了解它们吗

很多人以为自己知道 line-heightvertical-align 是怎么工作的,以及怎么使用它们。但实际上,它们非常复杂,并且是在CSS特性 inline formating context 中扮演主要的角色。

测试你的理解情况,可以尝试回答下面几个问题:

问题1:line-height 可以是具体的单位值,也可以是无单位数值,但它的默认值是 normal,什么是 normal?通常我们把它当作1或1.2,又或者是1到1.2之间的数值?CSS的规范中都没有清楚地说明这一点。

问题2:我们知道line-height使用无单位数值的是相对 font-size而言,但问题是font-size:100px 对于不同的字体而言有着截然不同的表现,所以line-height的效果是相同的还是不同的呢?

问题3:vertical-align 对 line-height 有什么影响呢?

首先聊聊font-size

下面的简单HTML代码中,三个 <p> 标签包含了对应的 <span> ,区别在于它们有不同的 font-family

<p>
    <span class="a">Ba</span>
    <span class="b">Ba</span>
    <span class="c">Ba</span>
</p>
p  { font-size: 100px }
.a { font-family: Helvetica }
.b { font-family: Gruppo    }
.c { font-family: Catamaran }
image.png

相同的 font-size 作用在不同的 font-family 上导致了不同高度,原因是:

  • 每个字体都有它的em-square(默认字体容器高度,字体就在这个容器内被绘制),通常它的基准单位被设置为1000单位,但也有字体是1024、2048单位,甚至任何单位
  • 正是基于这个em-square的单位,字体的ascend,descend,capital height,x-height等被确定

FontForge 打开 Catamaran 字体,可以看到它的一些数值(结合下面两张图来看):

  • em size(基准单位):1000
  • ascent(上升部分):1100
  • descent(下沉部分): 540
  • capital height(大写高度): 680
  • x height(小写高度): 485
image.png
image.png

总结:

Catamaran字体在1000单位的em-square 中使用了1640单位的高度,设置 font-size:100px 最终得到了 164px的高度,我们称这个高度为 content-area ,你可以将它等同于 background 起作用的高度。

line-box 和 inline-element

在下面的例子中, <p> 元素受限于它的宽度,视觉上分了三行展示,每一行称作 inline-box。每一行都由匿名 或 带HTML标签的inner element组成,inline-box 的高度由它所包含的 inner element 决定。

line-box的高度肯定能容纳它的所有子元素,因此如果你知道了一个HTML元素的所有line-box的高度,那么你就知道了这个元素的高度(相加即可)。

下图中可以看到第二个line-box的高度大于另外两个,因为 Catamaran 字体的 content-area 高于其他的字体。

<p>
    Good design will be better.
    <span class="a">Ba</span>
    <span class="b">Ba</span>
    <span class="c">Ba</span>
    We get to make a consequence.
</p>
image.png

不过实际应用中,如果有多个line-box你是无法看到单个line-box的高度的。

line-height相关的问题

image.png

一个 inline element 有两个高度

  • content area,字体内容的高度(计算公式为:ascend+descend)
  • virtual area,用于计算line-box的高度,等于 line-height(作者发明了这个称呼)

virtual area 和 content area 高度的差值,分别对半被添加到了顶部和地步的 leading 中。依据 line-height的具体知道,这个差值可能为0、正数、负数。负数意味着virtual arae的高度低于content area。

还有一些类型的inline elements,他们的高度会由 height 、marign、border属性决定。如果 height 设置为 auto,那么(似乎默认就设置了,因此此处不强调它) content-area 的高度严格和line-height相等。

  • replaced inline elements: <img> <input> <svg>
  • inline-blockinline-* 元素
  • 在某些特殊的formating content 中(比如 flexbox中的元素)的inline元素

注:你可以尝试将 <p> 包含的 <span>设置 display:inline-block,可以看到 content-area(背景颜色区域)变得和virtual area一样了。

所以回到开篇问题:line-height 的 normal 的值是多少?

我们以另外一个字体 Arial 为例,它的 em-square取的基准单位是 2048,ascender的高度是1854单位,descender的高度是434单位,行间距是67单位,计算得到font-size:100px时,content-area的高度是:

(1854+434) / 2048 * 100=112px(小数点后四舍五入),line-height:normal的高度是:(1854+434+67)/2048*100=115px。因此 line-height:normal 的高度通常是由字体的设计者决定的。

所以显而易见的是:设置 line-height:1 是一种糟糕的实践,因为我们它相对的是font-size(即em-square),而不是 content-area,结果就是 virtual area 比 content area小。

我(注:原文作者)统计了电脑上安装的1117中字体,发现95%的字体 line-height的值都大于1,计算得到的line-heigt的值在 0.618到3.378之间。

一些line-box计算的知识点

  • inline元素,padding和border会导致背景区域变大,但是content-area高度不变,可知content-area的高度并不总和你看到的一致;margin-top和margin-bottom没有效果。
  • 对于replaced inline elements设置 inline-block后,padding margin border都会增加高度:包括content-area和line-box的高度。

vertial-align一个属性将它们串起来

虽然到目前位置我们还没提起过 vertical-align,但实际上它可能是 inline formating context 的最重要的概念。

vertical-align 的默认值是 baseline,如果你还记得前面条的 ascender 和 descender的话,它们的value决定了baseline的位置,以及上下leading的比率(很少是50/50)。

<p>
    <span>Ba</span>
    <span>Ba</span>
</p>
p {
    font-family: Catamaran;
    font-size: 100px;
    line-height: 200px;
}

一个 <p> 标签包含两个相邻的 <span> 元素,它们都继承相同的 font-family , font-sizeline-height 。两个 <span>元素的baseline对齐并且line-box的高度和line-height一样。

image.png

如果我们把第二个元素的 font-size 设置得更小一些会怎么样?

span:last-child {
    font-size: 50px;
}

可以看到,默认的baseline对齐导致了更高的line-box:line-box的高度计算方式是由子元素的最高点到最低点

image.png

让我们看看另外一个例子,一个 <p> 标签设置了 line-height: 200px,包含一个 <span> 继承了父级的 line-height

<p>
    <span>Ba</span>
</p>
p {
    line-height: 200px;
}
span {
    font-family: Catamaran;
    font-size: 100px;
}

line-box的高度会是多少呢?我们期望它是200px,事实上 <p> 有它自己的 font-famliy,结果就是 <p> 的baseline和 <span> 的baseline不同,更高的baseline导致了更高的context-area的高度。在规范中这个规则叫strut:0宽度的字符也会参与line-box的计算当中。

image.png

直接使用 vertical-align: middle 能解决这个问题吗?如规范中所说, middle 对齐的是:垂直中点加上x-height高度的一半。考虑到上下ratio并不相同,设置为 middle 也不可靠,在大多数情况下,middle 并不表示的绝对中间位置。

顺带提一下,还有4个值可能会有用:

  • vertical-align: top / bottom 对齐line-box的头部和底部
  • vertical-align: text-top / text-bottom 对齐content-area的头部或底部
image.png

当仍旧要小心的是,多数情况下对齐的参照标准是 virtual area,看看同样使用了 vertical-align: top 的例子:line-height导致了奇怪但并不令人惊讶的结果

image.png

最后, vertical-align 可以忽略baeline的位置接受数字类型的值,它可能会派上用场。

CSS真奇妙

最后原文作者演示了如何设置一个字体的各个部位的高度,可以做到让不同字体在同一个 line-box齐整的对仗,但在实际中有若干理由并不推荐你这么做。

知识点总结

  • inline formatting context 是一个实在难以理解的概念
  • 所有的inline elements有两个高度:context area 和 virtual area,并且它们都无法被视觉查看到
  • line-height: normal 是基于每种字体的设计而定的
  • vertical-align 是不可靠的
  • 一个 line-box 高度计算是基于所有子元素的 line-heightvertical-align
  • 在CSS中查询或设置字体的度量并不容易
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,601评论 4 369
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,367评论 1 305
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,249评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,539评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,967评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,929评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,098评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,825评论 0 207
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,609评论 1 249
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,796评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,282评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,603评论 3 261
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,277评论 3 242
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,159评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,959评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,079评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,874评论 2 277

推荐阅读更多精彩内容