女王控的博客

CSS世界强大文本处理能力

line-height 的另外一个朋友 font-size

第 5 章介绍过 line-height 和 vertical-align 的好朋友关系,实际上 font-size 也和 line-height 是好朋友,同样也无处不在,并且纸面上 line-height 的数值属性值和百分比值属性值都是相对于 font-size 计算的,其关系可谓不言而喻。

font-size 和 vertical-align 的隐秘故事

line-height 的部分类别属性值是相对于 font-size 计算的,vertical-align 百分比值属性值又是相对于 line-height 计算的,于是,看上去八辈子都搭不上边的 vertical-align 和 font-size 属性背后其实也有有着关联的。

例如,下面的 CSS 代码组合:

css 复制代码
p {
   font-size: 16px;
   line-height: 1.5;
}

p > img {
   vertical-align: -25%;
}

此时,p > img 选择器对应元素的 vertical-align 计算值应该是:

16px * 1.5 * -25% = -6px

也就是上面的 CSS 代码等同于:

css 复制代码
p {
   font-size: 16px;
   line-height: 1.5;
}

p > img {
   vertical-align: -6px;
}

但是两者又有所不同,很显然 -25% 是一个相对计算属性值,如果此时元素的 font-size 发生变化,则图片会自动进行垂直位置调整。我们可以看一个无论 font-size 如何变化、后面图标都垂直居中对齐的例子,无论文字字号是大还是小,后面的图标都非常良好地垂直居中对齐,如图 8-1 所示。

2020 01 10 11 53 07

核心 CSS 代码如下:

css 复制代码
p > img {
   width: 16px;
   height: 16px;
   vertical-align: 25%;
   position: relative;
   top: 8px;
}

原理如下:内联元素默认基线对齐,图片的基线可以看成是图片的下边缘,文字内容的基线是字符 x 下边缘,因此,本例中,图片下边缘默认和“中文”两个汉字字形底边缘往上一点的位置对齐。然后,我们通过 vertical-align: 25% 声明让图片的下边缘和中文汉字的中心线对齐。此时,图标和文字的状态应该如图 8-2 所示。

2020 01 10 11 55 22

图 8-2 完全就是实例效果注释 top: 8px 后的截图标注,没有任何加工。看上去似乎上面小,实际上是视觉误差,分隔线上下完全均等,1 像素不差。

由于我们这里的图标是固定的像素尺寸,因此,通过偏移自身 1/2 高度来实现真正的居中,可以使用 CSS3 transform 位移,我这里为了兼容性,使用了 relative 相对定位。

其居中原理本质上和绝对定位元素 50% 定位加偏移自身 1/2 尺寸实现居中是一样的,只不过这里的偏移使用的是 vertical-align 百分比值。

这么一看,vertical-align 百分比属性值似乎还是有点用的!如果再联想到 vertical-align: middle 实现垂直居中效果经常不尽如人意,说不定还能找到一块更好的宝。但我要告诉你,其实还有更好的实现,那就是使用单位 ex。例如,将前面例子中的 vertical-align: 25% 改成 vertical-align: .6ex,效果基本上就是一样的,并且还多了一个优点,就是使用 vertical-align: .6ex 实现的垂直居中效果不会受 line-height 变化影响,而使用 vertical-align: 25%,line-height 一旦变化,就必须改变原来的 vertical-align 大小、重新调整垂直位置,这容错性明显就降了一个层次。

因此,虽然例子演示的是 vertical-align 百分比值,实际上是推荐使用与 font-size 有着密切关系的 ex 单位。

说到这里,忍不住想介绍另外一些和 font-size 有着密切的关系的东西。

理解 font-size 与 ex、em 和 rem 的关系

ex 是字符 x 高度,显然和 font-size 关系密切,font-size 值越大,自然 ex 对应的大小也就大,对此本书前面已经有介绍,这里不赘述。

下面来看看单位 em。如果说 ex 是字符 x 高度,那是不是 em 就是字符 m 的高度?

我的回答是“不是的”,但是 em 和字符 m 确实有关。em 在传统排版中指一个字模的高度(可以脑补下活字印刷的字模)。

例如,浏览器默认 font-size 大小是 16px,假设一个<div>宽度是 160px,则正好可以放下 10 个汉字不换行;如果是 159px 像素,第十个汉字就会掉下来;如果再同时设置 line-height:1 和一个背景色,代码如下:

css 复制代码
div {
   width: 160px;
   line-height: 1;
   background-color: #eee;
}

我们就会发现中文汉字的尺寸就可以看作 em 单位的代名词,尤其在高度这一块,简直分毫不差,如图 8-3 所示。

2020 01 13 15 12 20

也就是说,em 就是’中’等汉字的高度!于是,我们对 em 的理解就更加简单了,直接看一个很容易理解错误的题目,在 Chrome 浏览器下,<h1>元素有如下的默认 CSS:

css 复制代码
h1 {
   font-size: 2em;
   -webkit-margin-before: 0.67em;
   -webkit-margin-after: 0.67em;
}

那么,假设页面没有任何 CSS 重置,根元素 font-size 就是默认的 16px,请问:此时<h1>元素 margin-before 的像素计算值是多少?

如果对 em 了解不够,很容易认为 1em 大小就是 16px,于是计算值是 16px × 0.67 = 10.72px,实际上这是错误的。

我们可以这样想,假设 <h1> 里面有汉字,此时汉字的高度是多少?这个高度就是此时 1em 大小。<h1> 元素此时 font-size 是 2em,算一下就是 32px,因此,此时里面汉字的高度应该是 32px,也就是说,此时<h1>元素的 1em 应该是 32px,于是 margin-before 的像素计算值为 32px × 0.67 = 21.44px,和浏览器自己的计算值一样,如图 8-4 所示。

2020 01 13 15 15 42

乍一看,似乎出现了死循环悖论:font-size: 2em,于是 1em 变成 32px,那此时的 2em 不又是 64px,然后又……

正如前面提到过的一样,CSS 世界的渲染是一次渲染,是不会有死循环的。这里是先计算 font-size,然后再计算给其他使用 em 单位的属性值大小。

总结如下:在 CSS 中,1em 的计算值等同于当前元素所在的 font-size 计算值,可以将其想象成当前元素中(如果有)汉字的高度。

所有相对单位的好处都是一样的,样式表现更具有弹性。例如,理论上,有一个布局,希望小屏时整体缩小,大屏时再弹性扩大,此时就可以让所有元素宽高尺寸等都使用 em,于是,最后只要改变布局祖先元素的 font-size 大小就可以实现整体的弹性变化。

这种策略很棒,也确实可行,但是有一个比较麻烦的事情,它和上面 <h1> 元素计算一样的麻烦,em 是根据当前 font-size 大小计算的,一旦布局中出现标题这种跟基础 font-size 大小不一样的场景的时候,标题里面所有元素 em 都要重新计算一遍,甚为麻烦,最终的成品维护成本就比较高了。

正是由于这种局限性,另外一个和 font-size 密切相关的单位出现了,就是 rem,即 root em,顾名思义,就是根元素 em 大小。em 相对于当前元素,rem 相对于根元素,本质差别在于当前元素是多变的,根元素是固定的,也就是说,如果使用 rem,我们的计算值不会受当前元素 font-size 大小的影响,假设 <h1> 的默认 CSS 是这样:

css 复制代码
h1 {
   font-size: 2em;
   -webkit-margin-before: 0.67rem;
   -webkit-margin-after: 0.67rem;
}

那么 2em 的 font-size 计算值会被忽略,直接使用根元素的 16px 进行计算,于是 margin-before 计算值是 16 像素 ×0.67 = 10.72 像素。

因此,要想实现带有缩放性质的弹性布局,使用 rem 是最佳策略,但 rem 是 CSS3 单位,IE9 以上浏览器才支持,需要注意兼容性,我这里就不再多介绍了。

回到 em 单位。em 实际上更适用于图文内容展示的场景,对此进行弹性布局。例如 <h1>~<h6> 以及 <p> 等与文本内容展示的元素的 margin 都是用 em 作为单位,这样,当用户把浏览器默认字号从“中”设置成“大”或改成“小”的时候,上下间距也能根据字号大小自动调整,使阅读更舒服。

再举个适用于 em 的场景,如果我们使用 SVG 矢量图标,建议设置 SVG 宽高如下:

css 复制代码
svg {
   width: 1em;
   height: 1em;
}

这样,无论图标是个大号文字混在一起还是和小号文字混在一起,都能和当前文字大小保持一致,既省时又省力。

理解 font-size 的关键字属性值

font-size 支持长度值,如 1em,也支持百分比值,如 100%。这两点想必众所周知,但 font-size 还支持关键字属性值这一点怕是就有不少人不清楚了。

font-size 的关键字属性值分以下两类。

  1. 相对尺寸关键字。

    指相对于当前元素 font-size 计算,包括:

    • larger:大一点,是 <big> 元素的默认 font-size 属性值。
    • smaller:小一点,是 <small> 元素的默认 font-size 属性值。
  2. 绝对尺寸关键字。

    与当前元素 font-size 无关,仅受浏览器设置的字号影响。注意这里的措辞,是“浏览器设置”,而非“根元素”,两者是有区别的。

    • xx-large:好大好大,和 <h1> 元素计算值一样。
    • x-large:好大,和 <h2> 元素计算值一样。
    • large:大,和 <h3> 元素计算值近似
    • medium:不上不下,是 font-size 的初始值,和 <h4> 元素计算值一样。
    • small:小,和 <h5> 元素计算值近似。
    • x-small:好小,和 <h6> 元素计算值近似。
    • xx-small:好小好小,无对应的 HTML 元素。

其中,相对尺寸关键字 larger 和 smaller 由于计算的系数在不同浏览器下差异很大,因此实用价值有限,只有类似文档页、帮助页这类对文字尺寸要求不高的场合才有用;而绝对尺寸关键字的实用性要大一些,而且在某些场合是推荐使用的关键字属性值,这个要慢慢讲。

下面两个 CSS 代码有什么区别?

css 复制代码
html {
   font-size: 14px;
}

html {
   font-size: 87.5%;
}

在绝大多数场景下,两者没有差别,全都计算为 14px,但是如果用户对浏览器的字号进行了调整,例如,把默认的“中”设置成了“大”,如图 8-5 所示(截自 Chrome 浏览器),那么此时,font-size: 14px 计算值还是 14px,但 font-size: 87.5% 的计算值则大了一圈,于是差别就出现了。如果是像素单位 font-size,用户改变浏览器的默认字号后,页面会微丝不动;如果是百分比值 font-size,则字号相应放大,这就涉及用户体验和可访问性问题了。

2021 12 14 13 54 00

正常情况下,14 像素的文字大小是足够的,但是,如果是高度近视的用户,或者上班急急忙忙忘记戴眼镜,或者在投影仪上投影网页内容,此时就有大字号浏览网页的需求,如果使用固定的像素单位,显然对这些使用场景是不友好的。

好在浏览器还提供了“网页缩放”功能,但是此功能也是有局限性的:如果网页是定宽非响应式的,则网页放大后窗体以外的内容就看不到了,在 Chrome/Firefox 浏览器下甚至连个水平滚动条都没有,说不定重要信息就会看不到。由此可见,我们是不能轻易忽视浏览器字号设置功能的。

然而,现代网页设计得很精致,要想网页布局跟随字体内容缩放实在两难,要么使用 em,但 em 计算与当前 font-size 耦合,不好维护;要么使用 rem,但 IE8 不支持,桌面端使用尴尬。因此,现实的压迫导致我们只能使用 px 进行布局,尤其桌面端网页。

如何权衡“易于实现维护”“视觉还原”“可访问性”这三者,我这里有两个建议。

  1. 即使是定宽的传统桌面端网页,也需要做响应式处理,尤其是针对 1200 像素宽度设计的网页,但只需要响应到 800 像素即可,可以保证至少有 1.5 倍的缩放空间,如果做到这一步,那么是否需要响应浏览器的字号设置这一点就可以忽略。

  2. 如果因各种原因无法做响应式处理,也没有必要全局都使用相对单位,毕竟成本等现实问题摆在那里,其实只需要在图文内容为主的重要局部区域使用可缩放的 font-size 处理即可。例如,小说网站的阅读页、微信公众号文章展示区、私信对话内容区、搜索引擎的落地页、评论区等,都强烈建议摒弃 px 单位,而采用下面的实践策略。

    • 容器设置 font-size: medium,此时,这个局部展示区域的字号就跟着浏览器的设置走了,默认计算值是 16px。

    • 容器内的文字字号全部使用相对单位,如百分比值或者 em 都可以,然后基于 16px 进行转换。例如:

      css 复制代码
      .article {
       font-size: medium;
      }
      .article h1 {
       font-size: 2em;
       margin: 0.875em 0;
      }
      .article p {
       font-size: 87.5%; /* 默认字号下计算值是 14px */
       margin: 1em 0;
      }

同时使用自适应流体布局,间距什么的也使用相对单位,例如上面 margin 使用的是 em 单位。于是,当用户改变了浏览器的字号后,整个阅读区域的所有字样甚至布局都会跟着放大,文字一下子就看清楚了。这种局部处理的好处在于,页面的导航、侧边栏这些不需要长时间阅读的模块还是原来的像素布局,还是那么精致,丝毫不受影响。就这么很微小的变动,就可以让你的网页在可访问性这一块超越大多数的网站,何乐而不为?

可以看到,绝对尺寸关键字在实际项目中还有很有价值的,但有价值的仅仅是 medium,至于其他关键字,作用仅限于字面上的那点儿,大家了解一下即可。

font-size: 0 与文本的隐藏

桌面 Chrome 浏览器下有个 12px 的字号限制,就是文字的 font-size 计算值不能小于 12px,我猜是因为中文,如宋体,要是小于 12px,就会挤成成一团,略丑,Chrome 看不下去,就直接禁用了。

正是这种限制导致我们在使用 em 或 rem 进行布局的时候,不能这么处理:

css 复制代码
html {
   font-size: 62.5%;
}

理论上,此时根字号计算值是 16px*0.625=10px,于是 width: 14px 可以写成 width: 1.4em,省了很多计算的麻烦。但是,在 Chrome 下由于 12px 的限制,根字号计算值实际不是 10px,而是 12px,所以,可以试试处理成这样:

css 复制代码
html {
   font-size: 625%;
}

此时根字号计算值是 100px,既计算无忧,又没有 12px 的最小字号限制。

但是我个人建议还是不要这样处理,尤其使用 em 的时候,因为 font-size 属性和 line-height 属性一样,由于继承性的存在,会影响贯穿整个网页,100px 的环境 font-size 一定会将平时不显山露水的底边对齐问题、间隙问题等放大,导致出现一些明显的样式问题,如果对 CSS 了解不是很深刻,怕是很难明白为什么会发生这样的问题。同时这样做也限制了 px 等其他单位的使用,有时候是比较要命的。

因此,我的建议是仍基于浏览器默认的字号进行相对计算,也就是 medium 对应的 16px,16 这个数字是一定可以整除的,因此计算成本还行,或者使用 Sass 或 Less 之类的工具辅助计算。

还是回到字号限制的问题。实际上,并不是所有小于 12px 的 font-size 都会被当作 12px 处理,有一个值例外,那就是 0,也就是说,如果 font-size:0 的字号表现就是 0,那么文字会直接被隐藏掉,并且只能是 font-size: 0,哪怕设置成 font-size: 0.0000001px,都还是会被当作 12px 处理的。

因此,如果希望隐藏 logo 对应元素内的文字,除了 text-indent 缩进隐藏外,还可以试试下面这种方法:

css 复制代码
.logo {
   font-size: 0;
}

字体属性家族的大家长 font-family

font-family 默认值由操作系统和浏览器共同决定,例如 Windows 和 OS X 下的 Chrome 默认字体不一样,同一台 Windows 系统的 Chrome 和 Firefox 浏览器默认字体也不一样。

font-family 支持两类属性值,一类是“字体名”,一类是“字体族”。“字体名”很好理解,就是使用的对应字体的名称。例如:

css 复制代码
body {
   font-family: simsun;
}

就表示使用的是“宋体”。如果字体名包含空格,需要使用引号包起来。例如:

css 复制代码
body {
   font-family: 'Microsoft Yahei';
}

根据我的实践,可以不用区分大小写。如果有多个字体设定,从左往右依次寻找本地是否有对应的字体即可。例如:

css 复制代码
body {
   font-family: 'PingFang SC', 'Microsoft Yahei';
}

就是先寻找是否本地有 PingFang SC 字体;如果没有,则继续寻找本地是否有 Microsoft Yahei 字体;如果都没找到,就使用默认值。

但是,“字体族”分为很多类,MDN 上文档分类如下:

  • font-family: serif;
  • font-family: sans-serif;
  • font-family: monospace;
  • font-family: cursive;
  • font-family: fantasy;
  • font-family: system-ui;

具体含义解释如下。

  • serif:衬线字体。
  • sans-serif:无衬线字体。
  • monospace:等宽字体。
  • cursive:手写字体。
  • fantasy:奇幻字体。
  • system-ui:系统 UI 字体。

对于中文网站,cursive 和 fantasy 应用场景有限,因此这里不予探讨,这里着重介绍一下衬线字体、无衬线字体和等宽字体。

了解衬线字体和无衬线字体

字体分衬线字体和无衬线字体。所谓衬线字体,通俗讲就是笔画开始、结束的地方有额外装饰而且笔画的粗细会有所不同的字体。网页中常用中文衬线字体是“宋体”,常用英文衬线字体有 Times New Roman、Georgia 等。无衬线字体没有这些额外的装饰,而且笔画的粗细差不多,如中文的“雅黑”字体,英文包括 Arial、Verdana、Tahoma、Helivetica、Calibri 等

以前人们排正文喜欢使用衬线字体,但是如今,不知是审美疲劳还是人们更加追求简洁干净的缘故,更喜欢使用无衬线字体,如“微软雅黑”或者“苹方”之类的字体。

在 CSS 世界中,字体是有对应的属性值的,如下:

css 复制代码
font-family: serif; /* 衬线字体 */
font-family: sans-serif; /* 无衬线字体 */

我们在移动端 Web 开发的时候,虽然设备的默认中文字体不一样,但都是无衬线,都挺好看的,因此可以直接使用下面的 CSS 代码:

css 复制代码
body {
   font-family: sans-serif;
}

没有必要特别指定中文字体,否则说不定会画蛇添足。

serif 和 sans-serif 还可以和具体的字体名称写在一起,例如:

css 复制代码
body {
   font-family: 'Microsoft Yahei', sans-serif;
}

但是需要注意的是,serif 和 sans-serif 一定要写在最后,因为在大多数浏览器下,写在 serif 和 sans-serif 后面的所有字体都会被忽略。例如:

css 复制代码
body {
   font-family: sans-serif, 'Microsoft Yahei';
}

在 Chrome 浏览器下,后面的 Microsoft Yahei 字体是不会被渲染的。据我的推测,有可能浏览器认为当前“字体族”已经满足了文本渲染的需要,没必要再往后解析了

等宽字体的实践价值

所谓等宽字体,一般是针对英文字体而言的。据我所知,东亚字体应该都是等宽的,就是每个字符在同等 font-size 下占据的宽度是一样的。但是英文字体就不一定了,我随便写一个单词,就 iMac 吧,大家很明显地发现这个字符 i 要比 M 占据的宽度小。如果这样看着还不够清楚,那我换一种呈现方式,上下两行,上一行 6 个 i,下一行 6 个 M,如下:

复制代码
iiiiii
MMMMMM

实际的两行文本的宽度可能就如图 8-6 所示这般差异明显。

但是,如果是等宽字体(可以让英文字符同等宽度显示的字体就称为“等宽字体”),如 Consolas、Monaco、monospace,则宽度表现就不一样了,如图 8-7 所示。

2021 12 15 11 28 20

等宽字体在 Web 中有什么用呢?

  1. 等宽字体与代码呈现

    首先等宽字体利于代码呈现。对于写代码的人来说,无论是什么语言,易读是第一位,使用等宽字体,我们阅读起来会更轻松舒服。因此,一般编辑器使用的字体或者 Web 上需要呈现源代码的字体都是等宽字体。例如,即将出现的演示页面的源代码如图 8-8 所示。

    2021 12 15 11 30 20

  2. 等宽字体与图形呈现案例一则

    假设某工具有这么一个功能:通过下拉选择,可以改变元素的边框样式,也就是 borderStyle 在 solid/dashed/dotted 间切换。

    大家都知道,原生的 <select><option> 元素的 innerHTML 只能是纯 text 字符,不能有 html,也不支持伪元素,因此,要模拟 solid、dashed 和 dotted,只能使用字符,而字符有长有短,可以模拟成像样的规整的图形吗?

    可以的,试试使用等宽字体。

  3. ch 单位与等宽字体布局

    ch 和 em、rem、ex 一样,是 CSS 中和字符相关的相对单位。和 ch 相关的字符是 0,没错,就是阿拉伯数字 0。1ch 表示一个 0 字符的宽度,所以 6 个 0 所占据的宽度就是 6ch。

    由于 ch 是个 CSS3 单位,且 IE9 浏览器的宽度和其他浏览器明显不一样,因此此处不展开,但可以提一提一些不错的应用场景。例如,有些输入框是输入手机号的,在中国,手机号是 11 位,因此我们可以设置该输入框宽度为 11ch,同时让字体等宽,则用户一眼就能看出自己是否少输入或者多输入了 1 位数字。又如,我们想实现一个屏幕上代码一个一个出现的动效,如果代码是等宽字体,此时使用 ch 单位来控制宽度,配合 overflow 属性和 CSS animation 就能在完全不使用 JavaScript 的情况下将此效果模拟出来。当然,还有其他一些应用场景,不一一说明。

中文字体和英文名称

使用中文字体,就必须要知道其对应的英文名称,下面是一些常见中文字体对应的 font-family 英文属性名称。

  1. Windows 常见内置中文字体和对应英文名称见图 8-10
  2. OS X 系统内置中文字体和对应英文名称见图 8-11
  3. Office 软件安装新增中文字体和对应英文名称见图 8-12
  4. 其他一些中文字体和对应英文名称见图 8-13

2021 12 15 13 39 01

2021 12 15 13 39 14

2021 12 15 13 39 37

一些补充说明

微软正黑体是一款全面支持 ClearType 技术的 TrueType 无衬线字体,用于繁体中文系统。相对应地,中国大陆地区用的是微软雅黑。但由于中国大陆和中国港、澳、台地区在各自的文字规范中对汉字的写法规定有很多细节上的不同,所以这两套字形在正式场合是不能混淆使用的。

我们平常所说的“宋体”,指的都是“中易宋体”,英文名称 SimSun,“黑体”类似的是“中易黑体”。在 OS X 常见内置中文字体中我罗列了一个“宋体-简”,需要注意的是,这个“宋体- 简”和我们平常所说的“宋体”并不是同一个字体,其英文名称是“Songti SC”,字形表现也有差异。

OS X 也就是苹果操作系统的字体名称中经常会出现“SC”,这个“SC”指的是“简体” (simplified chinese)的意思,相对应的还有“TC”,指的是“繁体”(traditional chinese)的意思。

字体家族其他成员

貌似粗犷、实则精细无比的 font-weight

font-weight 都支持哪些属性值。具体如下

css 复制代码
/* 平常用的最多的 */
font-weight: normal;
font-weight: bold;
/* 相对于父级元素 */
font-weight: lighter;
font-weight: bolder;
/* 字重的精细控制 */
font-weight: 100;
font-weight: 200;
font-weight: 300;
font-weight: 400;
font-weight: 500;
font-weight: 600;
font-weight: 700;
font-weight: 800;
font-weight: 900;

如果使用数值作为 font-weight 属性值,必须是 100 ~ 900 的整百数。这里的数值关键字和字母关键字之间是有对应关系的,例如,font-weight:400 实际上等同于 font-weight: normal,font-weight:700 等同于 font-weight:bold。

因此 400 和 700 是文字粗细与否的重要临界点,加上最小的 100 和最大的 900,就构成了 font-weight 完整的字重临界点。知道这个有什么意义呢?意义就在于 lighter 和 bolder 这两个具有相对特定的关键字就是基于这 4 个临界点进行解析和渲染的。

以下是 lighter 和 bolder 解析规则表

继承的值 bolder lighter
100 400 100
200 400 100
300 400 100
400 700 100
500 700 100
600 900 400
700 900 400
800 900 700
900 900 700

然而很多人发现 font-weight 无论是设置 300、400、500 还是 600,文字的粗细都没有任何变化,只有到 700 的时候才会加粗一下,感觉浏览器好像不支持这些数值,那么搞这么多档位不就是多余的吗?

实际上,所有这些数值关键字浏览器都是支持的,之所以没有看到任何粗细的变化,是因为我们的系统里面缺乏对应粗细的字体。尤其我们做桌面端项目时,大部分用户都是使用 Windows 系统,而 Windows 系统中的中文字体粗细就一个型号,如“宋体”,或者说“微软雅黑”,因此,最终的效果就是 CSS 层面的“加粗”和“正常尺寸” 两种表现。

假如我们的操作系统安装了该字体家族全部的字重字体,则设置 300、400、500 时,彼此之间就能看出明显的变化了。例如,OS X 系统中的“苹方”,又如我这里即将要演示的“思源黑体”。“思源黑体”是一款免费的开源字体,我自己电脑上的版本有 7 个字重,如图 8-14 所示。

2021 12 19 22 39 31

此时,应用如下 HTML 和 CSS 代码:

html 复制代码
<p class="f100">轻如鸿毛,重如泰山</p>
<p class="f200">轻如鸿毛,重如泰山</p>
<p class="f300">轻如鸿毛,重如泰山</p>
<p class="f400">轻如鸿毛,重如泰山</p>
<p class="f500">轻如鸿毛,重如泰山</p>
<p class="f600">轻如鸿毛,重如泰山</p>
<p class="f700">轻如鸿毛,重如泰山</p>
<p class="f800">轻如鸿毛,重如泰山</p>
<p class="f900">轻如鸿毛,重如泰山</p>
<style>
   p {
      font-family: 'Source Han Sans CN';
   }
   .f100 {
      font-weight: 100;
   }
   .f200 {
      font-weight: 200;
   }
   .f300 {
      font-weight: 300;
   }
   .f400 {
      font-weight: 400;
   }
   .f500 {
      font-weight: 500;
   }
   .f600 {
      font-weight: 600;
   }
   .f700 {
      font-weight: 700;
   }
   .f800 {
      font-weight: 800;
   }
   .f900 {
      font-weight: 900;
   }
</style>

结果可以看到明显的字重变化,而不是单纯的加粗和正常两种形态,如图 8-15 所示。

2021 12 19 22 41 24

也就是说,font-weight 要想真正发挥潜力,问题不在于 CSS 的支持,而在于是否存在对应的字体文件。如果没有对应的字体文件,我又想有多档字重效果,该怎么办呢?可以试试看看之后的 font-face 章节

具有近似姐妹花属性值的 font-style

font-style 表示文字造型是斜还是正,与 font-weight 相比,其属性值就要少很多,如下:

css 复制代码
font-style: normal;
font-style: italic;
font-style: oblique;

italic 和 oblique 这两个关键字都表示“斜体”的意思,差别在于:italic 是使用当前字体的斜体字体,而 oblique 只是单纯地让文字倾斜。如果当前字体没有对应的斜体字体,则退而求其次,解析为 oblique,也就是单纯形状倾斜。

我们平常在 Web 上使用比较多的中文字体,如“宋体”“微软雅黑”等,是没有专门的倾斜字体的,因此,从最终表现上来看 font-style:italic 和 font-style:oblique 是没有区别的。但是,对于一些英文字体,如“Georgia”,情况就不一样了,因为“Georgia”有一个专门设计的斜体字体文件。

html 复制代码
<p class="i">Georgia italic</p>
<p class="o">Georgia oblique</p>
<p>Georgia normal</p>
<style>
   p {
      font-size: 50px;
      font-family: Georgia;
   }
   .i {
      font-style: italic;
   }
   .o {
      font-style: oblique;
   }
</style>

结果可以看出,两个“倾斜”有着明显的不同,例如非常明显的字母 g,属性值为 italic 时长得像鱼钩,而为 oblique 时长得像糖葫芦,如图 8-16 所示。

2021 12 19 22 47 44

之所以会专门为一个字体设计倾斜字体,就是因为单纯倾斜的时候不好看,比方说上面的 “Georgia” 字体,当字号比较小同时文字倾斜的时候,字符会挤作一团,疏密不规则,可读性比较糟糕。相比之下,专门设计的“Georgia”斜体阅读体验就要好很多。再加上没有斜体字体时 italic 表现会和 oblique 一致,因此,我们在实际开发的时候,几乎没有任何理由需要使用 font-style: oblique。

不适合国情的 font-variant

font-variant 是一个从 IE6 时代就过来的 CSS 属性,实现小体型大写字母,两个属性值要么 normal,要么 small-caps,font-variant: small-caps 就是可以让英文字符表现为小体型大写字母。

代码示意如下:

html 复制代码
http://www.<span style="font-variant:small-caps">css-world.com</span>/

2021 12 19 22 51 50

也就是大小跟小写字母一样,但样式是大写。

font 属性

作为缩写的 font 属性

如果在一段 CSS 代码中发现了 font 属性,八九不离十就是利用 font 属性进行文本相关样式的缩写。可以缩写在 font 属性中的属性非常多,包括 font-style、font-variant、 font-weight、font-size、line-height、font-family 等。完整语法为:

css 复制代码
font: [ [ font-style || font-variant || font-weight ]? font-size [ / line-height ]? font-family ];

|| 表示或,? 和正则表达式中的 ? 的含义一致,表示 0 个或 1 个。仔细观察上面的语法,会发现 font-size 和 font-family 后面没有问号,也就是说是必需的,是不可以省略的。这和 background 属性不一样,background 属性虽然也支持缩写,但是并没有需要两个属性值同时存在的限制。

因此,如果你的 font 属性缩写无效,检查一下 font-size 和 font-family 这两个属性是否同时存在。例如,下面 CSS 语句看上去写了很多属性,实际却是无效的,因为缺字体:

css 复制代码
.font {
   font: normal 700 14px/20px;
}

而下面这个反而是有效的:

css 复制代码
.font {
   font: 14px '☺';
}

需要注意的是,font 缩写会破坏部分属性的继承性。举个简单的例子,假设你的页面行高是 20px,当你使用了下面的 CSS 后:

css 复制代码
.font {
   font: 400 30px 'Microsoft Yahei';
}

.font 元素的行高 line-height 属性值就被重置为了 normal,而不同浏览器上 line-height: normal 是不一样的,因此,在使用 font 缩写的时候,如果不设定行高值,一定会出现不兼容的问题。换句话说,如果你的 CSS 代码原本就没有 line-height 属性,使用 font 缩写反而是不推荐的。

另外,还有一个令人很头疼的问题,就是 font 缩写必须要带上 font-family,然而,原本真实继承的 font-family 属性值可能会很长,每次 font 缩写后面都挂一个长长的字体列表,有什么小技巧可以避免吗?

这里有两个方法。

  1. 我们可以随便找一个系统根本不存在的字体名占位,如字母 a,或者特殊一点,用笑脸表情 ☺,然后再设置 font-family: inherit 来重置这个占位字体。例如,我们想把字号和行高合并缩写,就可以这样:

    css 复制代码
    .font {
      font: 30px/30px '☺';
      font-family: inherit;
    }

    是不是有点拆东墙补西墙的感觉?这么做主要是因为 font 缩写不能使用 inherit 等全局关键字。

  2. 利用 @font-face 规则将我们的字体列表重定义为一个字体,这是兼容性很好、效益很高的一种解决方法,会在之后详细介绍

使用关键字值的 font 属性

font 属性除了缩写用法,还支持关键字属性值,这个怕是很多人都不知道的。其语法如下:

css 复制代码
font: caption | icon | menu | message-box | small-caption | status-bar;

如果将 font 属性设置为上面的一个值,就等同于设置 font 为操作系统该部件对应的 font,也就是说直接使用系统字体。

根据 W3C 官方维基的解释,以及我自己在 Windows 系统下的测试,各个关键字的含义如下。

  • caption:活动窗口标题栏使用的字体。
  • icon:包含图标内容所使用的字体,如所有文件夹名称、文件名称、磁盘名称,甚至浏览器窗口标题所使用的字体。
  • menu:菜单使用的字体,如文件夹菜单。
  • message-box:消息盒里面使用的字体。
  • small-caption:调色板标题所使用的字体。
  • status-bar:窗体状态栏使用的字体

使用示例:

css 复制代码
.menu {
   font: menu;
}

需要注意的是,使用关键字作为属性值的时候必须是独立的,不能添加 font-family 或者 font-size 之类的,这和 font 属性缩写不是一个路子。如果混用,例如:

css 复制代码
.menu {
   font: 14px menu;
}

则此时的 menu 是作为自定义的字体名称存在的,而不是表示系统的 menu 菜单字体。

实际上,font 关键字属性值本质上也是一种缩写,里面已经包含了诸如 font-size 等信息,如图 8-17、图 8-19 所示。

2021 12 19 23 10 10

2021 12 19 23 11 15

这 3 张图透露出不少重要的信息。从 Windows 下 Chrome 和 IE 浏览器部分关键字的字体和字号表现不一样可以看出,同一系统下浏览器的表现是有差异的。既然 font 关键字属性值的样式表现是跟着系统走的,那为何同一系统下不同浏览器的表现会不一样呢?

显然是某个浏览器出现了问题。后来,通过设置修改 Windows 系统相关控件的默认字体我发现,这次是 Chrome 浏览器拖了后腿。caption、icon、message-box 这 3 个关键字在 Windows 系统下的 Chrome 浏览器中似乎是无效的,并不会实时跟着系统字体走。

而所有这些问题在 Firefox 和 IE 浏览器中一个都没有,表现非常一致,非常符合预期,例如,修改“图标”字体为“思源黑体”,如图 8-20 所示,则所有文件名称全部变成了“思源黑体”,同时 font: icon 所在元素 font-family 计算值也成了“思源黑体”,如图 8-21 所示。

2021 12 19 23 14 51

考虑到 Chrome 浏览器的市场占有率,我们在使用 font 属性的时候,要避开 caption、icon 和 message-box 这 3 个关键字。

对于不同的操作系统,字体表现不一样,这是预料之中的,毕竟使用系统字体,而不同系统默认字体肯定是不一样的;然后字体大小也不一样。例如,在 Windows 下 Chrome 的 caption 字体大小 16px,而在 OS X 下却只有 13px。因此,在实际使用时,我们还需要在下面再设定一下 font-size 大小来保证一致性。照理讲,直接这样设置就可以了:

css 复制代码
html {
   font: menu;
   font-size: 16px;
}

但是,实际上 IE8 浏览器会莫名其妙地忽略这里的 font-size: 16px,因此,一般都是下面这样处理:

css 复制代码
html {
   font: menu;
}
body {
   font-size: 16px;
}

除了 caption、icon、menu、message-box、small-caption 和 status-bar,还有很多其他非标准的关键字,如 button、checkbox、checkbox-group、combo-box、desktop、dialog、document、field、hyperlink、list-menu、menu-item、menubar、outline-tree、password、pop-up-menu、 pull-down-menu、push-button、radio-button、radio-group、range、signature、tab、tooltip、window 和 workspace。不过,这些关键字浏览器大多不支持,尽管 Firefox 浏览器支持一部分,但是需要添加私有前缀 -moz-。例如:

css 复制代码
font: -moz-button;

因此,它们的实际应用价值不大。另外,WebKit 浏览器还支持其他关键字,如 font: -webkit-control,如下图所示。

2021 12 19 23 20 00

font 关键字属性值的应用价值

font 关键字属性值的价值如何呢?有没有合适的使用场景呢?有,并且相当适合!

目前,非常多网站的通用 font-family 直接就是:

css 复制代码
html {
   font-family: 'Microsoft YaHei';
}

知道问题在哪里吗?这样一设置,就意味着所有操作系统下的所有浏览器都要使用“微软雅黑”字体。假如说用户的 iMac 或者 macbook 因为某些原因安装了“微软雅黑”字体,那岂不是这些系统原本更加漂亮的中文字体就不能使用了?

于是,人们就会有这样的需求:希望非 Windows 系统下不要使用“微软雅黑”字体,而是使用其系统字体。怎么处理呢?

一种方法是可以试试使用非标准的 -apple-system 等关键字字体,具体方法如下:

css 复制代码
html {
   font-family: -apple-system, BlinkMacSystemFont, 'Microsoft YaHei';
}

这能够一定程度上满足我们的需求,但是毕竟是非标准的属性值,说不定哪天就被浏览器舍弃了,因此若非迫不得已,还是少用为妙。

顺便多说两句,实际上还真有标准的系统字体关键字,叫作 system-ui,使用示例如下:

css 复制代码
html {
   font-family: system-ui;
}

在我写这段内容的时候,仅 Chrome 浏览器支持它(从版本 56 开始),并且,根据我的在 Windows 电脑上的测试,Chrome 浏览器的 system-ui 指的就是“调色板标题”对应关键字 small-caption 使用的字体,有点儿出乎我的意料。

显然还有个更好的方法就是使用这里的 font 关键字,这是标准属性,10 年前浏览器就支持了,可以放心使用。CSS 代码如下(三选一即可):

css 复制代码
html {
   font: menu;
}
body {
   font-size: 16px;
}

html {
   font: small-caption;
}
body {
   font-size: 16px;
}

html {
   font: status-bar;
}
body {
   font-size: 16px;
}

这几个声明就可以让各个系统使用各自引以为傲的字体,推荐最短的:

css 复制代码
html {
   font: menu;
}
body {
   font-size: 16px;
}

最后,我要对 font 关键字属性值的用法做一个点评。

让网页的字体跟系统走,对设计师而言,其实是一个比较冒险的做法,因为最终呈现的字体是不可控的。举个例子,某女生非常喜欢可爱风格,于是就把她的电脑主题变成了非常可爱的风格,菜单栏的字体全部变成那种很可爱的字体,此时,font: menu 所呈现的就不是“微软雅黑”,而是这个用户定义的“可爱字体”,这可能不是设计师想看到的,因为往往会跟自己的网页设计风格不一致。但是,转念一想,万一这是用户想看到的呢?既然用户把自己的主题设为该字体,那就说明这个用户对这个字体并不排斥,而是喜欢。当她浏览网页的时候,发现就你的网站呈现出了她喜欢的那种字体,你说会不会给用户一种“你懂我的心”的感觉呢?对用户而言,反而成了一种情感化的设计!

另外,让网页的字体跟系统走,还有一个更加长远的好处。随着软件的不断发展,我们的操作系统的默认中文字体一定是越来越好看,如果网页的 font-family 定死为某个字体,用户就无法及时享受到新系统新字体带来的愉悦的视觉感受。举个例子,OS X 的默认中文字体其实已经变过好多次了,例如,你今天写了个页面,字体设置为很潮的“苹方”,过两年说不定会出来更好的名叫“梨方”的字体,我打包票,网站绝对是不会跟进的,因为大多数的线上项目维护都不会管 font-family 这种边边角角的事情,因此,就算很多年过去了,网站使用的依然是老字体。但是,如果使用的是 font 关键字属性值,就完全不会有这样的问题,网站字体能时时刻刻与时俱进。

// TODO CSS 世界读书笔记待完成

评论

阅读上一篇

CSS新世界概述及准备
2021-12-19 23:44:24

阅读下一篇

TypeScript 练手测试
2021-11-24 16:36:17
0%