CSS探索系列之Margin
外边距(margin)位于元素边框外侧,用来分隔当前盒子与周边其他盒子。其他盒子不会侵入外边距区域,父元素背景可以透过外边距区域显示出来。
作用:控制盒子与周边元素的间距,实现留白、居中;同时具备几个特殊行为:块级元素会发生外边距折叠、百分比取值参照包含块行内尺寸、auto 关键字分配剩余空间。
取值与简写
margin 可拆分为四个独立方向属性:margin-top、margin-right、margin-bottom、margin-left;也支持复合简写。
可用取值:
- 长度值:
px、rem、em等;支持负值,负值会让相邻元素互相靠近,甚至发生重叠。 - 百分比:相对于包含块的行内尺寸计算。
- auto:行内方向可自动分配剩余空间;普通流下块向会解析为
0。
简写规则:顺序「上 → 右 → 下 → 左」,支持 1~4 个值自动推导补全剩余方向。
外边距折叠
外边距折叠(margin collapsing):同一块级格式化上下文(BFC)中,普通流块级盒子会发生垂直外边距合并,最终只保留一个外边距。
水平书写模式下,仅 margin-top、margin-bottom 参与折叠;竖排书写模式,折叠行为切换到行方向。
合并计算规则:
- 两边均为正值:取更大的值;
- 一正一负:两值直接相加;
- 两边均为负值:取更负(绝对值更大)的值。
折叠必要条件:
- 参与折叠的是普通流块级盒子;
- 两个外边距之间无分隔:不存在
border、padding、内容、行框阻断; - 不参与折叠:浮动元素、绝对定位元素、Flex子项、Grid子项。
四类折叠场景:
- 父子顶部折叠:父
margin-top与第一个普通流子元素margin-top合并; - 兄弟相邻折叠:前一个兄弟的
margin-bottom与后一个兄弟的margin-top合并; - 父子底部折叠:高度为
auto的父容器margin-bottom,与最后一个普通流子元素margin-bottom合并; - 空盒子自折叠:盒子满足
height:0|auto、min-height:0、无普通流子元素、没有新建 BFC,自身margin-top和margin-bottom互相合并。
示例:两个相邻段落设置 margin:50px 0,中间间距不是 50+50=100px,而是折叠后 50px。
<div style="border:1px solid #f00;">
<p style="margin:50px 0;">我喜欢回忆,是因为回忆是我们审视自己生活的过滤器。</p>
<p style="margin:50px 0;">回忆模糊不清,就给自我欺骗提供了机会。</p>
</div>
阻止外边距折叠的手段:
- 根元素的
margin不会参与折叠; - 创建新 BFC,阻断父子折叠(兄弟仍在同一父级 BFC 中);
- 父容器增加
border、padding,隔断父子margin接触; - 父容器设置非
auto的height,避免和最后子元素折叠;min-height、max-height挡不住这一类; - 空盒子设置
height、min-height,阻止自身上下margin折叠。
margin 百分比
所有方向的 margin 百分比,统一参照包含块的行内尺寸:
- 默认横排(水平书写):行内尺寸 = 包含块宽度;哪怕是
margin-top、margin-bottom,百分比依旧参照宽度,不是父容器高度。 - 竖排书写模式:行内尺寸 = 包含块高度。
设计原因:主要为规避普通流下的循环布局依赖;统一全部方向参照行内尺寸是规范方案。
示例:父容器宽 600px、高 300px;子元素设置 margin:10% 5%。
计算结果:上下margin = 600*10% = 60px,左右margin = 600*5% =30px → 最终 margin:60px 30px 60px 30px,不是参照父高度得到 15px。
<div style="width:600px;height:300px;border:1px solid #f00;">
<p style="margin:10% 5%;background:#0aa;">
我喜欢回忆,是因为回忆是我们审视自己生活的过滤器。
</p>
</div>
margin 的 auto
普通流块级盒子:块向(横排下即上下)的 margin-top:auto、margin-bottom:auto 计算值为 0,margin: auto 等价于 margin:0 auto,仅实现水平居中。该规则对绝对定位、Flex、Grid子项不生效。
行内方向(横排下左右)的 auto 会分配剩余空间。
规则总结:
- 普通流块盒子必须设置确定宽度,左右
auto才有剩余空间可分配;width: auto时盒子直接撑满父容器,无余量。 - 非替换行内盒子,左右
auto解析为0,无法用margin:0 auto居中。 - 绝对定位:对应轴的两个 inset 都设置(水平居中要
left与right)且该轴有确定尺寸,margin:auto可在该轴居中。 - Flex 子项、Grid子项:四个方向
auto都可以瓜分剩余空间;单个子元素下写margin: auto可实现完全居中。
示例:父宽 600px,子定宽 200px,设置 margin-left:auto。剩余空间全部分配给左侧,实现右对齐。

<div style="width:600px; border:1px solid #f00;">
<div style="width:200px; margin-left:auto; background:#0aa;">
我喜欢回忆,是因为回忆是我们审视自己生活的过滤器。回忆模糊不清,就给自我欺骗提供了机会。—— 石黑一雄《远山淡影》
</div>
</div>
margin 与相对定位偏移的区别
margin-top: 30px 和 position:relative; top: 30px 视觉上都可以让盒子向下偏移 30px,但布局行为完全不同:
- margin:参与普通流布局。会改变盒子占位,后面兄弟元素跟随偏移后的位置继续排布。
- relative + top:只改变盒子渲染绘制位置。元素原本占位保持不变,后续兄弟依旧在原始位置排布。


section { padding: 20px; border: 1px solid #f00; }
.item { display: inline-block; width: 48%; vertical-align: top; }
<section>
<div class="item">
<div style=" margin-top: 30px; color: #a0a; border: 1px solid #a0a;">
我喜欢回忆,是因为回忆是我们审视自己生活的过滤器。回忆模糊不清,就给自我欺骗提供了机会。—— 石黑一雄《远山淡影》
</div>
人常常靠着改编过的过去获得心灵平静。
</div>
<div class="item">
<div style="position: relative; top: 30px; color: #0aa; border: 1px solid #0aa;">
我喜欢回忆,是因为回忆是我们审视自己生活的过滤器。回忆模糊不清,就给自我欺骗提供了机会。—— 石黑一雄《远山淡影》
</div>
人常常靠着改编过的过去获得心灵平静。
</div>
</section>
书写模式对 margin 的影响
margin 的折叠、百分比、auto 逻辑,跟随行向、块向,而不是固定的上下左右物理方向。
- 水平书写
horizontal-tb(默认)- 折叠发生在垂直物理方向;
- 百分比参照包含块宽度;
- 块向(上下)
auto= 0;margin:auto等价margin:0 auto。
- 垂直书写
vertical-rl、vertical-lr- 折叠发生在水平物理方向;
- 百分比参照包含块高度(此时高度为行内尺寸);
- 块向(物理左右)
auto= 0;margin:auto等价margin:auto 0。
.h { writing-mode: horizontal-tb; }
.v { writing-mode: vertical-rl; }
圣杯布局
圣杯布局:两侧定宽、中间自适应。
实现:中间 DOM 靠前优先渲染;全部浮动;利用负 margin 把侧边栏拉回可视区域,再用 relative 偏移到父容器 padding 预留的位置。

<section>
<header>header</header>
<div class="container">
<div class="middle">middle(中间弹性区)</div>
<div class="left">left</div>
<div class="right">right</div>
</div>
<footer>footer</footer>
</section>
<style>
header, footer, .middle, .left, .right {
border: 1px solid #0aa;
padding: 15px 10px;
text-align: center;
box-sizing: border-box;
}
.container {
overflow: hidden;
padding: 0 200px;
}
.middle {
width: 100%;
float: left;
}
.left {
width: 200px;
float: left;
margin-left: -100%;
position: relative;
left: -200px;
}
.right {
width: 200px;
float: left;
margin-left: -200px;
position: relative;
right: -200px;
}
</style>
双飞翼布局
双飞翼布局:两侧定宽、中间自适应。
实现:不使用 relative 偏移,中间栏内部嵌套一层,靠内层 padding 留出侧边栏位置;侧边栏仅使用负 margin 完成定位。
<section>
<header>header</header>
<div class="container">
<div class="middle">
<div class="in">middle(中间弹性区)</div>
</div>
<div class="left">left</div>
<div class="right">right</div>
</div>
<footer>footer</footer>
</section>
<style>
header, footer, .middle, .left, .right {
padding: 15px 10px;
border: 1px solid #0aa;
box-sizing: border-box;
text-align: center;
}
.container {
overflow: hidden;
}
.middle {
width: 100%;
float: left;
}
.in {
padding: 0 200px;
}
.left {
width: 200px;
float: left;
margin-left: -100%;
}
.right {
width: 200px;
float: left;
margin-left: -200px;
}
</style>
margin 视觉无效的几种场景
- 外边距折叠:外边距发生合并,修改数值较小一侧,视觉看不出变化。
- 行内非替换元素:垂直
margin不参与行高计算,视觉无效;水平margin正常生效。替换元素,如:<img>垂直margin有效,也不参与折叠。 - 表格盒模型:
tr、td、display:table-row、table-cell不支持margin;table、inline-table、table-caption可以正常使用margin。 - 绝对定位元素:没有全部设置对应方向
inset时,对应方向margin不参与剩余空间分配。四个方向inset都设置且盒子有确定宽高,margin:auto才可以完全居中。 - 父容器定高、定宽:父设置固定高度,不会被子元素
margin-bottom撑开;定宽同理,margin-right撑不开父宽度。margin会溢出父容器,看起来像失效,实际计算存在。
小结
margin 控制盒子与周边元素的布局关系:
- 普通流块级盒子会发生外边距折叠;
- 百分比统一参照包含块行内尺寸,受书写模式影响;
- 普通流下块向
auto解析为0; - 需要推动周边盒子位置使用
margin;只想移动自身、不影响后续布局,选择相对定位偏移。