HTML and CSS course ยท Module 4: Flexbox

Gap Property - Modern Spacing in Flexbox

11 min read ยท
In this lesson7

Welcome back! Three cards in a row need spacing between them, but not at the edges of the container. For years we did this with margins and tricks with negative values. Egyptian scribes also watched the spacing between hieroglyphs, because readability depended on it. Meet the gap property, a modern and clean way to create spacing in Flexbox.

What is Gap?

Gap is a modern CSS property, widely supported since 2021, that creates spacing between flex items without using margin.

Before gap - the old way

In the past, every element got a margin on all sides, and the container got a negative margin that ate up the excess at the edges:

/* Old way - complicated and problematic */
.container {
  display: flex;
  margin: -10px;  /* Negative margin on container */
}

.item {
  margin: 10px;  /* Margin on each element */
}

/* Problem: Margin is also on the outer edges! */

It works, but you have to keep an eye on two places at once, because the margins of the elements also land on the outer edges.

With gap - the new way

With gap, the whole spacing is described by a single declaration on the container, and the elements need no margins at all:

/* New way - clean and simple */
.container {
  display: flex;
  gap: 20px;  /* Spacing only BETWEEN elements */
}

.item {
  /* No margin needed! */
}

/* Gap does NOT add spacing on the outer edges! */

Gap adds spacing only between elements, never at the edges of the container. It is like the spacing between hieroglyphs carved by a master: precise, even and without any extra space.

Gap Syntax

Gap accepts one or two values, and instead of the shorthand you can use two separate properties:

/* One dimension - same spacing horizontally and vertically */
.container {
  gap: 20px;
}

/* Two dimensions - row-gap and column-gap */
.container {
  gap: 20px 40px;  /* 20px vertically, 40px horizontally */
}

/* Or separately */
.container {
  row-gap: 20px;
  column-gap: 40px;
}

With two values, the first is the spacing between rows and the second between columns, so gap: 20px 40px means the same as row-gap: 20px and column-gap: 40px.

Gap in Different Flexbox Directions

Gap works in every direction of the main axis, and with wrapping also between lines:

/* flex-direction: row (default) */
.container-row {
  display: flex;
  flex-direction: row;
  gap: 20px;  /* Spacing between elements horizontally */
}

/* flex-direction: column */
.container-column {
  display: flex;
  flex-direction: column;
  gap: 20px;  /* Spacing between elements vertically */
}

/* flex-wrap: wrap */
.container-wrap {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;  /* Spacing between rows AND columns */
}

In a row, gap pushes elements apart horizontally, in a column vertically, and with flex-wrap: wrap it separates both the elements and the consecutive rows.

Practical Examples

1. Card grid

The first example is a grid of six cards in three columns:

<div class="card-grid">
  <div class="card">Card 1</div>
  <div class="card">Card 2</div>
  <div class="card">Card 3</div>
  <div class="card">Card 4</div>
  <div class="card">Card 5</div>
  <div class="card">Card 6</div>
</div>

We reduce the width of each card by its share of the gaps, because gap also takes up space in the row:

.card-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;  /* Perfect spacing between cards */
}

.card {
  flex: 0 0 calc(33.333% - 14px);  /* 3 columns */
  /* calc accounts for gap: (40px total gap / 3 columns) ~ 14px */
  padding: 20px;
  background: #3498db;
  color: white;
  border-radius: 8px;
}

@media (max-width: 768px) {
  .card {
    flex: 0 0 calc(50% - 10px);  /* 2 columns on tablet */
  }
}

@media (max-width: 480px) {
  .card {
    flex: 0 0 100%;  /* 1 column on mobile */
  }
}

Three cards mean two gaps of 20 pixels, so about 13.3 pixels per card, and the code rounds it up to 14. These calculations assume box-sizing: border-box, because otherwise the padding of the cards is added to their width and the third card drops to a new row.

Gap math for multi-column layouts:

  • 3 columns with a 20px gap: calc(33.333% - 13.333px)
  • 4 columns with a 20px gap: calc(25% - 15px)
  • Formula: calc(100% / n_columns - gap * (n_columns - 1) / n_columns)

2. Button group

The second example is a group of buttons pushed to the right edge:

<div class="button-group">
  <button class="btn">Cancel</button>
  <button class="btn btn-primary">Save</button>
  <button class="btn btn-danger">Delete</button>
</div>

The container gets gap: 10px and justify-content: flex-end, and the buttons themselves have no margins at all:

.button-group {
  display: flex;
  gap: 10px;  /* Equal spacing between buttons */
  justify-content: flex-end;
}

.btn {
  padding: 10px 20px;
  border: 1px solid #ddd;
  background: white;
  cursor: pointer;
  border-radius: 4px;
}

.btn-primary {
  background: #3498db;
  color: white;
}

.btn-danger {
  background: #e74c3c;
  color: white;
}

The buttons stand evenly spaced at the right edge, and when you add a fourth one, the spacing appears by itself.

3. Form layout

The third example is a form in which the first and last name stand in one row:

<form class="form">
  <div class="form-row">
    <input type="text" placeholder="First name">
    <input type="text" placeholder="Last name">
  </div>
  <input type="email" placeholder="Email">
  <textarea placeholder="Message"></textarea>
  <button type="submit">Send</button>
</form>

The form is a column with a 15-pixel gap, and the row with the first and last name is a separate flex container with the same gap:

.form {
  display: flex;
  flex-direction: column;
  gap: 15px;  /* Spacing between form fields */
  max-width: 500px;
}

.form-row {
  display: flex;
  gap: 15px;  /* Spacing between fields in a row */
}

.form input,
.form textarea {
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 4px;
  font-family: inherit;
}

.form-row input {
  flex: 1;  /* Equal width */
}

.form textarea {
  min-height: 100px;
  resize: vertical;
}

.form button {
  padding: 12px;
  background: #3498db;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  align-self: flex-start;
}

The fields in the row share the width equally thanks to flex: 1, and the button with align-self: flex-start does not stretch to the full width.

4. Navigation bar

The fourth example is a navigation bar with a logo, a menu and buttons:

<nav class="navbar">
  <div class="nav-brand">Logo</div>
  <ul class="nav-menu">
    <li><a href="#">Home</a></li>
    <li><a href="#">About</a></li>
    <li><a href="#">Services</a></li>
    <li><a href="#">Contact</a></li>
  </ul>
  <div class="nav-actions">
    <button>Login</button>
    <button class="btn-primary">Sign Up</button>
  </div>
</nav>

Gap works on three levels here: between the sections of the bar, between the links and between the buttons:

.navbar {
  display: flex;
  align-items: center;
  padding: 15px 30px;
  background: #2c3e50;
  gap: 30px;  /* Spacing between main sections */
}

.nav-brand {
  font-size: 1.5em;
  font-weight: bold;
  color: goldenrod;
}

.nav-menu {
  display: flex;
  list-style: none;
  margin: 0;
  padding: 0;
  gap: 20px;  /* Spacing between links */
  flex: 1;  /* Take up the middle */
}

.nav-menu a {
  color: white;
  text-decoration: none;
  transition: color 0.3s;
}

.nav-menu a:hover {
  color: goldenrod;
}

.nav-actions {
  display: flex;
  gap: 10px;  /* Spacing between buttons */
}

.nav-actions button {
  padding: 8px 16px;
  border: 1px solid white;
  background: transparent;
  color: white;
  cursor: pointer;
  border-radius: 4px;
}

.nav-actions .btn-primary {
  background: goldenrod;
  border-color: goldenrod;
  color: #2c3e50;
}

The menu with flex: 1 takes the middle. When the bar has only a logo and a menu, justify-content: space-between on the container is enough: the logo goes to the left edge, the menu to the right, and align-items: center aligns them vertically.

5. Dashboard layout

The fifth example brings everything together: a panel with a sidebar, a header and widgets:

<div class="dashboard">
  <aside class="sidebar">
    <div class="sidebar-item">Dashboard</div>
    <div class="sidebar-item">Analytics</div>
    <div class="sidebar-item">Settings</div>
  </aside>
  <main class="dashboard-main">
    <header class="dashboard-header">Header</header>
    <div class="dashboard-content">
      <div class="widget">Widget 1</div>
      <div class="widget">Widget 2</div>
      <div class="widget">Widget 3</div>
      <div class="widget">Widget 4</div>
    </div>
  </main>
</div>

Each level of the layout has its own gap, and the widgets form two columns:

.dashboard {
  display: flex;
  gap: 20px;  /* Spacing between sidebar and main */
  height: 100vh;
  padding: 20px;
}

.sidebar {
  display: flex;
  flex-direction: column;
  gap: 10px;  /* Spacing between items */
  flex: 0 0 250px;
  background: #34495e;
  padding: 20px;
  border-radius: 8px;
}

.sidebar-item {
  padding: 12px;
  background: #2c3e50;
  color: white;
  border-radius: 4px;
  cursor: pointer;
}

.dashboard-main {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 20px;  /* Spacing between header and content */
}

.dashboard-header {
  flex: 0 0 auto;
  padding: 20px;
  background: white;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.dashboard-content {
  flex: 1;
  display: flex;
  flex-wrap: wrap;
  gap: 20px;  /* Spacing between widgets */
}

.widget {
  flex: 0 0 calc(50% - 10px);  /* 2 columns */
  min-height: 200px;
  padding: 20px;
  background: white;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

No element has a margin, yet all the spacing is even, because two widgets with calc(50% - 10px) split one gap in half.

Gap vs Margin - When to Use Which?

Use Gap:

  • Spacing between elements in a flex/grid container
  • Even spacing without complications
  • When you don't need outer spacing
  • Responsive layouts

Use Margin:

  • Outer spacing (from the container)
  • Asymmetric spacing
  • When you need margin: auto for centering
  • When you must support old browsers (pre-2021)

In practice, both tools work together: gap and padding work inside the container, and margin outside it:

/* Gap for internal spacing */
.container {
  display: flex;
  gap: 20px;
  padding: 20px;  /* Inner spacing from the edge */
}

/* Margin for external spacing */
.container {
  margin: 40px auto;  /* Center the container */
}

/* Combination */
.card {
  /* Gap removes the need for margin between items */
  /* but margin-top can be used for a specific purpose */
  margin-top: auto;  /* Push to bottom in flex column */
}

margin: 40px auto still centers the container, and margin-top: auto pushes an element to the bottom of a flex column. My advice: always leave the spacing between elements to gap, and keep margin for special tasks.

Responsive Gap

On a small screen, large gaps waste space, so it is worth reducing gap in media queries or through CSS variables:

.container {
  display: flex;
  flex-wrap: wrap;
  gap: 30px;
}

@media (max-width: 1024px) {
  .container {
    gap: 20px;  /* Smaller gap on tablet */
  }
}

@media (max-width: 768px) {
  .container {
    gap: 15px;  /* Even smaller on mobile */
  }
}

/* Or with CSS variables */
:root {
  --gap-large: 30px;
  --gap-medium: 20px;
  --gap-small: 15px;
}

.container {
  gap: var(--gap-large);
}

@media (max-width: 768px) {
  :root {
    --gap-large: var(--gap-medium);
  }
}

The second version only changes the value of the --gap-large variable, and every rule that uses it adapts on its own.

Browser Support

Gap in Flexbox is supported since:

  • Chrome 84+ (2020)
  • Firefox 63+ (2018)
  • Safari 14.1+ (2021)
  • Edge 84+ (2020)

If you need to support older browsers, start with margins and override them where gap is available:

.container {
  display: flex;
  /* Fallback with margin */
  margin: -10px;
}

.item {
  margin: 10px;
}

/* Modern browsers override */
@supports (gap: 20px) {
  .container {
    gap: 20px;
    margin: 0;
  }

  .item {
    margin: 0;
  }
}

Watch out for a trap: @supports (gap: 20px) only checks whether the browser knows the gap property. Older versions of Chrome and Safari already knew it from CSS Grid, even though they did not yet support it in Flexbox, so the test could lie. Today such a fallback is rarely needed.

Summary

Gap is a modern and clean way to create spacing that works in Flexbox and in Grid. It adds spacing only between elements, takes one or two values, has a simpler syntax than margins and makes width calculations easier. It shines in card grids, button groups, forms, navigation bars and dashboards. In the next lesson we will look at accessibility.

In the editor below you can compare both approaches: the first example keeps the spacing with margins, the second uses gap: 10px, and the third sets row-gap and column-gap separately.

Code for this lesson: index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Flexbox Gap Property</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <h1>Gap in Flexbox</h1>

    <h2>Without gap (old way with margin)</h2>
    <div class="container-old">
        <div class="box">A</div>
        <div class="box">B</div>
        <div class="box">C</div>
        <div class="box">D</div>
    </div>

    <h2>With gap (modern way)</h2>
    <div class="container-gap">
        <div class="box">A</div>
        <div class="box">B</div>
        <div class="box">C</div>
        <div class="box">D</div>
    </div>

    <h2>Row-gap and column-gap separately</h2>
    <div class="container-mixed">
        <div class="box">1</div>
        <div class="box">2</div>
        <div class="box">3</div>
        <div class="box">4</div>
        <div class="box">5</div>
        <div class="box">6</div>
    </div>
</body>
</html>

Remember: gap is the perfect spacing between hieroglyphs on an Egyptian papyrus, precise and even, and set with a single declaration, without complications.

Docs entries used in this lesson

Spotted a mistake in this lesson?

Check yourself

Answer the questions from this lesson. Pick an answer to see right away whether it is correct.

  1. 1. What is the purpose of the gap property in a Flexbox container?

Hands-on tasks in the game

  • Code editor

    Create a navigation bar with a logo on the left and 4 menu links on the right, using Flexbox with justify-content: space-between and align-items: center.

Useful articles