这不是字体清单,是配对与排版的推理规则。
design-styles.md已经给了 40 种风格各自的字体名;本文回答的是「为什么这样配」「拿到任意内容怎么推导出字号/行长/字重」。目标:同一个风格标签,落到不同内容上,能推导出不同的排印结果,而不是每次都抄同一套字号。前置纪律不变:有 design context 先 lift 用户自己的字体(见
design-context.md),本文的一切只在「用户没有字体规范」时启用。
拿到内容后按这个顺序推,每一步都由上一步决定,不许跳到「直接选个好看的字体」:
design-styles.md 的安静/中性/大胆三档)→ 决定字体配对的对比度来源为什么:先选字体名的做法,会让「内容是什么」对排印零影响,这正是千人一面的病根。
字号不是拍脑袋,是从正文字号乘一个固定比例逐级推出来的。比例决定页面的「戏剧性」:
| 比例 | 名字 | 性格 | 适用 |
|---|---|---|---|
| 1.2 | 小三度 | 平缓、层级多而不吵 | dashboard、文档站、信息密集 UI |
| 1.25 | 大三度 | 通用、安全 | 大多数网页、产品落地页 |
| 1.333 | 纯四度 | 标题明显跳出 | editorial 长文、营销页、报告 |
| 1.5 | 纯五度 | 戏剧性、层级极少 | 大字报、slides、hero 一屏一句 |
推导规则:正文定 16-18px(中文正文建议 17-18px,汉字笔画密、同字号比西文显挤),然后按比例上推标题、下推 caption。层级超过 5 档就是失控,砍掉。
| 档位 | 1.25 比例下的参考值 | 用途 |
|---|---|---|
| caption | 12-13px | 图注、meta 信息、EXIF 式小字 |
| small | 14px | 辅助说明、表格 |
| body | 16-18px | 正文,一切的基准 |
| h3 | ≈1.25x | 小节标题 |
| h2 | ≈1.56x | 章节标题 |
| h1 | ≈1.95x | 页面标题 |
| display | 3x-8x,脱离音阶自由发挥 | hero 巨字,由版面而非音阶决定 |
流式字号写法(display 档必用,避免大屏死板小屏溢出):
/* clamp(最小值, 首选值, 最大值):首选值 = 基础rem + 视口系数 */
h1 { font-size: clamp(2rem, 1.2rem + 3.5vw, 4.5rem); }
.display { font-size: clamp(3rem, 1rem + 9vw, 9rem); }
/* 正文不要 clamp 出大幅波动,16→18 的窄区间即可 */
body { font-size: clamp(1rem, 0.95rem + 0.3vw, 1.125rem); }
为什么 display 脱离音阶:hero 巨字是版面元素不是文本层级,它的尺寸由「占视口几成」决定,用 vw 推导比用音阶推导更合理。
| 语言 | 舒适区 | CSS 实现 |
|---|---|---|
| 西文正文 | 45-75 字符,最佳 66 | max-width: 65ch |
| 中文正文 | 一行 22-38 字,最佳 28-32 字 | max-width: 36em(em 随字号缩放) |
| 图注/侧栏 | 更短,中文 15-20 字 | 窄容器天然限制 |
为什么中文更短:汉字是无空格的致密方块字,同宽度下承载的信息量明显高于西文,同样的眼跳次数中文读进更多内容,行太长回行时找不到下一行开头。
行高不是常数,是行长的函数。行越长,眼睛回行距离越远,需要更大的行间距当「轨道」:
| 场景 | 西文 | 中文 |
|---|---|---|
| display 大字(1-2 行) | 0.95-1.1 | 1.1-1.25 |
| 标题(h1-h3) | 1.1-1.3 | 1.3-1.4 |
| 短行正文(<30 字/行) | 1.4-1.5 | 1.6-1.7 |
| 长行正文(接近上限) | 1.6 | 1.8-2.0 |
中文全线比西文高 0.2 左右:汉字是满格方块,没有西文小写字母之间的天然空隙,行距不足会糊成一片。
h1, h2, h3 { text-wrap: balance; } /* 标题多行时各行长度均衡,消灭孤字行 */
p { text-wrap: pretty; } /* 正文消灭行尾孤词(西文效果明显,中文轻微) */
balance 只用于 ≤4 行的标题(算法限制 6 行且有性能成本);pretty 全局给正文无副作用。
配对的三种对比度来源,配之前先想清楚用哪种:
| # | 配对(display + body) | 配对逻辑 | 温度 | 获取 |
|---|---|---|---|---|
| 1 | Newsreader + Geist | 形式对比:屏显优化的过渡衬线,x-height 高、与 Geist 咬合好;Fraunces 的正牌平替 | 安静 | Google Fonts / Vercel 官方仓库 |
| 2 | Source Serif 4 + Source Sans 3 | 同族咬合:Adobe 同设计系统,字高字重节奏完全对齐,报告和文档零翻车 | 安静 | Google Fonts |
| 3 | EB Garamond + IBM Plex Sans | 时代对比:16 世纪法国老衬线 x 2017 理性 grotesque,差 400 年的张力;注意 Garamond x-height 低,同行混用需字号补偿(+8% 是经验起点,系统解法用 font-size-adjust,见第 4 章) |
安静·文气 | Google Fonts |
| 4 | Lora + Hanken Grotesk | 形式对比:Lora 笔刷感衬线中等反差,屏显耐看;Hanken 是 Söhne 气质的开源近亲 | 中性 | Google Fonts |
| 5 | Instrument Serif + Geist | 形式对比:只有 400 一档字重,天生 display-only,正文必须交给 sans。⚠️ 正在被 AI 工具用烂的路上,2026 年慎用于「想显得独特」的场合 | 中性 | Google Fonts |
| 6 | Schibsted Grotesk + Source Serif 4 | 反转结构:grotesque 当 display、衬线当正文,媒体感;Space Grotesk 泛滥后的平替(挪威 Schibsted 报业定制开源,带新闻血统) | 中性 | Google Fonts |
| 7 | Bricolage Grotesque + Newsreader | 形式对比:Bricolage 的 ink trap 和不规则细节在大字号才显现,天生 display;配安静衬线正文形成粗野 x 文雅 | 大胆 | Google Fonts |
| 8 | Archivo(Expanded/Black)+ Inter | 大字报结构:Archivo 宽体黑重压场,Inter 只当 14-16px 正文工蜂(这是 Inter 的正确用法,见反模式) | 大胆 | Google Fonts |
| 9 | Cormorant Garamond + Work Sans | 高反差奢侈感:Cormorant 笔画极细,必须 ≥40px 才成立,小字号笔画会断;适合时尚/太空图录风 | 大胆 | Google Fonts |
| 10 | Geist Mono / JetBrains Mono + Geist | 等宽当主角:命令行感、工程感;等宽只用于标签/编号/代码,整段正文用等宽是灾难(行长膨胀 30%) | 中性·技术 | Vercel / JetBrains 官方,均 OFL |
已被用烂名单(AI 生成页面的指纹,用了等于自曝):
| 烂大街 | 为什么烂 | 平替 |
|---|---|---|
| Fraunces 当 display | 2023-2025 所有 AI 设计工具的默认「有品位」选项 | Newsreader、Libre Caslon Text |
| Inter 当 display | Inter 是为 UI 小字设计的,大字号下匀质无表情 | Archivo、Anton、Schibsted Grotesk |
| Space Grotesk | 「科技感」的偷懒答案,泛滥于加密/AI 落地页 | Schibsted Grotesk、Familjen Grotesk |
| Playfair Display | 「优雅」的偷懒答案,婚礼请柬既视感 | Cormorant(更极端)、DM Serif Display(更憨) |
西文排印有百年成熟工具链,中文没有。AI 设计工具在中文上集体摆烂(默认交给系统字体、直接套西文规则),这里是差异化所在。
| 字体 | 类别 | 气质 | 温度 | 获取 |
|---|---|---|---|---|
| 思源宋体(Noto Serif SC) | 宋体 | 出版正统、7 字重齐全,Heavy 可当 display | 安静-中性 | Google Fonts,OFL |
| 思源黑体(Noto Sans SC) | 黑体 | 中文界的 Inter:可靠、无表情,当默认正文没错但没个性 | 全温度兜底 | Google Fonts,OFL |
| 霞鹜文楷 | 楷体 | 手写温度、亲切,适合文艺/教育/个人博客正文与引文 | 安静·暖 | GitHub lxgw/LxgwWenKai,OFL |
| 霞鹜新晰黑 | 黑体 | 比思源黑更瘦更透气的屏显黑,正文久读不累 | 安静 | GitHub lxgw/LxgwNeoXiHei |
| 得意黑 Smiley Sans | 斜黑体 | 中文世界罕见的原生斜体,运动感、标题专用;正文用它会晕 | 大胆 | GitHub atelier-anchor/smiley-sans,OFL |
| 汇文明朝体 | 旧字形明朝 | 老印刷铅字气、复古出版,适合书封/文化类 display | 中性-大胆·复古 | 猫啃网/GitHub,免费商用 |
| 京华老宋体 | 老宋 | 笔画方硬的标题宋,报头感 | 大胆·复古 | 猫啃网,免费商用 |
| 源流明体/源样明体 | 明朝体(繁向) | 思源宋改刻,保留传统字形细节,繁体内容首选 | 安静·古典 | GitHub ButTaiwan,OFL |
| 未来荧黑 Glow Sans | 几何黑 | 思源黑衍生的现代几何黑,多宽度(Compressed 可做窄长 display) | 中性-大胆·现代 | GitHub welai/glow-sans,OFL |
| MiSans / HarmonyOS Sans / OPPO Sans | 厂商 UI 黑 | 比思源黑略有性格的 UI 黑,App 原型合适 | 中性 | 各厂官网,免费商用 |
选型推理:正文只在宋/黑/楷里选(其余都是 display 字体,整段用会累);display 想要个性时才去动得意黑/老宋/明朝体。中文字体一个顶西文十个(单文件 5-15MB),一页最多两个中文字体家族,为加载和统一性两个原因。
fallback 链是第一杠杆:中文字体自带的西文字符普遍难看(思源黑的拉丁字母呆板),把西文字体放在前面,拉丁字符和数字被它接住,汉字自动落到后面的中文字体:
/* 西文在前,中文在后,系统中文兜底,泛型收尾 */
font-family: "Geist", "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;
/* 衬线同理 */
font-family: "Newsreader", "Noto Serif SC", "Songti SC", serif;
为什么这个顺序:font-family 是逐字符匹配的,西文字体不含 CJK 码位,汉字自然穿透到中文字体。反过来写(中文在前)西文字符全被中文字体吃掉,等于白配。
字号补偿:同字号下西文小写视觉偏小(x-height 只占字身一半,汉字占满)。两种解法:
/* 解法一:font-size-adjust 让 fallback 字体按 x-height 归一(Chrome 127+/FF/Safari 17+) */
:root { font-size-adjust: from-font; }
/* 解法二:选 x-height 高的西文体(Geist/Inter/Source Sans 都高),混排天然齐 */
baseline 对齐:中西 baseline 不一致时症状是英文单词在中文行里「下沉」。优先换 x-height 更高的西文体;个别 display 场景用 vertical-align: -0.02em~-0.06em 微调西文 span,正文别这么修(维护成本大于收益)。
数字规则:数字一律走西文字体(fallback 链已保证),数据表格必须加 font-variant-numeric: tabular-nums,否则 1 和 8 宽度不同,列会抖。
中英之间不加空格:这是本仓库规范(花叔明确不用盘古之白),靠 fallback 链的字体本身留白,不靠手动敲空格。
中文字形没有 italic 传统,浏览器遇到 font-style: italic 会机械倾斜汉字(faux italic),笔画变形、极丑。强调手段替换表:
| 西文习惯 | 中文替代 | CSS |
|---|---|---|
| italic 强调 | 换字重 | font-weight: 600(前提:字体真有这档字重) |
| italic 书名/引用 | 底色高亮 | background: linear-gradient(transparent 60%, #FFE9A8 60%) 荧光笔式 |
| italic 引文块 | 换字体 | 引文整段换霞鹜文楷,楷体本身就是中文的「引用语气」 |
| italic 专名 | 颜色/着重号 | text-emphasis: dot(着重号,中文原生强调,支持度已可用) |
保险丝:font-synthesis: none; 全局禁掉合成斜体和合成加粗,宁可不强调也不接受变形字。
| 规则 | 做法 | 为什么 |
|---|---|---|
| 引号 | 直角引号「」『』,不用弯引号 "" | 弯引号在中文字体里是全角占位但形状是西文的,视觉漂浮;「」是本仓库硬规范 |
| 避头尾 | line-break: strict; |
禁止句号逗号出现在行首、开引号出现在行尾,这是中文排版的底线 |
| 标点悬挂 | hanging-punctuation: first allow-end;(仅 Safari);跨浏览器用 text-indent: -0.5em 处理段首开引号 |
段首的开引号不悬挂会让首行看起来缩进了半格,视觉左边缘不齐 |
| 连续标点挤压 | font-feature-settings: "halt";(行尾挤压)或 "palt"(全比例宽度,需配合 letter-spacing) |
全角标点连排(如「)。」)会出现一个半字宽的空洞,halt 收窄它 |
| 场景 | 区间 | 为什么 |
|---|---|---|
| 正文 | 0 至 0.05em | 微加字距提升透气度;超过 0.05em 词的完形被打散,读速下降 |
| 标题(24-48px) | 0 | 汉字方块字距天然均匀,不需要西文式 tracking 调整 |
| display 巨字(>60px) | -0.02em 至 0 | 大字号下字面之间的空隙被放大,微收更紧凑;再负就笔画相撞 |
| 全大写西文小标签 | 0.08-0.15em | 唯一需要大正字距的场景,且只对西文大写生效 |
中文永远不要用西文那套「display 收 -0.05em」:汉字是满格设计,负字距直接笔画打架。
中文没有西文那种 Ultra Thin 到 Black 的 display 字体生态,大字的戏剧性要靠推理制造:
writing-mode: vertical-rl 做书脊式标题、诗词、目录,西文做不到;注意竖排里的西文和数字用 text-orientation: upright 或 text-combine-upright: all(两位数字合体直立)| ❌ 反模式 | 为什么错 |
|---|---|
| 全场 Inter(display+body 一把梭) | Inter 是 UI 小字工具,当 display 匀质无表情;这是「AI 生成页面」的头号指纹 |
中文交给 sans-serif 系统默认 |
Windows 落到中易宋体/雅黑、macOS 落到苹方,同一页面跨设备完全两张脸,等于没做设计 |
| faux italic / faux bold | 浏览器合成变形:斜体扭曲汉字,合成加粗把笔画糊成墨团;用 font-synthesis: none 断根 |
| 大标题字距过松 | 西文 display 需要收紧(大字号空隙被放大),AI 常反着来加 +0.05em,标题松垮像临时占位 |
| 行长失控(无 max-width) | 大屏上一行 60 个汉字,读者回行必迷路;可读性问题里行长失控排第一,比字体选错伤害大 |
| 字号档位 >6 档 | 层级贬值,读者分不清什么重要;音阶的意义就是强制克制 |
| 只有 400/700 两档字重 | 层级全靠字号撑,页面平;variable font 时代 300-900 都是免费的表达维度 |
| 表格/数据不用 tabular-nums | 数字宽度不等,列左右抖动,数据可信感直接打折 |
| 中文正文用 display 字体(得意黑/老宋整段排) | display 字体的个性在正文里变成阅读阻力,200 字后就累 |
| 中西混排中文字体放 fallback 链最前 | 拉丁字符全被中文字体自带的难看西文吃掉,配好的西文体永远轮不到出场 |
:root {
/* 1. fallback 链:西文 → 中文 → 系统中文 → 泛型(顺序即规则,见 4.2) */
--font-body: "Geist", "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;
--font-display: "Newsreader", "Noto Serif SC", "Songti SC", serif;
/* 2. 禁合成:不接受浏览器伪造的斜体/加粗(中文场景必开) */
font-synthesis: none;
/* 3. 中文断行底线 */
line-break: strict; /* 避头尾 */
overflow-wrap: break-word; /* 长 URL/英文串不撑破容器 */
}
body {
font-family: var(--font-body);
font-size: 17px; /* 中文正文基准,见第 1 章 */
line-height: 1.8; /* 中文行高基准,见第 2 章 */
/* 正文开启标准连字,关闭花哨特性 */
font-feature-settings: "liga" 1, "calt" 1;
}
/* 数据场景:等宽数字 + 斜杠零(0 和 O 不混淆) */
.data, table { font-variant-numeric: tabular-nums slashed-zero; }
/* 西文小标签:全大写 + 大字距的唯一合法场景 */
.label { text-transform: uppercase; letter-spacing: 0.1em; font-size: 12px; }
/* 标点挤压:中文 display 大字里全角标点的空洞收窄 */
.display-cjk { font-feature-settings: "halt" 1; }
中文字体加载(单文件 5-15MB,直接引全量会毁掉首屏):
cn-font-split 或 fonttools 的 pyftsubset,正文字体按常用 3500 字切,display 字体按实际出现的字符切(一张海报往往只有 20 个字,子集能压到 50KB 以内)font-display: swap 保底,中文字体下载慢,白屏等字体是最差体验