CSS Gap 布局详解

lishihuan大约 10 分钟

CSS Gap 布局详解

📋 目录

  1. Gap 属性简介
  2. Gap 属性详解
  3. Flex 布局中使用 Gap
  4. Grid 布局中使用 Gap
  5. Gap 与传统间距方案对比
  6. 实战案例
  7. 浏览器兼容性
  8. 最佳实践

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 + GapGrid + Gap
Chrome84+66+
Firefox63+61+
Safari14.1+12+
Edge84+16+
Opera70+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 的优势

  1. 代码简洁:一行代码解决间距问题
  2. 自动处理边界:不需要处理最后一个元素
  3. 易于维护:修改间距只需改一个值
  4. 语义清晰:明确表达间距意图
  5. 响应式友好:配合媒体查询轻松实现响应式

使用建议

  • ✅ 优先使用 gap 属性
  • ✅ 使用 CSS 变量统一管理间距
  • ✅ 考虑浏览器兼容性,提供降级方案
  • ✅ 结合响应式设计调整间距
  • ✅ 保持间距的一致性

学习资源


创建时间:2026-02-04
最后更新:2026-02-04
版本:v1.0