Skip to content

Web 设计师在设计 Web 页面时,UI 元素与元素之间是有一定的间距的,而且设计和用户往往都是通过元素之间的间距的大小来判断它们之间的关系,尤其是多个元素分组的情况之下。如果元素之间没有任何间距的话,页面除了没有美感之外,用户访问也很困难,而且用户在浏览页的时候不知道哪些元素是相关的,哪些是不相关的,会给用户带来一定的困惑。

正因如此,Web 设计师在设计 Web 页面时,就会设计元素之间的间距。而 Web 开发者在还原 UI 设计稿时,也可以通过不同的方式来设置元素之间的间距。

在 CSS 中,Web 开发者可以使用 marginpaddinggap 以及 CSS 定位(toprightbottomleft)等手段来设置元素之间的间距。除此之外,在一些现代 Web 布局中,比如 CSS Flexbox 和 CSS Grid 布局,还可以通过 CSS 的对齐方式来控制元素之间的间距。

img

虽然 CSS 实现元素之间间距的方法很多,但很多 Web 开发者分不清楚它们的使用场景。比如说:

  • 什么时候应该使用 margin
  • 什么时候应该使用 padding
  • 什么时候应该使用 gap
  • ……

所以,在这节课中,我将通过一些实际案例来告诉大家,在 CSS 中应该如何设置元素之间的间距。而且,在设置元素间距时,我们还需要注意哪些事项,有哪些手段可以让你设置间距时变得更灵活。使你构建的 Web 页面除了有一定的美感之外,还能提高 Web 的可访问性。

CSS 中间距的类型和差异

CSS 的间距主要分为两种类型,一种是外间距 ,另一种是内间距。这主要是根据 CSS 属性的功能来划分的,其中:

  • 外间距,主要是用于设置元素盒子外部之间的间距,一般盒子元素主要通过 CSS 的 margin 属性来设置,在 CSS Flexbox 和 Grid 布局中,还可以通过 CSS 的 gap 属性来设置。
  • 内间距,主要是用于设置元素盒子框边缘和其内容之间的间距,主要通过 CSS 的 padding 属性来设置。

这主要是根据 CSS 的盒模型特性来划分的:

img

我们来看一个真实的示例:

HTML
<body>
    <div class="box">Box Content</div>
    <div class="box">Box Content</div>
</body>
CSS
.box {
    padding: 10vh;
    margin-bottom: 10vh;
}

img

Demo 地址:https://codepen.io/airen/full/PodzNax

很简单,对吧!然而,在处理具有大量细节和子元素的组件时,这可能会变得越来越复杂。比如下图:

img

使用不同的方式都可以使组件内部元素之间的间距达到相同的效果。既然如此,那哪一种才是最优的呢?答案并不复杂,它们的使用和组件的 HTML 结构有着紧密的关联。

假设你编写的 HTML 结构如下:

HTML
<div class="card">
    <figure><img src="card-thumbnail.jpg" alt="Card Thumbnail" /></figure>
    <div class="card__body">
        <h3>Card Title</h3>
        <p>Card Description</p>
    </div>
</div>

正如我前面提到的,padding 在元素内部添加一个内间距。它的目标可以根据所使用的情况有所不同;margin 在元素与元素之间添加一个外间距。我们从卡片组件中不难发现:

  • 卡片的缩略图(<figure>)和卡片主体(.card__body)在垂直方向有一个间距,它更符合使用 margin 来设置;
  • 卡片的标题(<h3>)和卡片的描述(<p>)在垂直方向有一个间距,它同样更符合使用 margin 来设置;
  • 卡片的标题(<h3>)和卡片的描述(<p>)在水平方向两侧都与其容器元素(.card__body)之间有一定的间距,以及 p 元素底部与 .card__body 之间也有一定的间距;而且 h3p 两个元素都属于 .card__body 元素的内容,它更适合在 .card__body 元素上使用 padding 来设置。
CSS
figure {
    margin-bottom: 40px; 
}

.card__body {
    padding: 0 40px 40px;
}

.card h3 {
    margin-bottom: 20px;
}

其实,你也可以将 figure.card__body 两元素之间的间距视为 0 (没有外间距),但需要将.card__body 容器和其内容(h3p)设置一个内距。如此一来,还可以像下面这样设置间距:

CSS
.card__body {
    padding: 40px;
}

.card h3 {
    margin-bottom: 20px;
}

img

Demo 地址:https://codepen.io/airen/full/BaOzzgL

你可能还会把 HTML 结构更扁平化,比如:

HTML
<div class="card">
    <figure><img src="card-thumbnail.jpg" alt="Card Thumbnail" /></figure>
    <h3>Card Title</h3>
    <p>Card Description</p>
</div>

和前面示例相比,HTML 结构少了一个 div.card__body 元素。基于该结构而言,如果直接在 .card 设置padding ,虽然能满足元素 h3p 与其父元素之间的间距需求,但会造成 figure 元素水平方向也会和 .card 有个内距。不过幸运的是,可以给 figure 水平方向设置 margin 的负值,会让该元素向水平方向两则拉伸。

CSS
.card {
    padding: 0 40px 40px;
}

.card figure {
    margin: 0 -40px 40px; /* margin 负值的绝对值要和 .card 的 padding 水平方向的值相等 */
}

.card h3 {
    margin-bottom: 20px;
}

另外一种方式,就是 .card 元素只设置 padding-bottom ,其他元素通过 margin 来控制与其父元素之间的间距:

CSS
.card {
    padding-bottom: 40px;
}

.card > *:not(figure) {
    margin-left: 40px;
    margin-right: 40px;
}

.card figure {
    margin-bottom: 40px;
}

.card h3 {
    margin-bottom: 20px;
}

最为暴力的是,.card 的子元素都通过 margin 来控制它们的间距:

CSS
.card > *:not(h3) {
    margin-bottom: 40px;
}

.card > *:not(figure) {
    margin-left: 40px;
    margin-right: 40px;
}

.card h3 {
    margin-bottom: 20px;
}

img

Demo 地址:https://codepen.io/airen/full/wvEWzbj

除此之外,内距 padding 和外距 margin 还有两个最大的差异:

  • padding 不可以设置负值,而 margin 却可以;
  • padding 没有叠加的概念,而 margin 却有。

CSS 中除了使用 paddingmargin 设置间距之外,还可以使用 gap 属性来设置间距,只不过到目前为止,gap 属性只可以用于 CSS Flexbox、CSS Grid 和 CSS 多列布局中

CSS
.flex--container {
    display: flex; /* or inline-flex */
    gap: 10px 20px;
    
    /* 等同于 */
    
    row-gap: 10px;
    column-gap: 20px;
}

.grid--container {
    display: grid; /* or inline-grid */
    gap: 10px 20px;
    
    /* 等同于 */
    
    row-gap: 10px;
    column-gap: 20px;
}

.multi--column {
    column-gap: 20px;
}

gapmargin 属性有点相似,都是用来设置元素与元素之间的间距,它无法用来设置内容与元素边框之间的间距。虽然说,gapmargin 有点相似,但它们还是有本质上差异的,如下图所示:

img

正如上图所示,在还没有 gap 属性时,只能使用 margin 来实现上图中“设计师所期望的效果”,只不过需要一些 Hack 手段。随着 gap 属性的出现,实现“设计师所期望的效果”不再是件难事了。或许你又会问,那什么时候使用 margin ,什么时候使用 gap 呢?

原则上,只要元素是一个 Flex 容器或网格容器,它们的子元素(Flex 项目或网格项目)之间的间距都可以使用 gap 属性。不过它有一个局限性,就是所有元素之间的间距需要是相等的。如果元素之间的间距不相等,则无法使用 gap 来控制。比如上图中的卡片示例,它就不太适合使用 gap 属性:

img

当然,你坚持要使用 gap 来控制元素之间的间距,也不是不可以,只不过需要在 HTML 的结构上做出牺牲,即 需要 Flex 或 Grid 的嵌套:

HTML
<!-- Card 1: 不适合使用 gap 设置元素间距的 HTML 结构 -->
<div class="card">
    <figure><img src="thumbnail.png" alt="Card Thumbnail"/></figure>
    <h3>Card Title</h3>
    <p>Card Description</p>
</div>

<!-- Card 2: 适合使用 gap 设置元素间距的 HTML 结构 -->
<div class="card"><!-- Flex 或 Grid 容器 -->
    <figure><img src="thumbnail.png" alt="Card Thumbnail"/></figure>
    <div class="card__body"><!-- Flex 或 Grid 容器 -->
        <h3>Card Title</h3>
        <p>Card Description</p>
    </div>
</div>
CSS
/* Card 1: 使用 margin 控制元素之间的间距 */
.card:nth-child(1) {
    padding: 0 40px 40px;
}

.card:nth-child(1) figure {
    margin: 0 -40px 40px;
}

.card:nth-child(1) h3 {
    margin-bottom: 20px;
}

/* Card 2: 使用 gap 控制元素之间的间距 */
.card:nth-child(2) {
    display: flex;
    flex-direction: column;
    gap: 40px;
    padding: 0 40px 40px;
}

.card:nth-child(2) .card__body {
    display: flex;
    flex-direction: column;
    gap: 20px;
    flex: 1 1 0%; /* 根据 Flex 容器的空间进行扩展或收缩 */
}

.card:nth-child(2) figure {
    margin-left: -40px;
    margin-right: -40px;
    
    /* 等同于 */
    margin-inline: -40px;
}

.card:nth-child(2) p {
    flex: 1 1 0%; /* 根据 Flex 容器的空间进行扩展或收缩 */
}

img

Demo 地址:https://codepen.io/airen/full/LYJZXwa

这个示例也从侧面说明了,gap 属性更适合于控制组件之间的间距 。而元素之间的间距,尤其是不同大小间距的时候,使用 margin 更方便。

img

上图中有两个卡片组件(<Card>),每个卡片组件都由 <CardHeader> 和多个 <Item> 组件构成。假设它们的结构是:

HTML
<div class="container">
    <Card />
    <Card />
</div>

<!-- Card 组件 -->
<div class="card">
    <CardHeader />
    <div class="card__body">
        <Item />
        <!-- 这里有多个 Item -->
    </div>
</div>

<!-- CardHeader 组件 -->
<div class="card__heading">
    <h3>Card Title</h3>
    <svg />
</div>

<!-- Item 组件 -->
<div class="item">
    <svg />Item Name
</div>

关键的 CSS 代码如下:

CSS
.container {
    padding: 10px;
    display: flex;
    flex-direction: column;
    gap: 20px;
}

.card {
    padding: 16px;
}

.card__heading {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 24px;
}

.card__body {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 32px 16px;
}

.item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}

.card__heading svg {
  margin-left: auto;
}

img

Demo 地址:https://codepen.io/airen/full/gOdMqMo

通过 CSS 属性设置元素之间的间距主要就 paddingmargingap 了。如果你需要给元素设置间距时,可以先这样问一下自己:

  • 是容器内容(指容器子元素)距容器边缘的间距?如果是,请使用 padding
  • 是元素与元素之间的间距?如果是,请使用 margin
  • 是组件与组件之间的间距?如果是,请使用 padding

如果你在 margingap 之间选择拿不定主意,你也可以这样问一下自己:“是 Flex 项目或 Grid 项目之间的间距?Flex 项目或 Grid 项目之间间距都相等?如果是,请使用 gap; 如果不是,请使用 margin ”。

防患于未然

在 CSS 中,如何给元素设置间距?我想你已经了然于胸了。即使是这样,也并不代表着就万事大吉。为什么呢?在给出答案之前,我们先一起来看一张图:

img

优秀的 Web 设计师(经验丰富),你可以从他提供的设计稿上看到同一组件在不同状态下的展示效果,即使没有在设计稿上提供,也会在设计稿上标注出来,尤其是在一些极端或边缘的环境条件之下。

如果你运气不好,你拿到的设计稿是没有那么多状态展示的,比如卡片标题不同长度的应对方式。这个时候,作为一名优秀的 Web 开发者,你需要有“防患于未然”思想,多考虑些不同的条件下的效果,以防万一。这样做的好处,那就是你构建的 Web 不会那么容易被其他因素而干扰。

这个道理同样适用于 CSS 间距设置上。就拿上图中的卡片标题来说,服务端吐出的标题内容可能会比设计稿中展示的多(毕竟设计稿是一种静态数据模板)。不管你使用哪种布局方式,我们都需要考虑标题内容过长时应该如何展示,比如标题和图标之间需要设置一个间距。

CSS
.card__heading {
    display: flex;
    align-items: center;
}

.card__heading svg {
    margin-left: auto; /* 让Icon图标居右 */
}

img

Demo 地址:https://codepen.io/airen/full/LYJZvrE

面对如此场景,优秀的 Web 开发者总是会给“标题”和“图标”之间设置一个间距。

CSS
/* CSS Flexbox Layout */
.card__heading {
    display: flex;
    align-items: center;
    gap: 1rem;
}

.card__heading svg {
    margin-left: auto; /* 让Icon图标居右 */
}

/* CSS Grid Layout */
.card__heading {
    display: grid;
    align-items: center;
    grid-template-columns: minmax(0, 1fr) min-content;
    gap: 1rem;
}

.card__heading svg {
    margin-left: auto; /* 让Icon图标居右 */
}

img

Demo 地址:https://codepen.io/airen/full/wvEWbXR

看上去很完美,是吧?

事实上,只能说部分完美,即 CSS Flexbox 实现的布局是完美的,CSS Grid 实现的布局还是有一定的缺陷在。比如,有可能个别卡片标题的右侧是没有 Icon 图标的。在这种情况下,CSS Grid 布局中的列轨道间距会占用一定的空间,致使标题没到容器右侧边缘就断行:

img

Demo 地址:https://codepen.io/airen/full/jOvrojO

换句话说,考虑到图标有可能不出现的情景,使用 CSS Grid 布局时,就不能使用 gap 属性来设置间距了。更妥当的方案是,在图标上设置 margin-left

CSS
/* CSS Grid Layout */
.card__heading {
    display: grid;
    grid-template-columns: minmax(0, 1fr) min-content;
}

.card__heading h3 + svg {
    margin-left: 1rem;
}

img

Demo 地址:https://codepen.io/airen/full/dyqXBMK

现在,卡片标题和其右侧图标之间的间距的设置已然很完美了。当然,h3 + svg 这样的设置图标 margin-left 方法同样也可以用于 CSS Flexbox 布局中。

CSS
/* 方案一 */
.card__heading {
    display: flex;
    align-items: center;
    justify-content: space-between; /* 两端对齐,标题居中,图标居右 */
}

.card__heading h3 + svg {
    margin-left: 1rem;
}

/* 方案二 */
.card__heading {
    display: flex;
    align-items: center;
}

.card__heading h3 {
    flex: 1 1 0%; /* 将图标挤到最右侧,相当于图标居右 */
    min-width: 0;
}

.card__heading h3 + svg {
    margin-left: 1rem;
}

注意,这里运用了 CSS 兄弟相邻选择器(E + F)的功能。要让该选择器生效,HTML 结构是有相应约束的,即 元素 F 紧跟在元素 E 后面:

HTML
<!-- E + F 选择器有效的 HTML 结构 -->
<div class="box">
    <div class="E">E</div>
    <div class="F">F</div>
</div>

<!-- E + F 选择器无效的 HTML 结构 -->
<div class="box">
    <div class="E">E</div>
    <img />
    <div class="F">F</div>
</div>

这个选择器功能可以帮助我们更好地设置元素之间的间距,尤其是适用于多个并排元素之间间距的设置。比如像下图所示,有两个相邻的按钮,只是希望在按钮之间有一个间距:

img

我们可以像下面这样来设置按钮之间的间距:

CSS
.button + .button {
    margin-left: 1rem;
}

它是说:“如果一个按钮紧挨着另一个按钮,以防万一,给第二个按钮加一个 margin-left”。这样可以很好地处理动态数据输出所产生的额外问题。比如下图这两个模态框,有时候它只有一个按钮,有时候它会有两个按钮。我们使用上面代码就可以很好地避免两个按钮出现时,它们之间没有任何间距:

img

事实上,在 Web 开发中,这样的场景特别得多。

img

可以采用相同的方式来设置间距:

CSS
.cards + .cards {
    margin-top: 20px;
}

.card + .card {
    margin-left: 20px;
}

.button + .button {
    margin-left: 20px;
}

另外,在 Web 布局中,还有像下图这样的场景,会在容器中设置一个内距(padding),然后元素与元素之间设置一个margin-bottom,造成最后一个元素与容器之间的间距明显变大:

img

Demo 地址:https://codepen.io/airen/full/BaOLwBm

代码可能像下面这样写的:

CSS
.cards {
    padding: 20px;
}

.card {
    margin-bottom: 20px;
}

同样的,使用 CSS 的兄弟相邻选择器就可以避免这种现象,达到设计师所期望的效果:

CSS
.cards {
    padding: 20px;
}

.card + .card {
    margin-top: 20px;
}

其实,除了使用 CSS 的兄弟相邻选择器之外,我们还可以使用 CSS 的 :not() 选择器,比如:

CSS
.card:not(:first-child){
    margin-top: 20px;
}

/* 或者 */
.card:not(:last-child) {
    margin-bottom: 20px;
}

你可能也已经想到了,前面卡片标题加图标之间的间距,也是可以使用 E + F 选择器,或 :not() 选择器来实现,而且都让你的代码具备相应的防御性:

CSS
.card__heading h3 + svg {
    margin-left: 20px;
}

/* 或者 */
.card__heading > *:not(h3) {
    margin-left: 20px;
}

img

Demo 地址:https://codepen.io/airen/full/eYLdeae

再回过头来往前看,在使用 CSS Grid 布局时,在 Grid 网格容器上设置 gap 值之后,要是标题后面没有 Icon 图标,gap 设置的间距也依然存在。虽然上面介绍了多种技术方案可以避免,但是在 CSS 中还有另外一种解决方案,即 CSS 的父选择器 :has()

通过 :has() 选择器来做判断,当它有 Icon 图标时,通过 :has(svg) 选择器给 .card__heading 设置 gap 值,如果没有,则让 gap 的值为 0 (即不设置间距):

CSS
/* CSS Flexbox Layout */
.card__heading {
    display: flex;
    align-items: center;
    gap: 0;
}

.card__heading:has(svg) {
    gap: 20px;
}

/* CSS Grid Layout */
.card__heading {
    display: grid;
    align-items: center;
    gap: 0;
}

.card__heading:has(svg) {
    gap: 20px;
}

img

Demo 地址: https://codepen.io/airen/full/gOdwowa

上面我们所探讨的都是元素与元素之间的间距,应该如何设置才能防患于未然。接下来,我们再来讨论一下内距 padding ,看看内距的设置会给 Web UI 带来哪些缺陷。

继续拿上面卡片为例。在每张卡片(.card)上可能会设置一定的 padding 值,但有的时候,可能会因为某个原因,卡片上的数据(内容)并没有正常的输出,输出的效果可能会像下面这样:

img

Demo 地址:https://codepen.io/airen/full/zYJKpzm

卡片没有内容的时候,由于 padding 的原因,它会有一个空白区域存在,让 Web UI 失去一定的美感,也让 Web 设计师无法接受。当然,对于 Web 开发者而言,它很难掌握卡片上的数据(内容)会不会输出,为了避免这种现象存在,我们在开发的时候,可以考虑使用 CSS 的 :empty 选择器来重置卡片的 padding0

CSS
.card {
    padding: 20px;
}

.card:empty {
    padding: 0px;
    border: none;
}

不过,需要注意的是,:empty 可以让你选择 空元素 ,只不过空元素是指没有任何内容的元素,甚至空格都不行 。即:

HTML
<div class="card"></div>

上面这个示例是因为内容不在,容器 padding 影响了整个 UI 效果。接下来这个示例是 padding 在视觉上被丢失。

img

上图中的水平滚动容器(或列表) 已经成为一种常见的 Web 布局,在移动端上,它有助于减少屏幕较小的设备的垂直空间。这种设计必须有一个视觉提示,告诉用户内容是水平滚动的。最好的方法是,让一部分可滚动的内容露出来。另外一个重点是要告诉用户滚动何时结束,最好的方法是在列表末尾使用额外的空间。

img

要实现一个水平滚动容器布局的效果,对于 Web 开发者来说是件很容易的事情。

HTML
<div class="cards">
    <Card />
    <Card />
    <!-- 滚动容器中的其他卡片 -->
</div>
CSS
.cards {
    display: flex;
    gap: 10px;
    padding: 18px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    overscroll-behavior: contain;
}

.card {
    scroll-snap-align: start;
    scroll-snap-stop: always;
    flex: 0 0 120px;
}

img

不难发现,滚动容器 .cards 的内距 padding 在视觉上被丢失了:

  • 默认情况之下,滚动容器水平两侧的 padding 在视觉上都被丢失;
  • 当滚动到容器最右侧时,滚动容器左侧的 padding 在视觉上被丢失;
  • 当滚动到容器最左侧时,滚动容器右侧的 padding 在视觉上被丢失。

img

而设计师可能要的滚动容器效果如下图所示:

img

现在,我们在滚动容器 .cards 上使用 scroll-padding 属性,并且将其值设置为 padding 属性的值,即:

CSS
.cards {
    scroll-padding: 18px;
}

这个时候,你会发现:

  • 默认情况下,滚动容器右侧的 padding 在视觉上依旧还是丢失状态;
  • 滚动到容器最右侧,滚动容器左侧的 padding 在视觉上也还是被丢失状态。

img

Demo 地址:https://codepen.io/airen/full/rNZMpbP

如果你希望还原出来的滚动容器布局效果达到 Web 设计师预期的效果,则需要把运用于滚动容器 .cardspadding 替换成 border ,并且把 border-color 设置为透明的:

CSS
.cards {
    border: 18px solid transparent;
    padding: 0;
}

img

Demo 地址:https://codepen.io/airen/full/YzOpVRQ

其实,这种 Hack 手段也不是完美的。如果滚动容器的 UI 原本就带有边框效果,那么使用透明边框来模拟滚动容器的内距的话,原本的边框就需要使用其他的 CSS 属性来实现,这样做反而得不偿失。

如果你希望从根本上解决问题,我们还是需要对 HTML 结构做出一定的调整,在滚动容器外增加一层包裹元素:

HTML
<div class="scroll--wrapper">
    <div class="cards">
        <Card />
        <!-- 这里有很多个 Card -->
    </div>
</div>

并且把运用滚动容器的 padding 移到其包裹容器中:

CSS
.scroll--wrapper {
    padding: 18px;
}

.cards {
    padding: 0;
}

注意,如果滚动容器有其他 UI 效果,最好是将它们移到 .scroll--wrapper 上,滚动容器上只承载滚动的能力:

CSS
.scroll--wrapper {
    padding: 18px;
    margin: 1rem;
    border-radius: 4px;
    border: 1px solid rgb(0 0 0 / .01);
    box-shadow: 0 0 .25em .125em rgb(0 0 0 / .125);
    background-color: #fff;
    color: #333;
}

.cards {
    display: flex;
    gap: 10px;
    scroll-padding: 18px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    padding: 0;
    scroll-behavior: smooth;
    overscroll-behavior: contain;
}

img

Demo 地址:https://codepen.io/airen/full/bGxBWJm

设置元素间距的其他方式和需要注意的事项

在 CSS Flexbox 或 CSS Grid 布局中,我们还可以使用 CSS 对齐方式来控制元素(Flex 项目或 Grid 项目)之间的间距。比如,Flexbox 布局中,在 Flex 容器中设置 justify-content: space-between 会让 Flex 项目在 Flex 容器中沿着主轴方向两端对齐:

CSS
.flex--container {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
}

img

Flex 项目之间的间距并不是 margingap 属性设置的值。通过对齐方式分配了 Flex 容器的剩余空间,仅是在视觉上让 Flex 项目之间有一定的距离。但是,当 Flex 项目增加或减少时,布局效果就会看上去很奇怪:

img

除了 space-between 之外,还有 space-aroundspace-evenly 有相似的现象。

正如上图所示,当 Flex 项目增加或减少时,效果都是不友好的。在 CSS 中可以使用不同的方式来处理,比如:

  • 在 Flex 项目上使用 margin 设置外边距,只不过某些场景需要使用一定的 Hack 手段,比如与 Flex 容器边缘没有任何间距的情况下;
  • 增加空元素来占位,灵活度不够;
  • 在 Flex 容器上设置 gap

最为简单的方法是使用 gap

CSS
.wrapper {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
}

这样做可以让 Flex 项目或 Grid 项目之间有一个固定的间距,只不当 Flex 项目或 Grid 项目数量过少时,Flex 容器或 Grid 容器会有一定的剩余空间存在。这个时候,你可以考虑让 Flex 项目或 Grid 项目是一个具有伸缩性的项目,即 Flex 项目或 Grid 项目能根据容器剩余空间(或不足空间)自动伸缩。比如,在 Flex 项目设置 flex 值:

CSS
.card {
    flex: 1 1 calc(100% / 3 - var(--gutter));
}

img

Demo 地址:https://codepen.io/airen/full/XWPNRvm

除了上面所描述的方式可以设置元素之间的间距之外,CSS 中还可以使用 position 属性,让元素脱离文档流,再结合 toprightbottomleftinset 属性来设置间距。它虽然不是直接设置元素间距的方式,但是在某些 Web 布局中可以发挥很强的作用。例如,一个绝对定位的元素(position: absolute)需要被定位在距离其父元素左边和顶边 1rem 的位置:

img

Demo 地址:https://codepen.io/airen/full/rNZWwQm

如上图所示,“收藏”图标距离卡片容器的左上角边缘保持一定的距离。在这种情况下,使用以下 CSS:

CSS
.card {
    position: relative;
}

.icon__wrapper {
    position: absolute;
    top: 1rem;
    left: 1rem;
}

小结

元素与元素之间设置间距,除了有利于提高 UI 的美感之外,还有易于提高 Web 的可读性。 正如课程中所述,设置元素之间间距有很多种方式。对于 Web 开发者而言,我们需要知道:

  • margingap 都可以用来设置元素与元素之间的外间距,一般情况下,Flexbox 和 Grid 布局中常用 gap 属性来设置项目之间的间距,但有一个前提条件,那就是所有项目之间的间距相等;
  • padding 用来设置容器边缘与其内容之间的间距;
  • 在 Flexbox 和 Grid 布局中,还可以使用对齐方式来设置项目之间的间距,但很容易因项目增加或减少造成 Web 布局上的不美观,因此应该尽可能避免使用对齐方式来控制项目之间的间距;
  • 在某些特殊的布局场景,还可以使用 CSS 的定位来设置元素之间的间距。

上面几条只是 CSS 中设置间距的常见方式,在实际使用的时候,我们还可以结合 CSS 的其他特性,比如 CSS 的兄弟相邻选择器(E + F)、否定选择器 :not() 、父选择器 :has() ,伪类选择器 :last-child:first-child ,根据相应条件来设置元素之间的间距,使其能更好匹配动态数据输出,不必因数据新增或减少重新手动修改 CSS 代码。让你编写的代码能更好适用不同的场景与环境。