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,切换这两个开关,看盒子相对绿线的对齐基准立即改变:
正是这条规则导致后续两个现象:「空盒子 /
→ baseline 落到下外边距边」意味着这种盒子是以下外边距边坐在 baseline 上的。给它一个负的 margin-bottom,这条边就被上移——这就是负 margin 谜题。而图片这种替换元素,baseline 就是它的下边,旁边的文字却用自己的 baseline
去对,这就是img 撑高谜题。
3 · 延伸:规范为什么这样定
这条「
就用下外边距边」的规则看似别扭,其实有其历史原因(贺师俊在知乎讲过来龙去脉)。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。