Chapter 03
字体排版
02 章用明度对比把文字分成标题 / 正文 / 次要三档。这章补上另外两个维度:字号(由字阶推导)和字重——并揭示一个反直觉的事实:字号、行长、行高三者互相牵制,任何一个都不能孤立地定。
本章你将建立的 schema
- 字号来自"模块化字阶":一个基准 × 一个比例生成的几何数列,而非随手取值
- 行长(measure)有客观上限(45–75 字符),根因是眼睛"扫视 + 回扫"的阅读机制
- 行高不是常数,是个关系:随行长变宽而增大、随字号变大而减小
- 层级用字号 / 字重 / 明度三个维度拉开,而不是靠堆字体——一套界面 1–2 个字体足矣
排版是工程师最容易"凭手感"的地方:字号随便填、行高用默认、文字撑满容器。本章把这三个动作各自换成一条可推导的规则,并说清它们为什么互相约束。
3.1模块化字阶:字号是算出来的
一套界面的字号,来自一个基准字号 × 一个固定比例(ratio)生成的几何数列;只在这条数列里取档,不随手填值。
随手取字号(14 / 15 / 16 / 18 / 20 / 22)会得到一堆“大小相近”的尺寸:既做不出 01 章要的清晰对比,又难维护(下次该用 17 还是 18?)。模块化字阶把选择从无限收敛到 6–8 个明显有别的档,每次"选一个",而不是"调一个"。
底层机制(比文档深一层):字阶是一个几何数列——size(n) = base × ration。关键在于几何而非等差。等差(14, 16, 18, 20,每档 +2)在小字端差异够、在大字端却几乎看不出(20 比 18 大不了多少);而人眼对尺寸的感知是相对的(02、01 章反复出现的同一条规律)——它在意"大了几倍",不在意"大了几像素"。几何数列让每相邻两档的比例恒定,于是从最小到最大,每一步的"变大感"处处一致。比例本身决定对比强度:
| 比例 | 名字 | 气质 / 适用 |
|---|---|---|
| 1.2 | minor third | 温和,层级密;适合信息密集的后台 / 仪表盘 |
| 1.25 | major third | 通用默认,正文产品的稳妥选择 |
| 1.333 / 1.5 | perfect fourth / fifth | 戏剧化,标题和正文拉得很开;适合营销页 / 落地页 |
/* base 16px,ratio 1.25:取整后落地为 6 档 */
:root {
--text-xs: 0.8rem; /* 12.8 → 元数据 / 角标 */
--text-sm: 0.875rem; /* 14 → 次要文字 */
--text-base:1rem; /* 16 → 正文 */
--text-lg: 1.25rem; /* 20 → 小标题 */
--text-xl: 1.5625rem;/* 25 → 区块标题 */
--text-2xl: 1.953rem; /* 31 → 页面主标题 */
}
/* 用 token 取档,而不是写死像素:font-size: var(--text-lg); */
与下一节的关系:字阶定了"字能多大";但一行能多宽,由另一条完全不同的规律决定——人眼怎么读。
3.2行长(measure):被低估的可读性开关
一行正文的理想长度是 45–75 个字符(约 66 最佳,中文约 30–40 字);全宽撑满容器,是最常见的可读性失败。
工程师的默认动作是让文字 width: 100% 撑满父容器。在宽屏上,这会产生每行上百字符的超长行——读起来累,却很少有人意识到这是排版问题而非内容问题。限制行长是单条性价比最高的可读性改进。
底层机制(比文档深一层):阅读不是匀速扫过一行,而是扫视(saccade)+ 回扫(return sweep)的跳跃过程——眼睛在一行内分几次小跳(每次落点叫注视点),读到行尾再做一次大幅度的"回扫",跳回下一行的行首。行太长(> 75 字符)时,回扫的水平跨度太大,眼睛容易跳错行或找不到下一行起点,于是反复重读、疲劳;行太短(< 45 字符)时,回扫过于频繁、节奏被打断,且断词增多。66 字符是这两个失败之间的甜点。CSS 里用 ch 单位(1ch ≈ 一个 "0" 的宽度)或 max-width 直接限制。
实例:同一段话,全宽 vs 限宽
行长是排版里最容易被工程师忽略的一个变量,因为代码里让文字撑满容器是最省事的默认。可是当一行文字横跨整个宽屏,眼睛在行尾做回扫时就要跨越很大的水平距离,很容易跳错行或者干脆找不到下一行的开头,于是反复重读,阅读变得疲惫——内容没问题,是行太长了。
行长是排版里最容易被工程师忽略的一个变量,因为代码里让文字撑满容器是最省事的默认。可是当一行文字横跨整个宽屏,眼睛在行尾做回扫时就要跨越很大的水平距离,很容易跳错行或者干脆找不到下一行的开头,于是反复重读,阅读变得疲惫——内容没问题,是行太长了。
同一段文字,下面那块只加了一句 max-width: 34em。回扫的跨度被收住,读起来明显轻松。英文正文用 max-width: 66ch,中文因为字宽更大,常用 max-width 配 em(约 32–38em)。
与下一节的关系:行长决定了回扫的跨度;而帮眼睛在回扫时"锁定"下一行的,是行与行的间距——行高。这就是为什么行高必须随行长一起定。
3.3行高(leading):一个关系,不是一个常数
行高是行与行的垂直间距;正文约 1.5–1.6,标题更紧(1.1–1.3)。它随行长变宽而增大、随字号变大而减小。
浏览器默认行高(normal ≈ 1.2)对正文太挤,但用到大标题上又正好。很多人把行高当成一个全局常数设一次了事——这正是正文发闷、标题发散的根因。行高的正确形态是一个关系。
底层机制(比文档深一层):行高的功能,是帮眼睛在回扫(§3.2)时把目标行和相邻行区分开、避免串行。由此推出两条牵制关系:
- 行越长,行高要越大。 回扫的水平跨度大时,眼睛更难锁定下一行,需要更大的垂直间距来制造区分度。所以宽栏配大行高,窄栏可以收紧。
- 字号越大,行高比例要越小。 行高是字号的倍数;字号大时,行内空白的绝对量已经很充足,再乘 1.6 就过散。所以大标题用 1.1–1.2,正文用 1.5–1.6,很小的说明文字甚至 1.6 以上。
讲成一句话
讲成一句话
正文用接近默认的行高会显得拥挤,几行叠在一起,眼睛在回扫时容易粘到相邻行,读起来发闷、费力。
同一段正文,把行高提到 1.6,行与行之间有了清晰的呼吸间隙,回扫更容易命中下一行,阅读节奏顺畅了。
一个全宽(约 100 字符 / 行)的段落,把行高从 1.5 提到 1.8,会更好读还是更糟?
先想 10 秒再展开
更好读一点点,但治标不治本。行太长是病根,加大行高只是部分缓解回扫串行。正确顺序是先把行长收到 66 字符以内,再据此定一个 1.5–1.6 的行高。两个旋钮要一起拧,这正是图 3.3 的牵制关系。
与下一节的关系:字号、行长、行高解决了"单一字体怎么排好";但层级还差一个维度——而新手往往用错维度去补它。
3.4用字重对比建层级,而不是堆字体
层级靠字号、字重、明度三个维度拉开;一套界面用 1–2 个字体家族足够,靠换字体来"制造变化"几乎总是帮倒忙。
新手为了让界面"有层次 / 有变化",倾向于用很多字体。结果是杂乱(直接违反 01 章的重复 / 一致),而且层级并没有变清楚。真正的层级来自字号、字重、明度的差异,这三者在同一个字体家族里就能拉满。
底层机制(比文档深一层):一个成熟的字体家族自带多个字重(400 / 500 / 600 / 700)和经过调校的字间距——这已经提供了充足的对比维度。换字体引入的不是"层级"而是"风格冲突":不同字体的 x-height(小写字母高度)、字宽、笔形气质各不相同,混排需要专业训练才不乱。字重对比(400 正文 vs 600/700 标题)是免费且零风险的层级来源,它正是 01 章"对比的三个发力点"在排版上的落地。要做字体搭配时,限制为两个、且要有明确反差(一个 serif 标题 + 一个 sans 正文),或干脆用同一个超级家族(superfamily)的不同子集。
右边只用了一个字体家族,层级却比左边清楚得多——靠的是字号(18/14/12)、字重(700/400/400)、明度(#111/#374151/#9ca3af)三者叠加。这三档正好接上 02 章的灰阶文字三层。
3.5字距、垂直节奏与渲染细节
三个把"还不错"推到"精致"的细节。
字距(letter-spacing / tracking)
机制:字体在设计时按"正文尺寸"优化了字间距。放大到大标题尺寸时,字间距显得过松,所以大标题常用 letter-spacing: -0.01em ~ -0.02em 略收紧,显得紧实有力。反过来,全大写英文(uppercase)和很小的字,字母过于拥挤、难辨,需要letter-spacing 适当放宽(如 0.05em)。中文一般不调字距。
垂直节奏(vertical rhythm)
机制:让所有垂直间距(段落间距、标题上下间距、行高叠加)都落在一个统一基数(如 4px)的倍数上,页面纵向就有一致的"节拍"。这条直接通向 04 章的间距阶——排版的间距和布局的间距,应该用同一套尺度。
字体加载
机制:Web 字体下载需要时间。font-display: swap 让浏览器先用后备字体渲染、字体到位后替换,避免 FOIT(字体加载完前文字隐形);代价是 FOUT(短暂闪烁 / 布局偏移)。零延迟的选择是系统字体栈(system-ui, -apple-system, ...)。可变字体(variable font)的 opsz(optical sizing)轴还能让同一字体在不同字号下自动微调字形,小字更清晰、大字更优雅。
3.6收束:排版的决策顺序
把三角牵制(图 3.3)落成一个固定顺序:
- 选基准字号(正文,通常 16px)。
- 选比例生成字阶(密集后台 1.2,通用 1.25,营销页 1.333+),取整成 6 档。层级从这条数列里选档。
- 限行长(正文 ≤ 66 字符 / 中文 ≤ ~38em),收住回扫跨度。
- 据行长和字号定行高(正文 1.5–1.6、标题 1.1–1.3;宽栏偏大、大字偏小)。
- 用字重补层级(400 正文 / 600–700 标题),而不是换字体。
到这里,02 的明度三层 + 03 的字号字重,共同把"文字层级"变成了完全可推导的东西——这正是 01 章那句"层级是目的"在文字上的兑现。下一章把同样的"用有限尺度做选择"搬到空间和布局上。
§本章 self-check
先合上教程,把答案写下来再展开对照。
- 为什么字阶要用几何数列(×1.25)而不是等差(+2px)?等差在哪一端会出问题?
- 行长 45–75 字符的上限,根因是什么阅读机制?用"扫视 / 回扫"解释,而不是"看着舒服"。
- 为什么大标题用行高 1.15、正文用 1.6?把这两条牵制关系(行长↔行高、字号↔行高)各说一句。
- 想给界面"增加层次",换三种字体和用一种字体配三档字重 / 明度,哪个对?为什么?
答案(先做完再展开)
- 因为人眼对尺寸的感知是相对的(在意"大几倍"而非"大几像素")。等差在大字端比例差太小,层级塌掉(20 vs 18 几乎无差);几何让每相邻两档比例恒定,处处有清晰对比。
- 阅读是"扫视 + 回扫"的跳跃。行太长,行尾回扫到下一行行首的水平跨度过大,易跳错行 / 找不到起点,反复重读;行太短则回扫过频、节奏断、断词多。66 字符是平衡点。
- ① 字号越大,行内空白绝对量已足,行高比例要小,否则发散——所以标题 1.15;② 行长越宽,回扫越难锁定下一行,需更大行高区分——正文较宽,用 1.6。
- 用一种字体 + 三档字重 / 明度。层级来自字号 / 字重 / 明度的差异,同一家族即可拉满;换字体引入的是风格冲突而非层级,且违反一致性。
给一篇博客文章页定一套完整的排版规格
目标:一篇长文阅读页(标题 + 副标题 + 多段正文 + 二级小标题 + 引用块 + 图注)。只用本章规则,定出:① 基准字号与比例,列出用到的字阶档;② 正文的 max-width 与行高;③ 标题 / 小标题的字号(取自字阶)、字重、行高、字距;④ 图注 / 元数据这类最小字的字号与对比下限(回扣 02 章)。每一项都要说清依据,不能只给数值。
提示(卡住再展开)
长文阅读优先可读性,选 base 16–18、ratio 1.25。正文 max-width 取 ~38em(中文约 34–38 字)、行高 1.65(宽栏 + 长文,偏大)。主标题取字阶高两档(如 31–39px)、字重 700、行高 1.15、字距 -0.02em(大字收紧)。小标题取中间档(20–25)、字重 600、行高 1.3。图注 / 元数据用 13px、明度 #6b7280(确认 ≥ 4.5:1)。引用块靠"缩进 + 左边线 + 次要明度"区分,而不是换字体。检查:整页只出现一个字体家族、垂直间距都落在 4px 的倍数上。