展开文章目录
目录导航
快速定位文章内容
刚开始写布局时,我很长一段时间都在靠 margin 处理间距。
列表项之间加一点。 卡片之间推开一点。 导航按钮之间隔一点。 看起来都能做,而且写起来也很顺手。
但项目做多了之后,我越来越明显地感觉到一件事:
很多“布局越来越乱”的问题,不是因为 CSS 太难,而是因为我一开始就把“组件自身的外边距”和“容器内部的排布间距”混在了一起。
这也是为什么我现在只要是处理一组元素之间的间距,第一反应通常都会先看一句:
能不能直接用 gap?
margin 不是不能用,而是它经常承担了不该它承担的事
先说清楚一点。
margin 本身当然没有问题。
它依然是 CSS 里非常基础、也非常有用的属性。
问题在于,很多时候我们明明想表达的是:
- 这一组元素之间要有固定间距
- 间距由父容器统一控制
- 第一个和最后一个元素不需要特殊处理
但最后却写成了这样:
.list-item {
margin-right: 16px;
}或者:
.card + .card {
margin-top: 20px;
}短期看没问题。 可一旦这个结构开始变化,你很快就会遇到下面这些情况:
- 最后一个元素多出一截空白
- 横排改竖排时,原来的
margin-right全得重写 - 组件被搬到另一个容器里之后,外边距语义不对了
- 嵌套布局一多,间距到底是谁控制的会越来越难看清
也就是说,margin 很容易把“排版责任”塞进组件自己身上。
而我现在更想要的是:
组件只管长什么样。 至于它和别人隔多远,尽量交给外层布局来决定。
gap 更像是在描述“这一组元素怎么排”
gap 的好处,不只是少写几行代码。
更重要的是,它表达得更准。
比如这段:
.list {
display: flex;
gap: 16px;
}这句话的意思非常清楚:
- 这是一个
flex容器 - 容器里的子项彼此相隔
16px - 不需要关心第一个和最后一个元素怎么处理
这其实是在描述“布局关系”。
而不是像 margin 那样,把间距挂在某一个具体元素身上。
这种区别在小 demo 里不明显,但一到真实页面里就特别重要。
因为页面里的结构不是静止的。 它经常会变:
- 横排变竖排
- 一行变多行
- 项目数量变多变少
- 内容从按钮换成卡片
- 组件被复用到别的区域
这时候,如果间距是靠容器统一控制的,整个结构会稳很多。
我现在最常用 gap 的几个场景
1. 按钮组
.actions {
display: flex;
gap: 12px;
}这个场景几乎没什么悬念。 按钮和按钮之间的距离,本来就应该由这一组按钮的容器来控制。
如果你给每个按钮都写 margin-right,那它一旦换行、反向排列,或者插入一个新按钮,维护成本就会上来。
2. 卡片列表
.card-list {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 24px;
}这个比给每张卡片写外边距自然得多。
因为这里真正存在的是“网格之间的间隙”,而不是“某张卡片自己要往外顶 24px”。
尤其在 grid 里,gap 基本就是天选属性。
它和布局模型本身是贴合的。
3. 表单项堆叠
.form-section {
display: flex;
flex-direction: column;
gap: 16px;
}这类纵向结构以前很容易写成:
.form-item {
margin-bottom: 16px;
}但一旦最后一个元素不想留白,或者中间插入帮助文本、错误提示,你就会开始补各种选择器。
换成 gap 之后,这类问题会少很多。
4. 导航、标签、筛选项
.tag-list {
display: flex;
flex-wrap: wrap;
gap: 10px;
}这个场景我现在几乎默认先用 gap。
因为一旦允许换行,margin 的控制就会变得很别扭。
横向间距、纵向间距、最后一列、换行后的对齐,都会变得更麻烦。
而 gap 对“换行后的整体观感”通常更友好。
响应式场景里,gap 会省事很多
我后来越来越喜欢 gap,还有一个很现实的原因:
它特别适合做响应式。
比如:
.list {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 24px;
}
@media (max-width: 768px) {
.list {
grid-template-columns: 1fr;
gap: 16px;
}
}这里你只需要改容器。
子项本身完全不用动。
但如果你之前是靠每个子项的 margin 在撑结构,那响应式时经常会变成:
- 改方向
- 改边距方向
- 去掉最后一个间距
- 调整特殊项
- 修正嵌套样式
最后就会越来越像在补漏洞。
什么时候我还是会继续用 margin
说了这么多,不代表 margin 就该退场。
我现在的习惯更像是这样:
如果我要表达的是“元素和元素之间的编排间距”,优先想 gap。
如果我要表达的是“某个元素需要和外界保持距离”,那还是用 margin。
比如这些场景,我还是会直接用 margin:
1. 文章段落和标题之间的间距
这是排版的一部分,不一定总能抽出一个专门容器统一控制。
2. 某个独立组件和上下文之间的留白
比如一个卡片模块和页面其他区块之间要拉开距离,这时候 margin-top、margin-bottom 依然很直接。
3. 需要刻意制造“这个元素自己往外让一点”的效果
比如徽标、浮动按钮、吸顶条这些局部元素,它们的空间关系更偏组件级,而不是容器级。
所以重点不是“全面禁用 margin”。
而是先想清楚:
这个间距,到底属于组件自己,还是属于父级布局?
一个我现在很在意的小原则
如果某个组件离开当前容器之后,间距定义就显得奇怪,那大概率这个间距本来就不该写在组件自己身上。
这句话对我帮助挺大。
因为它会逼着我区分两件事:
- 组件的样式
- 布局的样式
以前这两件事我经常混着写,结果就是复用一多,结构就开始打架。
现在我会更倾向于这样处理:
.card-list {
display: grid;
gap: 20px;
}
.card {
padding: 16px;
border-radius: 12px;
background: #fff;
}容器负责“卡片之间怎么排”。 卡片自己负责“卡片长什么样”。
这样拆开之后,代码会清楚很多。
最后
我现在不是不用 margin 了,而是不会再第一反应就拿它处理一组元素之间的间距。
因为很多时候,我真正想控制的是布局关系。 而不是给某个元素强行塞一个外边距。
对这类问题来说,gap 的语义更准,结构也更稳。
尤其是列表、卡片、标签、按钮组、表单堆叠、响应式布局这些场景,它通常比 margin 更像是“站在布局这一层解决问题”。
如果一句话总结我现在的习惯,就是:
能由容器统一控制的间距,就尽量别让子项自己背着走。