为什么 production 级组件库偏爱 position: absolute + inset: 0

从一次 Safari 图片高度问题出发,拆开 CSS 高度推导链,解释为什么很多组件库更偏向绝对定位方案。

为什么 production 级组件库偏爱 position: absolute + inset: 0

这篇文章是从一个 Safari 下的图片高度问题开始的。

需求本身很普通:卡片里放一张 4:3 的图片,裁切填满,object-fit: cover

代码看起来也没问题:

<div class="aspect-4/3 overflow-hidden">
  <img class="w-full h-full object-cover" />
</div>

在 Chrome、Edge、Firefox 里,它表现正常。到了 Safari,图片高度却不对:有时被压扁,有时填不满,object-fit 像是只生效了一半。

这类 bug 最麻烦的地方就在这里。它不会报错,也不会直接坏掉,只是在某个浏览器里看起来不对。

先别急着怪 Safari

看到这种现象,第一反应通常是 WebKit 又出问题了,补个 hack 收工。

但这次如果真这么下结论,反而容易看偏。Safari 在这里不一定是“错”的那一个,它只是更严格。

真正的问题不在 object-fit

问题的核心其实是:这张图片的高度到底从哪来。

很多项目里都会有类似的基础样式:

img {
  height: auto;
}

这条规则本身没有问题。麻烦出在另一边:子元素写了 height: 100%,而父元素的高度又是从 aspect-ratio 推出来的。

Safari 对这条链路的判断比较保守。它遵循的是一条老规则:

百分比高度只有在父元素高度是 definite 时才成立。

而在 Safari 看来,aspect-ratio 推出来的高度不算 definite。于是链路会变成这样:

  1. 父元素通过 aspect-ratio: 4 / 3 得到一个推导高度。
  2. 子元素的 height: 100% 发现父高度不够“确定”,于是退回 auto
  3. img { height: auto } 接管。
  4. 图片高度回到 intrinsic size 的计算路径。
  5. object-fit 失去了一个稳定的盒子。

逻辑上它是说得通的,只是写业务时很难喜欢这种结果。

Chrome 为什么看起来正常

Chrome 更宽松一些。它会把 aspect-ratio 推出来的高度继续往下传,近似当成 definite height 处理。

所以很多 demo 在 Chrome 里完全没问题,甚至会让人误以为这套写法本来就稳定。

问题在于,production 环境怕的从来不是“某个浏览器太严格”,而是不同浏览器在边界条件下给出两套答案。

为什么很多组件库改用绝对定位

这时候再看这类写法就比较容易理解了:

.wrapper {
  position: relative;
  aspect-ratio: 4 / 3;
}

.wrapper img {
  position: absolute;
  inset: 0;
  object-fit: cover;
}

这里最关键的变化,不是“换了个写法更老练”,而是布局模型变了。

元素一旦脱离 normal flow,浏览器就不再沿着“高度是不是 auto、百分比能不能成立、内容尺寸怎么推”这条链一直往下猜。它直接解几何约束:top + bottom = containing block height

换句话说,问题从“内容推导”变成了“矩形约束”。这对浏览器更直接,对工程也更稳。

这种方案到底稳在哪

绝对定位至少避开了几件麻烦事:

  • 不再依赖 img 的 intrinsic size 去决定最终盒子高度
  • 不再依赖 height: auto% height 的组合能否顺利闭环
  • 不再把结果押在浏览器是否愿意把 aspect-ratio 产出的高度视为 definite

到了这一步,Safari 的可发挥空间就小很多。它基本只能按 containing block 的几何结果来放元素。

为什么组件库宁可保守一点

如果你翻过一些成熟组件库的实现,会经常看到类似模式:

outer {
  position: relative;
}

inner {
  position: absolute;
  inset: 0;
}

原因不神秘,就是因为它更可控。

这些库要处理的不只是单张图片,还包括:

  • Safari
  • 滚动容器
  • imgvideo 这类 replaced elements
  • object-fit
  • resize
  • 缩放和各种边界状态

这时候“语义上更优雅”的 CSS 往往不是第一优先级。更重要的是它在不同浏览器、不同布局上下文里能不能稳定复现。

这类问题真正暴露了什么

一旦几件事叠在一起,CSS 就很容易从计算题变成推理题:

  • aspect-ratio
  • % height
  • height: auto
  • intrinsic size
  • overflow 或滚动容器

Safari 不是做错了,Chrome 也不一定更正确。它们只是选了不同的工程取向。

absolute + inset: 0 之所以常见,说到底不是因为它“高级”,也不是因为大家都爱写 old-school CSS。只是当你真的要做稳定组件时,主动绕开这条高度推导链,通常更省事。