写 CSS 久了会发现,很多"莫名其妙"的间距问题,最后都能追溯到两件事:盒模型到底怎么算宽度,以及外边距为什么会合并。这两个概念搞清楚,布局里的坑能少一半。

两种盒模型

浏览器把每个元素都看成一个矩形盒子,从内到外依次是 content、padding、border、margin。区别在于:width 这个属性,到底描述了哪一部分的宽度。

  • 标准盒模型content-box):width 只算内容区。实际占位还要加上 padding 和 border。
  • IE 盒模型border-box):width 包含了 content、padding 和 border 三部分。

举个最直观的例子:

.box {
  width: 200px;
  padding: 20px;
  border: 5px solid #ddd;
}

/* content-box:实际宽度 = 200 + 20*2 + 5*2 = 250px */
/* border-box:实际宽度 = 200px,内容区被压缩到 150px */

为什么大家都用 border-box

因为布局时我们关心的是"这个块在页面上占多宽",而不是"内容区有多宽"。用 content-box 时,一旦给元素加了 padding,就得重新心算总宽度,栅格布局里尤其容易算错。所以现在项目初始化基本都会全局重置:

*,
*::before,
*::after {
  box-sizing: border-box;
}

这行代码的代价很小,收益是后续所有尺寸计算都变得可预测。唯一要注意的是,第三方组件的样式如果依赖 content-box,可能会被影响,接入前扫一眼即可。

外边距折叠是什么

普通流中,两个垂直相邻的 margin 不会相加,而是取其中较大的那个,这就是外边距折叠(margin collapsing)。

常见三种情况:

  1. 相邻兄弟元素:上面元素的 margin-bottom 与下面元素的 margin-top 合并。
  2. 父子元素:父元素没有 border、padding 或 BFC 时,子元素的 margin-top 会"穿透"到父元素外面。
  3. 空块级元素:自身的上下 margin 会折叠。

需要说明的是,只有垂直方向会折叠,水平方向的 margin 永远相加。另外 flex 和 grid 容器内部不会发生折叠,这也是很多人觉得"用了 flex 后 margin 突然正常了"的原因。

怎么规避

  • 父子折叠:给父元素加 overflow: hiddenpaddingborder,触发 BFC。
  • 兄弟折叠:只在一个方向上设 margin,比如统一用 margin-bottom,是更省心的约定。
  • 统一交给 flex/grid 的 gap 处理间距,从根上绕开这个问题。

小结

盒模型决定"元素有多大",外边距折叠决定"元素之间隔多远"。前者用 border-box 一劳永逸,后者用 gap 或单向 margin 约定就能避开。真遇到算不清的间距,先在浏览器 DevTools 里看一眼盒模型图,通常比猜要快得多。

— 全文完 —