Appearance
字体
元信息
- 目标:掌握 font-family 的多值回退机制,理解"渲染发生在客户机"带来的安全字体问题;会用 @font-face 以 woff2 引入 web font 及字体子集,认识可变字体与图标字体这两类"字体技术的新用法"
- 关键概念:字体族、font-family、安全字体、@font-face、字体子集、可变字体、图标字体
- 关联阶段:cp1
- 常见误区:以为指定了字体名客户机就一定能显示(渲染用的是浏览者电脑上的字体库);以为中文字体可以整库附带进网页(体积高达数兆,须子集化或在线服务);以为图标放大必然模糊(字体是矢量图形,分辨率无关,模糊的是位图方案)
动机:页面在谁的电脑上渲染
在本周主线一「排印模式」的地图上,本篇管字符的物质形态:字符在屏幕上的样子叫字形(glyph),字形住在字体库里;而 font-family 这条指令要落到客户机执行——渲染发生在浏览者的电脑上,字形要用对方电脑上的字库来兑现。
第 3 周的结论在这里兑现:HTML 与 CSS 只是描述,渲染发生在浏览者的客户机上。你写 font-family: 微软雅黑,只是下了道指令;执行时浏览器要在对方的电脑上找到微软雅黑这个字体库,用它里面的字形画字。找不到,就出问题——这就是本篇全部内容的出发点。
font-family:多值回退机制
浏览器按从左到右的顺序逐个尝试:
- 先找"微软雅黑"这个具体字库,在里面找"头条"两个字的字形
- 失败则退而尝试下一个值(英文字库里没有汉字,就会退)
- 全部失败,字符显示为缺字符方框(俗称"豆腐块")
所以 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 图标是更新答案
延伸
- 字体如何阻塞渲染、
rel="preload"提前拉取,见关键渲染路径 - 字号行距等排印规则见排印
- 开源中文字体推荐:霞鹜文楷、得意黑 Smiley Sans