一个看起来很专业的小动作
做这个站的标题时,我给它们加了一点负字距:
.hero h1 {
font-size: clamp(54px, 8vw, 112px);
letter-spacing: -0.075em;
}这是从大量「现代网页设计」里学来的手法,也确实有道理。大字号下字母之间的间隙会显得松散,稍微收紧一点,整行字看起来更紧凑、更有整体感。很多设计系统的标题都用这个值,-0.02em 到 -0.05em 都是常见区间。
在桌面端 112px 的尺寸下,-0.075em 意味着每个字之间被压缩 8.4px。
直到我把它放大看
首屏标题是「把复杂的想法,做得清晰。」,分两行排。我把它单独截图放大之后,看到了几个地方不对劲:
- 「把复杂的」——「的」的左边几乎贴上「杂」
- 「想法**,**」——那个逗号左右几乎没有空隙,像一根竖线挂在字的边上
- 「清晰**。**」——句号同理
这不是「紧凑」,这是字形开始互相侵占。而且丢失的不只是字与字的间距,还有一样更关键的东西:标点的呼吸感。
为什么拉丁文字可以收紧,中文不行
差别在字形的构造方式。
拉丁字母是「方框里的图形」。 每个字符占据一个 em 方框,但字母本身的墨迹不填满这个方框——左右两侧留白叫 side bearing,是字母的一部分。比如 n 的墨迹只占方框中间一段,两侧都是空的。所以同一句话里的字母之间,天然已经累积了两份侧边留白。加一点负字距,只是把这些重复的留白收掉一部分,字与字仍然不会真的碰在一起。这是拉丁文字排版的常规操作,从金属活字时代就在做。
汉字是「填满方框的图形」。 一个汉字的墨迹大致铺满整个字身框,字与字之间的间隔基本就是方框本身的边界。它没有可压缩的冗余。在这个基础上再叠加负字距,直接从字形之间拿走距离——所以「的」会贴上「杂」。
全角标点是第三种情况。 中文的逗号、句号、顿号在设计上占满一格(一个 em),但墨迹只画在其中一侧,另一侧是故意留出的空白。这个空白不是为了好看,它是标点能起到「停顿」作用的前提——它给读者一个视觉上的呼吸口。
我那个 -0.075em 首当其冲吃掉的就是这块空白。它比字母间隙更脆弱,因为它是设计里唯一用来表达停顿的手段。
处理方式
改动的方向不是「把负字距删掉」,而是按文字系统分开处理。这个站的标题有两种:
- 中文标题:「把复杂的想法,做得清晰。」
- 拉丁标题:项目名叫
Spider King、Flowbase、Atlas Studio
拉丁标题的强收紧是好的——上面那套原理在这里完全适用,Spider King 这个尺寸下收紧之后确实更整齐。所以要改的只是中文那批。
我把两个值提成变量,放在 :root 里,顺便把「为什么」写进注释:
/* 标题字距策略:拉丁字形自带侧边空白(side bearing),收紧才显得整齐;
汉字是满 em 方框,同样的负值会先让字形相撞,再吃掉全角标点的呼吸感——
而标点留白正是中文排版的节奏来源。所以 CJK 只做很轻的收紧。
例外:纯拉丁标题(.brand / .case-title-row h1 / .project-copy h3)保持
原来的强收紧,那是它们该有的样子。 */
:root {
--tracking-cjk-display: -0.03em;
--tracking-cjk-heading: -0.02em;
}然后逐条替换:展示级中文标题(首屏、CTA、页级大标题、案例页 h3)用 -0.03em,小节级(卡片标题、列表标题)用 -0.02em。十二处规则,全部换成变量引用。
那几个纯拉丁的选择器一个都没动,仍然是 -0.08em。
改完再量:首屏标题的字距从 -8.4px 变成 -3.36px。
结果
改动之后,逗号和句号左右恢复了可见的间隙,字形不再互相侵占。两点值得记下来:
第一,原来那个值不是「错」的,是用错了对象。同一份 CSS 里,-0.075em 用在中文字上制造问题,用在拉丁文上制造品质。所以判断依据不该是「负字距是不是好设计」,而是「这个规则服务的是哪种文字」。
第二,全角标点的空白是内容,不是空隙。任何全局性的字距调整,都要把标点单独想一遍——它比正文更早出问题,也更容易被忽略,因为出问题的地方看起来只是「有点挤」,不像一个 bug。
顺带一提,字体本身其实已经带字距调整(kern)信息,我在生成子集时也保留了 layout_features = ["kern", "liga", "calt"]。也就是说,衬线字体自带一套针对特定字对(比如「的」和「复」)的间距微调。在它之上再压 -0.075em,等于把字体作者的调整覆盖掉——这大概是这件事里最该避免的部分。