返回博客

Monospace + letter-spacing 套中文:末尾汉字孤立换行的根因

做一个卡片列表页时,设计稿的元信息行(.vmeta)看起来挺工业感的——Space Mono 字体、letter-spacing: .14emtext-transform: uppercase,在英文内容下很好看。但当同一组样式套到中文属性串(比如「学校 · 年级 · 位置 · 特长」)上,在 375px 的手机屏里死活对不齐:最后两个汉字单独跑到第二行,页面上出现一个孤零零的「右脚」。

改了字号、改了容器宽度都没用,直到盯着 DevTools 里的 computed style 才意识到:letter-spacing 一直在偷偷加料。

现象

元信息行内容如下:

<div class="vmeta">余杭一小 · 五年级 · 前锋 · 右脚</div>

对应样式:

.vmeta {
  font-family: 'Space Mono', monospace;
  font-size: 9px;
  letter-spacing: .14em;
  text-transform: uppercase;
}

在 375px 视口下,缩去缩略图宽度和内边距后,.vmeta 容器只有约 201px,但这串 12 个汉字(含分隔点)实际渲染宽度约 210px——多了 9px,恰好把最后的「右脚」顶到第二行。

同一个 selector 复用了 16+ 张卡片,全部中招。

根因

W3C CSS Text 规范明确:letter-spacing每对相邻排版字符单元之间生效,CJK 表意字符不豁免。

计算一下:.14em9px 字号下 = 0.14 × 9 = 1.26px/字符间隔。12 个汉字有 11 个字符间隔,额外宽度 = 11 × 1.26 ≈ 13.9px。这就是容器溢出的来源。

Webkit、Blink、Firefox、Safari 行为一致——这是规范定义的,不是浏览器 bug。

坑:设计师从英文卡片拷贝样式,从没在中文下测过

Space Mono + letter-spacing >= .1em + text-transform: uppercase 是英文「工业字体」三件套。在英文/数字行上完全正确,但直接套到中文元信息行时:

  1. 每个汉字都被加字距,12 个字就溢出 ~14px,末尾字符孤立换行
  2. text-transform: uppercase 对 CJK 无效,保留它既无用又干扰调试
  3. 9px 中文几乎不可读,但 Space Mono 英文在 9px 仍可读,所以问题被忽视

审 mockup 时,凡看见这三件套套在中文行上,几乎必有溢出。

正解

一处 CSS fix,覆盖所有复用点:

.vmeta {
  /* 字体栈:中文走 PingFang SC,数字/英文 fallback 走 Space Mono
     工业感被保留在它该出现的字符上,中文不受等宽字体影响 */
  font-family: 'PingFang SC', 'Space Mono', monospace;
 
  /* 字距大幅压低:.02em 保留一点间距感,但汉字不再膨胀 */
  letter-spacing: .02em;
 
  /* 字号提升:11px 是中文元信息舒适下限 */
  font-size: 11px;
  line-height: 1.45;
 
  /* 删除 uppercase:对 CJK 无效,且让混排英文(如 U10)意外变形 */
  /* text-transform: uppercase; */
 
  color: var(--ink-mute);
}

如果同一 selector 偶尔也要渲染纯英文/数字内容,加 modifier class 而不是全局改:

.vmeta.en {
  font-family: 'Space Mono', monospace;
  letter-spacing: .14em;
  text-transform: uppercase;
  font-size: 9px;
}

配套数据精简(4 段属性仍偶尔溢出时):把次要属性从主元信息行移入下方 .tags chip 区,主行保留 3 段核心标识:

<!-- 改前:4 段挤在一行 -->
<div class="vmeta">余杭一小 · 五年级 · 前锋 · 右脚</div>
 
<!-- 改后:核心信息留主行,次要属性下沉 chip -->
<div class="vmeta">余杭一小 · 五年级 · 前锋</div>
<div class="tags">
  <span>右脚</span>
  <span>3 场</span>
  <span>2 球</span>
</div>
正解:字体栈让中文走系统字体,letter-spacing 压到 .02em

font-family: 'PingFang SC', 'Space Mono', monospace 是关键——浏览器按字符逐字选字体,中文走 PingFang SC,数字/英文 fallback 走 Space Mono,无需双 selector,工业感和可读性兼得。

验证

改完后三步确认:

  1. 截图对比改前/改后,同一卡片同一中文内容,确认末尾字符不再孤立换行
  2. grep -n 'class="vmeta"' 找出所有使用点,目测无 regression
  3. 日期(2026.03)、编号(U10)等数字/英文混排片段仍呈现等宽工业风,证明 fallback 字体链工作正常

一句话外卖

letter-spacing 对 CJK 字符同样生效,英文设计稿直接拷贝到中文行时必须压字距、换字体栈,否则汉字数量一多必溢出。