CSS Gap 布局详解
大约 10 分钟
CSS Gap 布局详解
📋 目录
Gap 属性简介
什么是 Gap?
gap 是 CSS 中用于设置**网格布局(Grid)和弹性布局(Flex)**中元素之间间距的属性。它是一个简写属性,可以同时设置行间距和列间距。
为什么使用 Gap?
传统方式的问题:
/* 传统方式:使用 margin */
.item {
margin-right: 10px;
margin-bottom: 10px;
}
/* 问题:最后一个元素也会有 margin,需要额外处理 */
.item:last-child {
margin-right: 0;
}
使用 Gap 的优势:
- ✅ 代码更简洁
- ✅ 不需要处理最后一个元素
- ✅ 自动计算间距
- ✅ 更符合语义化
- ✅ 维护更方便
Gap 属性详解
语法
/* 单值语法:行列间距相同 */
gap: 10px;
/* 双值语法:行间距 列间距 */
gap: 10px 20px;
/* 等价于 */
row-gap: 10px;
column-gap: 20px;
属性拆分
1. row-gap(行间距)
设置行与行之间的间距(垂直方向)
.container {
display: flex;
flex-wrap: wrap;
row-gap: 20px; /* 行间距 20px */
}
2. column-gap(列间距)
设置列与列之间的间距(水平方向)
.container {
display: flex;
column-gap: 15px; /* 列间距 15px */
}
3. gap(简写属性)
同时设置行间距和列间距
/* 行列间距相同 */
.container {
gap: 10px;
}
/* 行列间距不同 */
.container {
gap: 20px 10px; /* 行间距20px,列间距10px */
}
取值类型
/* 长度值 */
gap: 10px;
gap: 1em;
gap: 2rem;
/* 百分比(仅 Grid 布局支持) */
gap: 5%;
/* calc() 计算 */
gap: calc(10px + 1%);
/* 多个值 */
gap: 10px 20px;
Flex 布局中使用 Gap
基础用法
.flex-container {
display: flex;
gap: 20px; /* 所有子元素之间间距 20px */
}
<div class="flex-container">
<div class="item">Item 1</div>
<div class="item">Item 2</div>
<div class="item">Item 3</div>
</div>
效果:
[Item 1] --20px-- [Item 2] --20px-- [Item 3]
水平布局 + Gap
.flex-row {
display: flex;
flex-direction: row;
gap: 15px;
}
<div class="flex-row">
<div>A</div>
<div>B</div>
<div>C</div>
</div>
效果:
[A] --15px-- [B] --15px-- [C]
垂直布局 + Gap
.flex-column {
display: flex;
flex-direction: column;
gap: 10px;
}
<div class="flex-column">
<div>Top</div>
<div>Middle</div>
<div>Bottom</div>
</div>
效果:
[Top]
↓ 10px
[Middle]
↓ 10px
[Bottom]
换行布局 + Gap
.flex-wrap {
display: flex;
flex-wrap: wrap;
gap: 20px 10px; /* 行间距20px,列间距10px */
}
.flex-wrap .item {
width: 100px;
height: 100px;
}
<div class="flex-wrap">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
</div>
效果:
[1] -10px- [2] -10px- [3]
↓ 20px
[4] -10px- [5] -10px- [6]
实战案例:卡片列表
.card-list {
display: flex;
flex-wrap: wrap;
gap: 20px;
padding: 20px;
}
.card {
width: calc(33.333% - 14px); /* 减去 gap 的影响 */
background: #f5f5f5;
padding: 20px;
border-radius: 8px;
}
/* 响应式 */
@media (max-width: 768px) {
.card {
width: calc(50% - 10px);
}
}
@media (max-width: 480px) {
.card {
width: 100%;
}
}
<div class="card-list">
<div class="card">卡片 1</div>
<div class="card">卡片 2</div>
<div class="card">卡片 3</div>
<div class="card">卡片 4</div>
<div class="card">卡片 5</div>
<div class="card">卡片 6</div>
</div>
Grid 布局中使用 Gap
基础用法
.grid-container {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px; /* 行列间距都是 20px */
}
<div class="grid-container">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
</div>
效果:
[1] -20px- [2] -20px- [3]
↓ 20px ↓ 20px ↓ 20px
[4] -20px- [5] -20px- [6]
不同的行列间距
.grid-container {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 30px 15px; /* 行间距30px,列间距15px */
}
仅设置行间距
.grid-container {
display: grid;
grid-template-columns: repeat(3, 1fr);
row-gap: 20px; /* 只设置行间距 */
}
仅设置列间距
.grid-container {
display: grid;
grid-template-columns: repeat(3, 1fr);
column-gap: 15px; /* 只设置列间距 */
}
实战案例:响应式网格
.photo-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 16px;
padding: 20px;
}
.photo-item {
aspect-ratio: 1 / 1;
background: #ddd;
border-radius: 8px;
overflow: hidden;
}
.photo-item img {
width: 100%;
height: 100%;
object-fit: cover;
}
<div class="photo-grid">
<div class="photo-item"><img src="photo1.jpg" alt=""></div>
<div class="photo-item"><img src="photo2.jpg" alt=""></div>
<div class="photo-item"><img src="photo3.jpg" alt=""></div>
<div class="photo-item"><img src="photo4.jpg" alt=""></div>
</div>
Gap 与传统间距方案对比
方案对比表
| 方案 | 代码量 | 维护性 | 灵活性 | 兼容性 |
|---|---|---|---|---|
| gap | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| margin | ⭐⭐ | ⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| padding | ⭐⭐ | ⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐⭐ |
1. 使用 Gap(推荐)
.container {
display: flex;
gap: 20px;
}
优点:
- ✅ 代码简洁
- ✅ 自动处理边界
- ✅ 易于维护
- ✅ 语义清晰
缺点:
- ❌ 旧浏览器不支持
2. 使用 Margin(传统方式)
.container {
display: flex;
margin: -10px; /* 负边距抵消 */
}
.item {
margin: 10px;
}
优点:
- ✅ 兼容性好
缺点:
- ❌ 代码复杂
- ❌ 需要处理边界
- ❌ 维护困难
3. 使用伪类选择器
.item {
margin-right: 20px;
}
.item:last-child {
margin-right: 0;
}
/* 或者 */
.item:not(:last-child) {
margin-right: 20px;
}
优点:
- ✅ 兼容性好
- ✅ 不需要负边距
缺点:
- ❌ 代码冗余
- ❌ 换行布局处理复杂
4. 使用相邻选择器
.item + .item {
margin-left: 20px;
}
优点:
- ✅ 代码简洁
- ✅ 兼容性好
缺点:
- ❌ 只适用于单行布局
- ❌ 换行布局无效
实战案例
案例1:导航栏
.navbar {
display: flex;
align-items: center;
gap: 30px;
padding: 20px;
background: #333;
}
.nav-item {
color: white;
text-decoration: none;
padding: 10px 15px;
border-radius: 4px;
transition: background 0.3s;
}
.nav-item:hover {
background: rgba(255, 255, 255, 0.1);
}
<nav class="navbar">
<a href="#" class="nav-item">首页</a>
<a href="#" class="nav-item">产品</a>
<a href="#" class="nav-item">服务</a>
<a href="#" class="nav-item">关于</a>
<a href="#" class="nav-item">联系</a>
</nav>
案例2:表单布局
.form-row {
display: flex;
gap: 15px;
margin-bottom: 20px;
}
.form-group {
flex: 1;
display: flex;
flex-direction: column;
gap: 8px;
}
.form-group label {
font-weight: 600;
color: #333;
}
.form-group input {
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
}
<form>
<div class="form-row">
<div class="form-group">
<label>姓名</label>
<input type="text" placeholder="请输入姓名">
</div>
<div class="form-group">
<label>邮箱</label>
<input type="email" placeholder="请输入邮箱">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>电话</label>
<input type="tel" placeholder="请输入电话">
</div>
<div class="form-group">
<label>地址</label>
<input type="text" placeholder="请输入地址">
</div>
</div>
</form>
案例3:商品列表
.product-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 24px;
padding: 20px;
}
.product-card {
background: white;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transition: transform 0.3s, box-shadow 0.3s;
}
.product-card:hover {
transform: translateY(-4px);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
}
.product-image {
width: 100%;
height: 200px;
object-fit: cover;
}
.product-info {
padding: 16px;
display: flex;
flex-direction: column;
gap: 12px;
}
.product-title {
font-size: 18px;
font-weight: 600;
color: #333;
}
.product-price {
font-size: 24px;
color: #e74c3c;
font-weight: bold;
}
.product-actions {
display: flex;
gap: 10px;
margin-top: 8px;
}
.btn {
flex: 1;
padding: 10px;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: 600;
transition: opacity 0.3s;
}
.btn:hover {
opacity: 0.8;
}
.btn-primary {
background: #3498db;
color: white;
}
.btn-secondary {
background: #ecf0f1;
color: #333;
}
<div class="product-list">
<div class="product-card">
<img src="product1.jpg" alt="商品1" class="product-image">
<div class="product-info">
<h3 class="product-title">商品名称</h3>
<p class="product-price">¥299</p>
<div class="product-actions">
<button class="btn btn-primary">立即购买</button>
<button class="btn btn-secondary">加入购物车</button>
</div>
</div>
</div>
<!-- 更多商品卡片... -->
</div>
案例4:标签云
.tag-cloud {
display: flex;
flex-wrap: wrap;
gap: 12px;
padding: 20px;
}
.tag {
padding: 8px 16px;
background: #f0f0f0;
border-radius: 20px;
font-size: 14px;
color: #666;
cursor: pointer;
transition: all 0.3s;
}
.tag:hover {
background: #3498db;
color: white;
transform: scale(1.05);
}
.tag.active {
background: #2ecc71;
color: white;
}
<div class="tag-cloud">
<span class="tag">JavaScript</span>
<span class="tag">CSS</span>
<span class="tag">HTML</span>
<span class="tag active">Vue.js</span>
<span class="tag">React</span>
<span class="tag">Node.js</span>
<span class="tag">TypeScript</span>
<span class="tag">Webpack</span>
</div>
案例5:仪表盘布局
.dashboard {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
padding: 20px;
}
.dashboard-card {
background: white;
padding: 24px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
gap: 16px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.card-title {
font-size: 16px;
font-weight: 600;
color: #666;
}
.card-value {
font-size: 32px;
font-weight: bold;
color: #333;
}
.card-footer {
display: flex;
gap: 8px;
align-items: center;
font-size: 14px;
color: #999;
}
<div class="dashboard">
<div class="dashboard-card">
<div class="card-header">
<span class="card-title">总销售额</span>
<span>📈</span>
</div>
<div class="card-value">¥126,560</div>
<div class="card-footer">
<span style="color: #2ecc71;">↑ 12%</span>
<span>较上月</span>
</div>
</div>
<div class="dashboard-card">
<div class="card-header">
<span class="card-title">订单数量</span>
<span>📦</span>
</div>
<div class="card-value">1,234</div>
<div class="card-footer">
<span style="color: #2ecc71;">↑ 8%</span>
<span>较上月</span>
</div>
</div>
<div class="dashboard-card">
<div class="card-header">
<span class="card-title">新用户</span>
<span>👥</span>
</div>
<div class="card-value">456</div>
<div class="card-footer">
<span style="color: #e74c3c;">↓ 3%</span>
<span>较上月</span>
</div>
</div>
</div>
浏览器兼容性
支持情况
| 浏览器 | Flex + Gap | Grid + Gap |
|---|---|---|
| Chrome | 84+ | 66+ |
| Firefox | 63+ | 61+ |
| Safari | 14.1+ | 12+ |
| Edge | 84+ | 16+ |
| Opera | 70+ | 53+ |
兼容性检测
// 检测浏览器是否支持 gap
function supportsGap() {
const flex = document.createElement('div');
flex.style.display = 'flex';
flex.style.gap = '1px';
return flex.style.gap === '1px';
}
if (supportsGap()) {
console.log('浏览器支持 gap 属性');
} else {
console.log('浏览器不支持 gap 属性,使用降级方案');
}
降级方案
/* 方案1:使用 @supports */
.container {
display: flex;
flex-wrap: wrap;
}
/* 不支持 gap 时使用 margin */
.container .item {
margin: 10px;
}
/* 支持 gap 时覆盖 margin */
@supports (gap: 20px) {
.container {
gap: 20px;
}
.container .item {
margin: 0;
}
}
/* 方案2:使用 CSS 变量 */
:root {
--gap: 20px;
}
.container {
display: flex;
flex-wrap: wrap;
margin: calc(var(--gap) * -0.5);
}
.item {
margin: calc(var(--gap) * 0.5);
}
/* 支持 gap 时 */
@supports (gap: 20px) {
.container {
gap: var(--gap);
margin: 0;
}
.item {
margin: 0;
}
}
最佳实践
1. 使用 CSS 变量统一管理间距
:root {
--gap-xs: 4px;
--gap-sm: 8px;
--gap-md: 16px;
--gap-lg: 24px;
--gap-xl: 32px;
}
.container-sm {
display: flex;
gap: var(--gap-sm);
}
.container-md {
display: flex;
gap: var(--gap-md);
}
.container-lg {
display: flex;
gap: var(--gap-lg);
}
2. 响应式间距
.responsive-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 16px;
}
@media (min-width: 768px) {
.responsive-grid {
gap: 24px;
}
}
@media (min-width: 1024px) {
.responsive-grid {
gap: 32px;
}
}
3. 结合其他布局属性
.card-container {
display: flex;
flex-wrap: wrap;
gap: 20px;
justify-content: center; /* 居中对齐 */
align-items: stretch; /* 等高 */
}
4. 嵌套布局
.outer-container {
display: flex;
flex-direction: column;
gap: 40px; /* 外层大间距 */
}
.inner-container {
display: flex;
gap: 16px; /* 内层小间距 */
}
5. 避免过度使用
/* ❌ 不推荐:间距过大 */
.container {
gap: 100px;
}
/* ✅ 推荐:合理的间距 */
.container {
gap: 20px;
}
6. 语义化命名
/* ✅ 推荐 */
.product-grid {
display: grid;
gap: 24px;
}
.nav-links {
display: flex;
gap: 30px;
}
/* ❌ 不推荐 */
.box1 {
display: flex;
gap: 10px;
}
常见问题
Q1: Gap 会影响容器的总宽度吗?
答: 不会。Gap 只影响子元素之间的间距,不会增加容器的宽度。
.container {
width: 500px;
display: flex;
gap: 20px;
}
/* 如果有 3 个子元素,每个子元素的可用宽度是:
(500px - 2 * 20px) / 3 = 153.33px */
Q2: Gap 可以使用负值吗?
答: 不可以。Gap 不接受负值,如果设置负值会被忽略。
/* ❌ 无效 */
.container {
gap: -10px;
}
Q3: Gap 在 Flex 和 Grid 中有区别吗?
答: 基本相同,但 Grid 中可以使用百分比,Flex 中不行。
/* Grid 中可以使用百分比 */
.grid {
display: grid;
gap: 5%; /* ✅ 有效 */
}
/* Flex 中百分比可能不生效 */
.flex {
display: flex;
gap: 5%; /* ⚠️ 部分浏览器不支持 */
}
Q4: 如何在不支持 Gap 的浏览器中使用?
答: 使用 @supports 特性检测和降级方案。
.container {
display: flex;
margin: -10px; /* 降级方案 */
}
.item {
margin: 10px; /* 降级方案 */
}
@supports (gap: 20px) {
.container {
gap: 20px;
margin: 0;
}
.item {
margin: 0;
}
}
总结
Gap 的优势
- 代码简洁:一行代码解决间距问题
- 自动处理边界:不需要处理最后一个元素
- 易于维护:修改间距只需改一个值
- 语义清晰:明确表达间距意图
- 响应式友好:配合媒体查询轻松实现响应式
使用建议
- ✅ 优先使用
gap属性 - ✅ 使用 CSS 变量统一管理间距
- ✅ 考虑浏览器兼容性,提供降级方案
- ✅ 结合响应式设计调整间距
- ✅ 保持间距的一致性
学习资源
- MDN - gap
- CSS-Tricks - A Complete Guide to Flexbox
- CSS-Tricks - A Complete Guide to Grid
- Can I Use - gap
创建时间:2026-02-04
最后更新:2026-02-04
版本:v1.0