中文网页排版:标点、间距和字体回退

中文网页看起来”不够精致”,通常不是字体不好看,而是几个细节没处理。这些处理成本都很低,但改完之后观感差别相当明显。

标点造成的空洞

中文全角标点自带左右空白,本意是保证在竖排和方格稿纸里对齐。但在网页上,连续出现的标点就会挤出一片空白:

他问:「你确定吗?」——我没答。

后面跟 ,两个全角字符各带半个字宽的空白,视觉上就是一个突兀的洞。

CSS 有个新属性专门处理这件事:

body {
  text-spacing-trim: space-first;
}

它会裁掉开头标点的前置空白,让连排标点收紧。不支持的浏览器直接忽略,没有副作用,属于零成本的渐进增强。

不过目前浏览器支持还不完整,别指望它解决全部问题。真正要紧的排版位置——比如大字号标题——该手工调整还是得手工调整。

中西文之间的间距

「使用 Astro 构建」和「使用Astro构建」,后者读起来是要费点力的。

传统做法是在中西文之间手动加空格,或者上 pangu.js 这类库在运行时插入。但运行时插入意味着一段 JavaScript 参与了内容渲染,对于要求”禁用 JS 后页面仍完整”的站点来说不划算。

更轻的做法是接受”源文本里就写空格”这个约定。写作时在中英文之间敲一个空格,Markdown 原样保留,浏览器自然断行。这既是排版处理,也是可读的纯文本。

font-kerning: normal 也值得开——它让浏览器应用字体自带的字距调整表,对西文字母组合(比如 AVTo)效果明显,中文字体一般没有这张表,开了也不亏。

字体回退链

中文字体栈的写法有个常见错误:只写一个中文字体名就完事。

/* 不够健壮 */
font-family: 'Songti SC', serif;

问题是 Songti SC 只在 macOS 上有。Windows 用户拿到的是 serif 的系统默认值——通常是新罗马字体渲染中文,也就是宋体的某个替代品,效果不可控。

更稳的写法是把西文字体放在前面,中文字体依次列出各平台的名字:

--font-serif:
  Georgia,               /* 西文优先匹配,中文跳过 */
  'Songti SC',           /* macOS */
  'SimSun',              /* Windows */
  'Noto Serif CJK SC',   /* Linux / 思源宋体 */
  serif;

浏览器按字符逐个查找:西文字符在 Georgia 里找得到,直接用;中文字符 Georgia 里没有,继续往后找。一份字体栈同时服务两种文字,这是 font-family 的正常工作方式,很多人以为它是”整体选一个”。

顺序不能反。如果把中文字体放前面,西文字符会优先用中文字体里的西文字形——那些字形通常是等宽的、为方格设计的,混排时会明显偏胖。

行高与行宽

中文没有西文那样明显的升部降部,同样行高下看起来更挤。西文正文 1.5 够用,中文建议给到 1.75 到 1.9。

行宽的经验值是每行 30 到 40 个汉字。用 ch 单位不太准——ch 是数字 0 的宽度,对中文没意义。直接用 em 更直观:

.wrap { max-width: 34em; }

34em 在 17px 字号下约等于 578px,一行差不多 34 个汉字。

一个小结

这些改动都很小,单独看都不起眼。但排版就是这样——没有哪一条能让页面”变好看”,是所有细节一起把粗糙感磨掉的。