深度了解CSS里面字体规则、行高和垂直对齐方式

刘隆-产品设计师
• 阅读 1199

大家熟知的Line-heightvertical-align是简单的CSS属性。如此简单,以至于我们大多数人都确信完全理解它们的工作原理和使用方法。真的是这样么,我觉得事实并非如此。它们确实很复杂,也是CSS中难点之一,因为它们在创建 CSS 鲜为人知的各种功能方面发挥着重要作用:内联格式化上下文

例如,line-height可以设置为长度或无单位值,但默认为normal. 好的,但什么是正常?我们经常读到它是(或应该是)1,或者可能是 1.2,甚至CSS 规范在这一点上也不清楚。我们知道无单位line-heightfont-size相对的,但问题是它在不同字体系列中表现不同,所以总是相同还是不同?真的在1到1.2之间吗?并且,它的含义是什么?font-size: 100px``line-height``vertical-align``line-height

深入研究一个不那么简单的 CSS 机制……

我们font-size先来说说

看看这个简单的 HTML 代码,一个<p>包含 3 的标签<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 }

将相同font-size的字体与不同的字体系列一起使用会产生具有不同高度的元素:

深度了解CSS里面字体规则、行高和垂直对齐方式

*

不同的字体系列,相同的字体大小,给出不同的高度
*


即使我们知道这种行为,为什么不创建 100px 高度的元素呢?我测量并发现了这些值:Helvetica:115px,Gruppo:97px 和 Catamaran:164pxfont-size: 100px

深度了解CSS里面字体规则、行高和垂直对齐方式

*

100px 的元素高度在 97px 到 164px 之间变化
*


虽然一开始看起来有点奇怪,但这是完全可以预料的。**原因在于字体本身**。下面是它的工作原理:
  • 一种字体定义了它的em-square(或 UPM,units per em),一种将在其中绘制每个字符的容器。这个平方使用相对单位,一般设置为1000个单位。但它也可以是 1024、2048 或其他任何值。
  • 根据其相对单位,设置字体规格(升序、降序、大写高度、x 高度等)。请注意,某些值可能会溢出到 em-square 之外。
  • 在浏览器中,相对单位被缩放以适合所需的字体大小。

让我们使用 Catamaran 字体并在FontForge中打开它以获取指标:

  • em-square 是 1000
  • ascender 是 1100,descender 是 540。运行一些测试后,浏览器似乎在 Mac OS 上使用HHead Ascent / Descent值,在 Windows 上使用 Win Ascent / Descent值(这些值可能不同!)。我们还注意到资本高度为 680,X 高度为 485。
    ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/38f8e5245f2d42b0b0cf5824dc0dc331~tplv-k3u1fbpfcp-zoom-1.image)

*

font-size: 使用 FontForge 的字体规格值
*


这意味着 Catamaran 字体在 1000 个单位的 em-square 中使用 1100 + 540 个单位,这在设置时给出 164px 的高度。**这个计算出的高度定义了** **元素的** ***内容区域***,我将在本文的其余部分引用这个术语。您可以将*内容区域*视为属性适用的区域[2](https://iamvdo.me/en/blog/css-font-metrics-line-height-and-vertical-align#fn-2 "这不是严格意义上的")。`font-size: 100px`****** **`background`[](https://iamvdo.me/en/blog/css-font-metrics-line-height-and-vertical-align#fn-2 "这不是严格意义上的")

我们还可以预测大写字母高 68px(680 个单位),小写字母(x 高度)高 49px(485 个单位)。因此,1ex= 49px 和1em= 100px,而不是 164px(谢天谢地,em是基于font-size,而不是计算的高度)

深度了解CSS里面字体规则、行高和垂直对齐方式

*

font-size: 双体船字体:UPM —Units Per Em—和使用字体大小等效的像素:100px
*


在深入探讨之前,请简要说明它涉及的内容。当一个<p>元素在屏幕上呈现时,它可以根据其宽度由许多行组成。每行由一个或多个内联元素(HTML 标签或文本内容的匿名内联元素)组成,称为line-boxline-box 的高度基于它的孩子的高度**。因此,浏览器会计算每个内联元素的高度,从而计算行框的高度(从其子项的最高点到其子项的最低点)。因此,line-box总是足够高以包含其所有子项(默认情况下)。

每个 HTML 元素实际上是一堆line-boxes。如果你知道每个line-box的高度,你就知道元素的高度。

如果我们像这样更新之前的 HTML 代码:

<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>

它将生成 3 个行框

  • 第一个和最后一个都包含一个匿名内联元素(文本内容)
  • 第二个包含两个匿名内联元素,第三个<span>

深度了解CSS里面字体规则、行高和垂直对齐方式

*

font-size: font-size: A <p>(黑色边框)由包含内联元素(实线边框)和匿名内联元素(虚线边框)的行框(白色边框)组成
*


我们清楚地看到第二个*线框*比其他线框高,这是因为计算了它的子项的*内容区域*,更具体地说,是使用 Catamaran 字体的那个。

line-box创建的难点在于我们不能真正看到,也不能用 CSS 控制它。即使应用背景也不会给我们任何关于第一个line-box高度的视觉线索。::first-line**

line-height: 问题及其他

到现在为止,我介绍了两个概念:content-arealine-box。如果你没看错的话,我说过line-box的高度是根据它的children的高度来计算的,我并没有说它的children content-area的高度。这有很大的不同。

尽管这听起来很奇怪,但内联元素有两个不同的高度:内容区域高度和虚拟区域高度(我发明了术语虚拟区域,因为高度对我们来说是不可见的,但你不会发现任何出现在规范中)。

  • 内容区域高度由字体规格定义(如前所述)
  • virtual-area height 是,line-height它是*用于计算line-box*高度的高度

深度了解CSS里面字体规则、行高和垂直对齐方式

*

font-size: font-size: 行内元素有两种不同的高度
*


话虽如此,它打破了`line-height`基线之间距离的普遍看法。在 CSS 中,它不是[3](https://iamvdo.me/en/blog/css-font-metrics-line-height-and-vertical-align#fn-3 "在其他编辑软件中,它可能是基线之间的距离。 在 Word 或 Photoshop 中,就是这种情况。 主要区别在于第一行在 CSS 中也受到影响")。

深度了解CSS里面字体规则、行高和垂直对齐方式

*

font-size: font-size:font-size: 在 CSS 中,行高不是基线之间的距离
*


*计算出的虚拟区域*和*内容区域*之间的高度差称为行距。*前导的一半添加到内容区域*的顶部,另一半添加到底部。**因此*content-area*总是在*virtual-area***的中间。

根据其计算值,line-height( virtual-area ) 可以等于、高于或小于content-area。在较小的virtual-area的情况下,leading 是负的并且line-box在视觉上比它的孩子小。

还有其他类型的内联元素:

  • 替换内联元素(<img>, <input>,<svg>等)
  • inline-block和所有inline-*元素
  • 参与特定格式化上下文的内联元素(例如,在 flexbox 元素中,所有 flex 项目都是块化的)

对于这些特定的内联元素,高度是根据它们的height,marginborder属性计算的。如果heightauto,则line-height使用 并且内容区域严格等于line-height.

深度了解CSS里面字体规则、行高和垂直对齐方式

*

font-size:内联替换元素、inline-block/inline-* 和块化内联元素的内容区域等于它们的高度或行高


无论如何,我们仍然面临的问题是`line-height`的`normal`价值是多少?至于*内容区域*高度的计算,答案可以在字体指标中找到。

那么让我们回到 FontForge。双体船的 em-square 是 1000,但我们看到许多上升/下降值:

  • generals Ascent/Descent : ascender 为 770,descender 为 230。用于人物绘图。(表 “OS/2”
  • metrics Ascent/Descent:ascender 为 1100,descender 为 540。用于content-area*的高度。(表 *“hhea” 和表 “OS/2”
  • 公制线间隙。用于, 通过将此值添加到上升/下降*指标。(表 *“hhea”line-height: normal****

在我们的例子中,Catamaran 字体定义了一个 0 单位的行间距,因此等于 content-area ,即 1640 个单位,或 1.64line-height: normal**

作为比较,Arial 字体描述了一个 2048 个单位的 em-square,一个 1854 个单位的 ascender,一个 434 个单位的下行和一个 67 的行间距。这意味着给出了112px(1117 个单位)的内容区域和115px的内容区域(1150 个单位或 1.15)。所有这些指标都是特定于字体的,由字体设计师设置。font-size: 100px**line-height: normal

很明显,设置是一种不好的做法line-height: 1。我提醒您,无单位值是font-size相对的,而不是内容区域的相对值,处理小于内容区域的 虚拟区域是我们许多问题的根源。

深度了解CSS里面字体规则、行高和垂直对齐方式

*

font-size: 使用 line-height: 1 可以创建一个小于 content-area 的 line-box
*


但不仅如此。对于它的价值,在我的计算机上安装的 1117 种字体(是的,[我安装了所有来自 Google Web Fonts 的字体](https://github.com/qrpike/Web-Font-Load))中,1059 种字体(大约 95%)的计算值大于 1。它们的计算值从 0.618 到 3.378。你读得很好,3.378!`line-height: 1`[](https://github.com/qrpike/Web-Font-Load)`line-height``line-height`

line-box计算的小细节:

  • 对于内联元素,padding增加border背景区域,但不增加content-area的高度(也不增加line-box的高度)。因此,内容区域并不总是您在屏幕上看到的。margin-top并且margin-bottom没有效果。
  • 对于替换的内联元素inline-block块化的内联元素:paddingmarginborder增加height,因此内容区域行框的高度

vertical-align: 一个属性来控制一切

我还没有提到这个vertical-align属性,尽管它是计算line-box高度的一个重要因素。我们甚至可以说,vertical-align内联格式化上下文可能具有主导作用

默认值为baseline。您是否提醒字体指标升序器和降序器?这些值决定了基线的位置,以及比率。由于上升器和下降器之间的比例很少是 50/50,它可能会产生意想不到的结果,例如兄弟元素。

从该代码开始:

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

具有 2 个兄弟姐妹的<p>标签<span>inheritingfont-familyfont-sizefixed line-height。基线将匹配并且line-box的高度等于它们的line-height

深度了解CSS里面字体规则、行高和垂直对齐方式

*

font-size: 相同的字体值,相同的基线,一切似乎都OK
*


如果第二个元素有一个更小的`font-size`怎么办?
span:last-child {
    font-size: 50px;
}

听起来很奇怪,默认基线对齐可能会导致更高(!)的 line-box,如下图所示。我提醒你,一个line-box的高度是从它的孩子的最高点到它的孩子的最低点计算的。

深度了解CSS里面字体规则、行高和垂直对齐方式

*

font-size: 较小的子元素可能会导致较高的行框高度
*


这可能是支持使用[无单位值](http://allthingssmitty.com/2017/01/30/nope-nope-nope-line-height-is-unitless/)[的论据`line-height`](http://allthingssmitty.com/2017/01/30/nope-nope-nope-line-height-is-unitless/),但有时您需要固定的值来[创建完美的垂直节奏](https://scotch.io/tutorials/aesthetic-sass-3-typography-and-vertical-rhythm#baseline-grids-and-vertical-rhythm)。**老实说,无论你选择什么,你总是会遇到内联对齐的问题**。

看看这个另一个例子。带有的<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-family(默认为serif)。<p>标签和标签之间的基线<span>可能不同,因此行框的高度比预期的要高。发生这种情况是因为浏览器在进行计算时就好像每个行框都以零宽度字符开头,该规范称为 strut。

一个看不见的角色,但一个看得见的影响。

继续,我们面临着与兄弟元素相同的先前问题。

深度了解CSS里面字体规则、行高和垂直对齐方式

*

font-size: 每个子元素都对齐,就好像它的行框以一个不可见的零宽度字符开头一样
*

基线对齐被拧紧了,但是如何进行救援呢?正如您在规范中看到的那样,“将盒子的垂直中点与父盒子的基线加上父盒子 x 高度的一半对齐”。基线比率不同,x 高度比率也不同,因此 对齐也不可靠。最糟糕的是,在大多数情况下,永远不会真正“处于中间”。涉及的因素太多,无法通过 CSS 设置(x-height、ascender/descender ratio 等)vertical-align: middle``middlemiddlemiddle

作为旁注,还有 4 个其他值,在某些情况下可能有用:

  • vertical-align: top/bottom对齐到行框的顶部或底部
  • vertical-align: text-top/text-bottom对齐到内容区域的顶部或底部

深度了解CSS里面字体规则、行高和垂直对齐方式

*

font-size:垂直对齐:顶部、底部、文本顶部和文本底部
*


但要小心,在所有情况下,它都会对齐*virtual-area*,因此是不可见的高度。看看这个使用. **Invisible** **可能会产生奇怪但不足为奇的结果**。`vertical-align: top`**`line-height`

深度了解CSS里面字体规则、行高和垂直对齐方式

*

font-size: vertical-align 一开始可能会产生一些错乱,但在可视化 line-height 时是常常见到的
*


最后,`vertical-align`还接受相对于基线升高或降低框的数值。最后一个选项可能会派上用场。

CSS无所不能

我们已经讨论了如何协同工作,但现在的问题是:字体规格是否可以通过 CSS 控制line-heightvertical-align简短的回答:没有。即使我真的希望如此。无论如何,我认为我们必须玩一下。字体指标是恒定的,所以我们应该能够做一些事情。

例如,如果我们想要一个使用 Catamaran 字体的文本,其中大写高度正好是 100px 高怎么办?似乎可行:让我们做一些数学运算。

首先我们将所有字体指标设置为 CSS 自定义属性4,然后计算font-size得到 100px 的大写高度。

p {
    /* font metrics */
    --font: Catamaran;
    --fm-capitalHeight: 0.68;
    --fm-descender: 0.54;
    --fm-ascender: 1.1;
    --fm-linegap: 0;

    /* desired font-size for capital height */
    --capital-height: 100;

    /* apply font-family */
    font-family: var(--font);

    /* compute font-size to get capital height equal desired font-size */
    --computedFontSize: (var(--capital-height) / var(--fm-capitalHeight));
    font-size: calc(var(--computedFontSize) * 1px);
}

深度了解CSS里面字体规则、行高和垂直对齐方式

*

font-size:文本高度现在是 100px 高
*


很简单,不是吗?但是,如果我们希望文本在视觉上位于中间,以便剩余空间均匀分布在“B”字母的顶部和底部怎么办?为此,我们必须`vertical-align`根据上升/下降比率进行计算。

首先,计算content -area的高度:line-height: normal**

p {
    …
    --lineheightNormal: (var(--fm-ascender) + var(--fm-descender) + var(--fm-linegap));
    --contentArea: (var(--lineheightNormal) * var(--computedFontSize));
}

那么,我们需要:

  • 大写字母底部到底部边缘的距离
  • 大写字母顶部到上边缘的距离

像这样:

p {
    …
    --distanceBottom: (var(--fm-descender));
    --distanceTop: (var(--fm-ascender) - var(--fm-capitalHeight));
}

我们现在可以计算vertical-align,这是距离乘以计算所得的差值font-size。(我们必须将此值应用于内联子元素)

p {
    …
    --valign: ((var(--distanceBottom) - var(--distanceTop)) * var(--computedFontSize));
}
span {
    vertical-align: calc(var(--valign) * -1px);
}

最后,我们设置 desiredline-height并在保持垂直对齐的同时计算它:

p {
    …
    /* desired line-height */
    --line-height: 3;
    line-height: calc(((var(--line-height) * var(--capital-height)) - var(--valign)) * 1px);
}

深度了解CSS里面字体规则、行高和垂直对齐方式

*

font-size: 文本居中时候不同行高的显示情况
*


添加一个高度与字母“B”匹配的图标现在很容易:
span::before {
    content: '';
    display: inline-block;
    width: calc(1px * var(--capital-height));
    height: calc(1px * var(--capital-height));
    margin-right: 10px;
    background: url('https://cdn.pbrd.co/images/yBAKn5bbv.png');
    background-size: cover;
}

深度了解CSS里面字体规则、行高和垂直对齐方式

*

font-size: 图标和B字母等高
*


[在 JSBin 中查看结果](http://jsbin.com/tufatir/edit?css,output)

请注意,此测试仅用于演示目的。你不能依赖这个。很多原因:

  • 除非字体规格是常量,否则浏览器中的计算不是¯⁠\ ⁠(ツ)⁠ /⁠¯
  • 如果未加载字体,后备字体可能具有不同的字体规格,并且处理多个值将很快变得非常难以管理

总结

我们学到了什么:

  • 内联格式化上下文真的很难理解

  • 所有内联元素都有 2 个高度:

    • 内容区域(基于字体规格)
    • 虚拟区域( line-height)
    • 毫无疑问,这两个高度都无法想象。(如果你是一名 devtools 开发者并且想从事这方面的工作,那可能会很棒)
  • line-height: normal基于字体规格

  • line-height: n可以创建一个小于内容区域的 虚拟区域**

  • vertical-align不是很可靠

  • line-box的高度是根据其子项line-heightvertical-align属性计算的

  • 我们无法使用 CSS 轻松获取/设置字体指标

但我仍然喜欢 CSS :)

点赞
收藏
评论区
推荐文章
如何优雅的写 css 代码
CSS(全称CascadingStyleSheets,层叠样式表)为开发人员提供声明式的样式语言,是前端必备的技能之一,基于互联网上全面的资料和简单易懂的语法,CSS非常易于学习,但其知识点广泛且分散,很难做到精通,在我们日常开发中,常常忽视了CSS代码的质量,很容易写出杂乱无章的CSS文件。
小天 小天
1年前
深度学习简介
在这篇博文中(https://www.helloworld.net/p/6137616368),我们简要介绍了机器学习。对于大多数人来说,深度学习和机器学习这两个术语似乎是AI世界中可以互换的流行语。然而,事实并非如此。因此,每个想要更好地了解人工智能领域
Wesley13 Wesley13
2年前
CSS块级元素与行内元素
CSS块级元素与行内元素行内元素与块状元素1、块级元素:可以设置width,height属性。行内元素:设置width和height无效,其宽度随其元素的内容(文字或者图片等)的宽度而变化。可以通过lineheight设置行高(行高和height是不同的东西)。2、块级元素:可以设置margin和padd
Wesley13 Wesley13
2年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Stella981 Stella981
2年前
CSS 布局 ,文档流,定位,中划线,表格属性,line
CSS知识汇总verticalalign:top|middle|bottom//用于图片时,(如文字)其他元素相对于图片的上,中,下对齐divlineheight:200px;//设置lineheight的高度和div的高度一样,则div里的文字10Px上下居中对齐divheight:200px;divfontsiz
Wesley13 Wesley13
2年前
CSS开发过程中的20个快速提升技巧
摘要:本文涵盖了20个CSS技巧,可以解决许多工作中常见的问题,让你也成为一个CSS高手。1、使用CSS重置(reset)css重置库如normalize.css已经被使用很多年了,它们可以为你的网站样式提供一个比较清晰的标准,来确保跨浏览器之间的一致性。大多数项目并不需要这些库包含的所有规则,可以
Wesley13 Wesley13
2年前
CSS基础知识整理
1什么是CSS?CSS通常称为CSS样式表或层叠样式表(级联样式表),主要用于设置HTML页面中的文本内容(字体、大小、对齐方式等)、图片的外形(宽高、边框样式、边距等)以及版面的布局等外观显示样式。CSS以HTML为基础,提供了丰富的功能,如字体、颜色、背景的控制及整体排版等,而且还可以针对不同的浏览器设置不同的样式。
Stella981 Stella981
2年前
Css3 Animation 动画十二原则
前言本文主要介绍了CSS3动画基本原理和常用形式作为前端的设计师和工程师,我们用CSS去做样式、定位并创建出好看的网站。我们经常用CSS去添加页面的运动过渡效果甚至动画,但我们经常做的不过如此。动效是一个有助于访客和用户理解我们设计的强有力工具。这里有些原则能最大限度地应用在我们的工作中。迪士尼经过基础工作练习的长时间
芝士年糕 芝士年糕
1年前
apt update和apt upgrade命令 - 有什么区别?
这些是aptupdate和aptupgrade命令。aptupdate和aptupgrade是许多Linux用户最常用但被误解的两个命令。对于某些人来说,它们扮演着相同的角色,但事实并非如此。在本指南中,我们试图区分两者之间的差异以及如何使用它们。1)aptupdate命令这是一个主要在新系统安装之后或安装新软件包之前调用的命令。apt
臧霸 臧霸
3星期前
前端模拟面试:给你真实的求职体验和面试经验
一、当准备前端面试时,确保准备以下几个方面:1.基础知识:回顾HTML、CSS和JavaScript的基础知识,包括语法、DOM操作、事件处理等。2.框架和库:熟悉流行的前端框架和库,如React、Vue和Angular,了解它们的核心概念和工作原理。3.
刘隆-产品设计师
刘隆-产品设计师
Lv1
男 · 国家特务组 · 产品设计师
用AI构建有趣产品
文章
4
粉丝
3
获赞
6