← Inline 与 inline-block · 拆解 / 图标如何对齐文本:cap 单位与 ZWSP strut 待审核 7 / 8
配方 · icon × text

图标如何对齐文本:cap 单位与 ZWSP strut

本系列其余页面拆解 IFC 的机理;这页换个角度,把它当一道工程任务:给一个图标(<svg> / icon font),让它和旁边的文字看起来「对齐」。难点在于——文字的视觉重心既不在 baseline、也不在 vertical-align: middle 对的那条 x 中线上,而是更靠上的 大写字母中线(cap-line 与 baseline 的中点)。本页用真实 getBoundingClientRect() 把 baseline / x-line / cap-line 三条线一起画出来,逐一对比四种对齐法,看谁真的把图标中心送到了光学中线上。

两个核心方案:其一,cap 单位(Chrome 118+,1cap = 当前字体的 capHeight)——translateY(calc(50% - 1cap / 2)) 把图标中心精准抬到 cap 中线,不用再 hardcode .7em;其二,ZWSP strut hack(推荐)——用一个零宽空格 在局部容器里造一根「支柱」,让 inline-flex 容器自己长出一条文字 baseline,图标对齐这根支柱 = 对齐相邻文本,不依赖任何硬编码数值

取材自知乎《图标如何对齐文本》(长天之云,p/30624268);cap / ex 用 CSS cap / ex 单位实测,strut 概念见 CSS 2.1 §10.8。本页假设你已看过 02 · vertical-align04 · 替换元素 baseline

1 · 四种对齐法同台:谁真的对到了光学中线

一行里 Like + 图标 + 收藏,切换图标的对齐方式。叠加层画出这一行文字的四条参考线;玫红线光学中线(= baseline 上方 capHeight/2,大写字母的视觉中心,也是图标该对齐的目标);蓝线是图标当前的几何中心。两线越贴近越对齐,readout 给出实测差值(px)。

2 · 四种对齐法,差在哪

3 · cap 单位:不用再 hardcode 0.7em

思路:默认图标底贴 baseline,先把图标自身上移 50%让其中心对到 baseline,再下移 capHeight 的一半落到 cap 中线。合起来 translateY(calc(50% - 1cap / 2))translateY 正值向下,50% 是图标自身高度的一半)。cap 单位前(Chrome 118 才支持),只能用 0.7em 估算 capHeight——而它随字体浮动(见图 2 那张 Font Metrics 表),估不准就差像素。

三种等价写法:

  • transform 版(兼容老浏览器加 fallback):transform: translateY(calc(50% - var(--cap, .7em) / 2)),其中 --cap: 1cap
  • 负 margin 版(尺寸小时不额外撑行高):margin-bottom: calc(-1 * (var(--h) - 1cap) / 2)
  • 从 middle 起偏(复用 ex):vertical-align: middle; position: relative; top: calc(0.5ex - 1cap / 2)-.1em

4 · ZWSP strut:推荐方案,精确且无硬编码数值

cap 方案准,但占着 transform / margin,且要 fallback。更稳的是借 CSS 规范的 strut 概念:在图标外包一个 inline-flex 容器,塞进一个零宽空格 。这个看不见的字符给容器造出一根带 line-height 的「支柱」——于是容器自己长出一条文字 baseline,它作为 inline-level 元素按这条 baseline 与外部文字对齐;而 align-items: center 把图标垂直居中到这根支柱的行高里 → 图标中心 = 外部文字的视觉中心。上面 demo 的「inline-flex + ZWSP」选项实测它的对齐误差最小,且图标无论多大都自动跟随

封装成组件(框架无关):

  • inline-flex hack(推荐): <span style="display:inline-flex;align-items:center">​<svg/></span>——对齐完美,不支持过时浏览器。
  • **inline-block hack:**容器 display:inline-block; line-height:<图标高> 内放 ,图标 position:absolute 顶左对齐——兼容性更广,但图标不能超过行高。

关键都在那个 :没有它,容器里没有文字,inline-flex 的 baseline 会退化到容器底边,图标又会偏。

5 · 用了 flex 仍差 1px:奇偶取整问题

就算对齐法选对了,有时图标还是「差 1px 没对齐」。根源是子像素被光栅化到整数物理像素时的取整:对齐误差取决于 iconSize / fontSize / lineHeight 三者的奇偶组合(且不同浏览器表现不同)。下面拖三个滑块,用 inline-flex 居中固定,实测图标几何中心与文字光学中线的差(rect 子像素级):

注意:这里测的是 getBoundingClientRect()几何中心差(子像素小数)。肉眼看到的「1px」还要叠加浏览器把这个小数光栅化到物理像素时的舍入——所以同样的几何差,在不同 DPR / 浏览器下可能现身也可能消失。文章给出的经验规律(PingFang SC · Chrome · flex)如下,仅供参照,以你浏览器的实测为准:

6 · 总结

图标对齐文本,目标是光学中线(cap/2),不是 baseline、也不是 middle 的 x/2。要临时一处对齐用 translateY(calc(50% - 1cap / 2))(cap 单位免去 hardcode);要封装成可复用组件就用 inline-flex + ​(ZWSP 造 strut,无硬编码数值、随图标大小自适应)。剩下的 1px 抖动,是子像素光栅化的取整,对齐法选对后再用同奇偶尺寸收尾即可。