chapter[0] · design-system.md
在写下第一个字之前,这本书先写了一份 style.css。这不是工程洁癖,而是本书要教的第一课:美不是灵感的随机输出,而是一组被严格执行的决定。你将在第八章看到,工业界把这组决定叫作设计系统(Design System)。这一章公开本书自己的设计系统——它只有五种颜色、三种字体、一个 8px 网格,以及十来个组件。读完后,你在本书任何一页看到的任何视觉细节,都可以在这里找到出处。
设计系统之于界面,相当于类型系统之于代码:它把"什么是允许的"提前声明,把无穷的选择空间压缩成少数合法值。一个页面用了第六种颜色,等同于一次类型错误——也许能跑,但迟早出事。
本书的色彩系统借自中国传统印刷:纸、墨、朱砂、黛青、金。关键不在于这五个色值本身,而在于每种颜色绑定一个语义角色——颜色因此变得可预测:看到朱砂红,你就知道这是重点或章号;看到黛青,那一定可以点击。
注意底色不是纯白 #FFFFFF,文字也不是纯黑 #000000。纯白配纯黑的对比度高达 21:1,长时间阅读会刺眼;暖白配暖黑(约 14:1)既远超无障碍标准(WCAG 要求正文 ≥ 4.5:1),又模拟了纸张的柔和。第一个可以立刻带走的技巧:永远不要在界面里使用纯黑文字配纯白背景。
本书只用三种字体,像三种乐器,各司其职:
字号不是随手定的,而是来自一个公比 1.25 的模数字阶(modular scale):13 → 15 → 18 → 22 → 28 → 36 → 46 → 58。就像音阶里的音符,相邻字号之间存在固定的比例关系,整个页面因此有了"和声"。正文 18px、行高 2 倍,是为中文长文阅读调校的——汉字是方块字,比拉丁字母需要更宽的行距才能呼吸。
模数字阶就是字号的"等比数列生成器":fontSize(n) = base × ratio^n。Tailwind 的 text-sm/base/lg/xl… 就是一个预计算好的字阶。当你不知道标题该用多大时,不要微调像素,去字阶里取下一级。
本书所有间距都是 8 的倍数:8、16、24、32、40、48、64、96。这条规则消灭了"这里到底空 13px 还是 17px"的纠结——当所有间距来自同一个节拍,页面自然显得整齐,哪怕你说不出为什么。
正文栏宽 720px,约等于每行 36 个汉字。这是排版学里的行长(measure):太长,眼睛换行时会迷路;太短,视线跳动太频繁。西文的经验值是每行 45–75 字符,中文约 25–40 字。图版允许"破栏"到 960px——重要的画作值得更大的舞台,这种宽窄变化本身也是一种节奏。
以下组件构成了本书全部的表达手段。组件少而精确,胜过多而含糊。
| 组件 | 用途 | 设计意图 |
|---|---|---|
figure.art 图版 | 真实艺术品 + 图注 + "怎么看" | 每张图必须带鉴赏指引,拒绝装饰性配图 |
.callout.dev 程序员视角 | 把美学概念翻译成工程语言 | 黛青色 = 工程的声音 |
.callout.practice 鉴赏练习 | 章末动手训练 | 金色 = 值得花时间的高亮 |
.callout.key 核心概念 | 必须记住的结论 | 朱砂 = 最高优先级 |
.compare 对比并置 | 好坏方案并排 | 鉴赏力的最快训练法是对比 |
.principle 原则块 | 编号的设计原则 | 等宽数字编号,可被引用 |
三种标注框的实物演示:
朱砂色块只用于全书最重要的结论。一章最多出现两三次——强调的东西一多,就什么都没强调。
金色块是留给你的作业。本书的每个练习都可以在 15 分钟内完成,但效果取决于你是否真的动手。
居中单栏、衬线正文、大量留白、没有侧边栏和悬浮按钮。阅读长文时,一切非内容元素都是噪音。
问任何一处"为什么是这样",答案要么在本章,要么在 style.css 的注释里。不可解释的装饰已被删除。
五种颜色、三种字体、一个网格。后面十六章无论内容如何变化,都不允许突破这套词汇。
这是一本鉴赏书,画作享受最大版面、最重投影和强制的"怎么看"指引。文字为图让路。
一套设计系统是否成熟,不看它有多自洽,而看它知不知道自己的边界。每个系统都是为某个目标优化的,因此必然在别的目标上让步——说不出自己"为什么而优化、以什么为代价"的系统,只是还没遇到打脸的场景。为了示范这种诚实,这本书拿自己开刀,先做一次它最该被审的项:对比度(第九章 9.6 会正式讲)。下面是本书全部文字用色在纸色上的真实对比度账本:
对比度只是其中一项。这套"像书不像网页"的系统,还有几处坦白的代价:① 为沉浸阅读优化,就牺牲了查阅——没有持久导航、没有搜索、一路长滚动,你想跳到"第九章数据密度那段"并不方便(一个工具型文档绝不该这么设计,但一本书可以)。② 固定 720px 版心把中文长文行长锁得很舒服,代价是它对"边读边对照代码"这类双栏需求毫无帮助。③ 朱砂同时担任"强调"和"链接悬停"两个角色,存在轻微的语义张力——严格说一个颜色一个语义,这里为了克制总色数破了一点例。每一条都是真实的取舍,写出来不是为了道歉,是为了证明:这套系统的每个边界都是被看见、被选择的,而不是被忽略的。
给你自己的设计系统做同样的体检,两步即可:① 跑一遍对比度——把你的文字色 × 背景色组合扔进任何对比度检查器(或 axe),像上面这样列出账本,然后给每个不达标的颜色明确写下"它只许出现在哪里"。② 写下一句"优化目标 / 代价"——"本系统为 ___ 优化,因此在 ___ 上让步"。填不出这句话,说明你还没真正理解自己的系统,只是攒了一堆好看的值。这和给模块写 README 里的"非目标(non-goals)"是同一种成熟度:知道自己不做什么、不擅长什么,比罗列功能更能体现设计的清醒。
两道题。① 语义一致性:翻回目录页,找出朱砂色出现的所有位置,验证它们是否真的都是"强调/链接"语义;再看你最常用的一个 App,如果发现同一个红色既表示"删除"又表示"促销",你就完成了人生第一次设计审查。② 边界体检:给你自己项目(或本书)写下那句"为 ___ 优化、在 ___ 上让步",并把主色板跑一遍对比度,列出哪些颜色该被关进"非正文"的笼子。一套说得出自己边界的系统,已经赢过了大多数。