中文排版是一门减法
这一页是 Markit 的排版样张。它同时是两样东西:一篇能正常读下去的文章,以及一份把编辑器支持的全部 Markdown 语法排了一遍的清单。两者必须同时成立——只堆语法的样张骗不了眼睛,一段真实的长文才能暴露行距是不是散了、标点是不是漏了气、中英混排的地方是不是硌手。
排版的判断标准从来不是"好看",而是"读完一千字之后眼睛累不累"。这句话听起来像废话,但它推翻了大部分看起来很漂亮的方案。
版面的第一原则:先做减法
一个编辑器界面上真正属于内容的东西,只有文字本身。剩下的一切——工具栏、侧栏、按钮、状态指示——都是在跟内容抢注意力。所以我们的第一条规则是:默认状态下,界面元素要么隐形,要么退到视线边缘。
具体到几个数字:
| 元素 | 取值 | 理由 |
|---|---|---|
| 版心宽度 | 720 px | 中文约 45 字/行,接近纸质书的一行字数 |
| 正文行高 | 1.55 | 中文方块字比拉丁字母密,1.5 会挤 |
| 标题层级 | 三档 | 再多一档,层级就靠猜了 |
| 强调色面积 | ≤ 5% | 超过这个比例,强调就不再是强调 |
表格里的数字用等宽字形对齐——这是个很小的细节,但一列数字如果左右跳动,读者会不自觉地去比对而不是去理解。
留白比线条更能分组
用分隔线切分内容是初学者的做法,用留白切分才是成熟的做法。线条会在版面上留下硬边,而留白是无形的:
- 段落之间 16 px,让句子成组
- 小节标题上方 24 px、下方 8 px,上紧下松让标题黏住它管辖的内容
- 大节标题上方 40 px,让读者的视线自然停一下
这种"上紧下松"的分级留白一旦被压平——比如被一条统一的 margin 规则盖掉——版面会立刻退化成一串等距的段落,层级全靠字号硬撑。这类失效是静默的:代码里声明还在,看起来一切正常。
字体:中西文是两件事
CSS 的字体回退是逐字符进行的,这意味着字体栈的顺序决定了英文由谁来渲染。把中文字体排在前面,camelCase、版本号 v1.522、以及所有拉丁字母都会落到中文字体自带的西文字形上——那部分往往是附赠品。
正确的写法是西文在前、中文在后:
font-family: Charter, 'LXGW WenKai', -apple-system, 'PingFang SC', sans-serif;
这条规则的两种写法都"能跑",差别只体现在字形上。看代码是看不出对错的,所以它必须由测试来守。
一个反例
下面这段代码演示了容易踩的坑——把中文字体放在首位,同时用了合成斜体:
// ⚠️ 两处都错了
const stack = "'PingFang SC', Charter, sans-serif" // 中文在前:英文字形失控
const emphasis = { fontStyle: 'italic' } // 中文没有斜体传统
中文的强调传统是着重号(字下加点),不是把方块字压歪。合成斜体会让笔画交叠处糊成一团,字号越小越明显。
合成粗体是同一类问题。如果一款字体没有真正的粗体字面,浏览器会把字形描边加粗,横竖笔画的粗细关系会被破坏。
语法清单
下面把剩余的语法过一遍,顺便验证它们在这套样式下彼此不打架。
行内元素
正文里可以出现加粗、强调、删除线、行内代码,以及外部链接。自动识别的网址也算:https://example.com 。这些元素混在同一段里时,最容易出问题的是行高——任何一个行内元素带了额外的 padding,整行就会被顶开。
标签是元信息,不是正文重点,所以它做成低饱和的药丸而不是彩色标签墙:#排版 #字体 #工程实践
列表
有序列表用于步骤,无序列表用于并列项:
- 先确认要解决的是什么问题
- 再确认这个问题值不值得解决
- 影响多少人
- 多久遇到一次
- 最后才是怎么解决
任务列表用于清单:
- 定下版心宽度与行高
- 定下字体栈的顺序
- 确认中文强调用不用着重号
- 在真机上验证悬挂标点
嵌套的无序列表:
- 字体
- 西文:Charter、Georgia、Palatino
- 中文:霞鹜文楷、思源宋体、系统黑体
- 排版
- 中西混排间隙
- 标点挤压
引用与提示
好内容,值得好版面。
排版的目的不是让文字变好看,而是让读者忘记版面的存在。
五种提示块各有各的语气,它们是内容作者选择的语义,不是界面装饰:
长文阅读用宋体或黑体更省眼;楷体适合随笔与短文。
磁盘上的
.md文件是唯一真相。任何排版效果都只能活在渲染层。
不要在用户打字的过程中改动他的文本。自动插入的空格会随自动保存落盘。
代码块
代码块需要横向滚动而不是撑破版心,配色也要克制——关键字用强调色,注释退到灰阶,其余是正文色。彩虹配色在暖纸底上会显得很吵:
/// 在访达里选中当前文档
@objc private func revealInFinder(_ sender: Any?) {
guard let url = session.url else { return }
NSWorkspace.shared.activateFileViewerSelecting([url])
}
一行很长的代码应该滚动,而不是换行:
curl -sSL "https://raw.githubusercontent.com/example/repo/main/very/long/path/to/some/file.txt" -o output.txt
图片
图片下方紧跟一段说明文字时,两者的间距应该比普通段间距更紧,读者才会把它们看成一组。
最后
分隔线在上面这一处是合理的:它标记的是"文章结束、收尾开始"这种真正的断裂,而不是段落之间的呼吸。用得越少,它出现的那一次就越有力量。
一套排版方案是否成立,最终只有一个判据:把这一页从头读到尾,有没有哪一处让你停下来注意到了版面本身。有,就是那里还需要改。