Skip to content

字体 ​

元信息 ​

  • 目标:掌握 font-family 的多值回退机制,理解"渲染发生在客户机"带来的安全字体问题;会用 @font-face 以 woff2 引入 web font 及字体子集,认识可变字体与图标字体这两类"字体技术的新用法"
  • 关键概念:字体族、font-family、安全字体、@font-face、字体子集、可变字体、图标字体
  • 关联阶段:cp1
  • 常见误区:以为指定了字体名客户机就一定能显示(渲染用的是浏览者电脑上的字体库);以为中文字体可以整库附带进网页(体积高达数兆,须子集化或在线服务);以为图标放大必然模糊(字体是矢量图形,分辨率无关,模糊的是位图方案)

前置 ​

  • CSS基础:规则语法与选择器
  • 想直观认识"字体库",可打开系统的字体目录看看(Windows 在 C:\Windows\Fonts),每个文件就是一种字体

动机:页面在谁的电脑上渲染 ​

在本周主线一「排印模式」的地图上,本篇管字符的物质形态:字符在屏幕上的样子叫字形(glyph),字形住在字体库里;而 font-family 这条指令要落到客户机执行——渲染发生在浏览者的电脑上,字形要用对方电脑上的字库来兑现。

第 3 周的结论在这里兑现:HTML 与 CSS 只是描述,渲染发生在浏览者的客户机上。你写 font-family: 微软雅黑,只是下了道指令;执行时浏览器要在对方的电脑上找到微软雅黑这个字体库,用它里面的字形画字。找不到,就出问题——这就是本篇全部内容的出发点。

font-family:多值回退机制 ​

<!DOCTYPE html>
<html>
<head>
  <title>Parcel Sandbox</title>
  <meta charset="UTF-8" />
  <link rel="stylesheet" href="/styles.css" />
</head>
<body>
  <h1>Hello world</h1>
</body>
</html>

浏览器按从左到右的顺序逐个尝试:

  1. 先找"微软雅黑"这个具体字库,在里面找"头条"两个字的字形
  2. 失败则退而尝试下一个值(英文字库里没有汉字,就会退)
  3. 全部失败,字符显示为缺字符方框(俗称"豆腐块")

所以 font-family 惯例是具体在前、宽泛在后,最后一项写字体族兜底。

字体族与安全字体 ​

字体族(font family)不是某个具体字体,而是符合某种特性的多种字体的集合,如 serif(衬线体)、sans-serif(非衬线体)。族只能保证"字符能正确显示",不保证显示成你想要的样子。

哪些字体"一定找得到"?三重现实决定了没有绝对答案:

  • 各操作系统预装的字体不同:微软雅黑是 Windows 专有,macOS、手机上没有
  • 商业字体有版权,客户机多半没买
  • 你为中文指定的英文字体,字库里根本不含汉字字形

在各平台都大概率存在的字体,习惯上称安全字体。现代做法是直接采用社区维护的系统字体栈(如 modernfontstacks.com 列出的各风格字体栈),比手挑更稳。

设计师的电脑往往装满了华丽字体,而浏览者的电脑可能"一穷二白"——在别人机器上验证,是每个 Web 设计师的必修课。

@font-face:把字体当资源下载 ​

客户机上没有的字体,能不能像图片一样下载过去?@font-face 就是干这个的:把字体文件当作一项资源,浏览器发现页面用到它,就从 Web 服务器下载到客户端再渲染(一次 HTTP 请求,见HTTP协议)。

css
@font-face {
  font-family: "myfont";
  src: url("myfont.woff2") format("woff2");  /* woff2 是当代唯一需要准备的格式 */
  font-display: swap;   /* 先用回退字体显示,字体就绪后替换,避免文字"隐形" */
}

h1 { font-family: myfont, sans-serif; }

早年这段代码要同时准备 eot/woff/ttf/svg 好几份文件伺候各路旧浏览器,如今 woff2 一份走天下。font-display: swap 值得记住:字体下载期间先用回退字体顶上,就绪后再换装,用户不会盯着一片空白。

中文字体:体积问题与子集化 ​

西文字体几十 K,中文字体动辄数兆——GB 汉字每个都要一套字形。整库附带进网页不可行,工程上有两条路:

  • 子集化:只抽取页面实际用到的字符生成小字体文件(标题、logo 等少量文字的标准做法),社区工具如 iconfont.cn 的在线 webfont 上传文字即可定制
  • 按需分片:把字体拆成多份,每份用 unicode-range 声明生效的字符区间,浏览器只下载页面命中的那几片,并行下载迅速拼齐

在线字体服务(如 Google Fonts,国内可自托管或用镜像)把上述两件事都做在了服务端。使用时留意网络可达性:字体服务慢,页面文字就慢,DevTools 的 Network 面板一眼可见(见课程工具箱·devtools)。

可变字体:一份文件,连续可调 ​

传统上一个字重一份文件:Regular 一份、Bold 一份。可变字体(variable font)把整条变化轴压缩进一份文件,字重从 100 到 900 连续可调,还有宽度、斜度等轴——字体也变得可编程了,正与本周"CSS 是可编程语言"的主轴同构:

css
@font-face {
  font-family: "Inter Variable";
  src: url("inter-variable.woff2") format("woff2-variations");
  font-weight: 100 900;   /* 声明轴范围,一个文件覆盖所有字重 */
}

.light { font-weight: 200; }
.heavy  { font-weight: 800; }

体验各种可变字体的轴:v-fonts.com

图标字体:用字体技术承载图标 ​

字体库的本质是一组矢量图形与字符编码的对应表——主线一"字符为最小单元"落到物质层就是这张表:每个编码查到一个矢量形状。既然如此,图标为什么不能占几个编码位?图标字体(icon font)正是这么做的:把图标做成字形,继承字体的一切优点——矢量缩放不模糊、分辨率无关(对比位图图标放大后的锯齿)、颜色大小全由 CSS 控制:

css
@font-face { font-family: "iconfont"; src: url("iconfont.woff2") format("woff2"); }
.icon { font-family: iconfont; }
.home::before { content: "\e600"; }   /* 编码位 e600 对应房子图标 */

用第三方图标库的好处是共享设计成果;只用少量图标时不必引入整库,iconfont.cn 等平台支持在线挑选、只打包用到的子集。成熟的图标库还提供 npm 包等多种接入形式(如 tabler-icons)。

更新的答案是 SVG 图标:直接内联 SVG 标记,继承矢量与 CSS 可控的全部优点,还规避了字体方案的缺陷(不必外链字体文件、支持多色)。本站用的就是 SVG 图标。图标字体没有消亡,但新项目多从 SVG 起步。

小结 ​

  • 渲染发生在客户机:font-family 靠多值回退兜底,末位必留字体族
  • 安全字体是跨平台的最大公约数;系统字体栈是现代默认
  • @font-face 把字体当资源下载,woff2 一份格式走天下,font-display: swap 防文字隐形
  • 中文字体的体积问题靠子集化与 unicode-range 分片解决
  • 可变字体一份文件连续调字重;图标字体是字体技术的挪用,SVG 图标是更新答案

延伸 ​