← Inline 与 inline-block · 拆解 / baseline 与 vertical-align:对齐线在哪 待审核 2 / 8
vertical-align · baseline

baseline 与 vertical-align:对齐线在哪

01 · 行框与行内框讲过「行内框按 vertical-align 对齐」。默认值是 baseline——让盒子的 baseline 落在这一行文字的 baseline(那条字母 x 坐着、g 的尾巴垂下去的线)上。那么:一个 inline-block 盒子的 baseline 又在哪?这是负 margin 谜题替换元素 baseline 谜题共同前提

1 · 先看 vertical-align 把盒子挪去哪

一行里:锚文本 + 一个紫色 inline-block绿色虚线是这一行文字的 baseline(实测画出)。切 vertical-align 看盒子相对这条线怎么跳。

两组取值的区别:text-top/text-bottom 对齐的是父级文字的字体顶 / 底(随字号变);top/bottom 对齐的是整个 line box 的顶 / 底(随这一行最高的内容变)。middle 也不是「行的正中」,而是「锚文本小写字母 x 的中线」附近。

2 · 关键规则:inline-block 自己的 baseline 在哪

规范(CSS 2.1 §10.8.1)规定:inline-block 的 baseline = 它最后一个 in-flow line box 的 baseline;但只要它「没有 in-flow 的行内内容」或者 overflow 不是 visible,baseline 就改成它的下外边距边(bottom margin edge)。下面固定 vertical-align: baseline,切换这两个开关,看盒子相对绿线的对齐基准立即改变:

正是这条规则导致后续两个现象:「空盒子 / overflowvisibleoverflow\ne visible → baseline 落到下外边距边」意味着这种盒子是以下外边距边坐在 baseline 上的。给它一个负的 margin-bottom,这条边就被上移——这就是负 margin 谜题。而图片这种替换元素,baseline 就是它的下边,旁边的文字却用自己的 baseline 去对,这就是img 撑高谜题

3 · 延伸:规范为什么这样定

这条「overflowvisibleoverflow\ne visible 就用下外边距边」的规则看似别扭,其实有其历史原因(贺师俊在知乎讲过来龙去脉)。inline-block 是 CSS2 之后才补进来的,CSS 2.1 这块更多是对浏览器既成实现的追认,而不是当初设计者的本意:

  • 有滚动条时,「最后一行 line box」根本定不了位。overflow: scroll/auto/hidden 的盒子内部可以滚动,你无法知道「最后一行」此刻在哪——于是只剩盒子的边界可以当锚,这种盒子本质像个 <iframe>,从一致性看就该拿底边对齐。
  • 但这在 overflow: visible 时留下一处不连续:同一个盒子,有内容(跟末行对齐)和没内容(跟底边对齐)布局会突然跳变——相当反直觉,也常被批评。
  • 为什么不干脆用「首行」对齐?那样确实没有上面的跳变问题,但会和图片对齐(图片底边对父级 baseline)显得不一致——而图片对齐是 CSS1 时代就定死的,那时还没有 inline-block
  • **后续怎么补救:**CSS Inline Layout L3 重新定义了这块,允许用 inline-box-align 指定按首行(或任意指定行)对齐,而不再被钉死在末行 / 底边。

4 · 把上面的规则连成一张图:vertical-align: top 实战

最后用一个混合行高的经典例子总结(取自貘吃馍香的图解)。容器 div { font-size: 20px; line-height: 20px } 里有三段内容:两段裸文字(它们各自是匿名行内框,从父级继承 line-height: 20px),中间夹一个 <span> 显式设了 line-height: 60px(更高,不再继承)。切换 span 的 vertical-align 看整行怎么排:

两点结论:其一,不管 span 怎么对齐,行框高度都 = 60px——因为这一行最高的内容始终是那个 60px 的 span。其二,baseline 时三段内容的 baseline 拉到同一条线,矮的两段匿名文字被「拽」到了下方;切到 top,span 的顶边改去贴行框顶,两段匿名文字则在各自 baseline 上回到行的上部。

这些高度从哪来?行框高度、leading 的上下半分、baseline 的位置,根上都来自字体度量(ascender / descender / x-height)。想看 content-area 与 line-height: normal 怎么被字体度量算出来,看姊妹页 字体度量:为什么 100px 不是 100px