In Praise of the Humble CSS “Gap” Property

Meet my new favorite CSS technique.

When you’re trying to solve even the simplest design problems with CSS — for example, vertically centering something on a page or consistently styling form controls — you’ll often find that there’s no one single solution that’s right for every possible situation.

Using align-items: center; for vertical centering works most of the time — except when absolute positioning makes more sense. It all just depends, which is a beautiful thing about web development (because you’re often pushed to be creative with your problem-solving), but also really annoying (because sometimes, you just want your code to work so you can move on to something else).

That said, I’ve become increasingly convinced that when it comes to creating consistent spacing within a group of elements, there actually is one single approach that is perfect pretty much all of the time. And that’s the gap property, specifically when used with flexbox and grid containers. (Note: This post just covers the basics of the gap property. For more info, check out CSS Tricks or MDN.)

Consistent spacing between elements might not seem like a big deal, but it’s an integral part of designing a webpage, from laying out links in a site’s navigation to controlling form layouts to setting up gutters in a grid layout (e.g., an image gallery). Basically, any time you’re laying out a group of elements, you’ll probably need to specify their spacing.

Consider this simple code snippet:

<div class="group">

We’ve got a <div> with the “group” class that contains several child <div> elements. For years, we probably used one of two methods to control the spacing between those child elements:

  1. The adjacent sibling combinator (+)
  2. The :not() pseudo-class

The “adjacent sibling” CSS looks like this:

See the Pen Adjacent sibling selector by Jason Morehead (@jasonopus) on CodePen.

The key part is the .group > div + div rule. It states that any <div> that is a child of the .group element and immediately follows another child <div> — i.e., is an adjacent sibling — should have a 20-pixel left margin.

If we use the :not() pseudo-class, however, then our CSS looks like this:

See the Pen :not() pseudo-class by Jason Morehead (@jasonopus) on CodePen.

The key part is the .group > div:not(:first-child) rule. It states that any <div> that is a child of the .group element but is not the first child <div> should have a 20-pixel left margin.

Both of these approaches are functionally the same and are perfectly valid CSS. Indeed, there might be times when they still make sense to use. But lately, I’ve pretty much switched to using the gap property in all such cases simply because it’s so much simpler and more elegant.

See the Pen Flexbox gap by Jason Morehead (@jasonopus) on CodePen.

As expected, adding gap: 20px; to the .group rule says that there should be a 20-pixel gap between every child of the .group element — and that’s it. It’s so clean and logical. Obviously, this solution won’t work if .group isn’t a flexbox element, but as you can see in the earlier examples, you’d almost always make it a flexbox element because that makes it so much easier to align and arrange its children.

The gap CSS is also agnostic with regards to the child elements. They can be any HTML element and they’ll still be spaced 20 pixels apart. (I could achieve that with the other examples if I used * instead of div, but universal selectors always feel a little dodgy and inefficient to me.)

Another nice thing about the gap property is that it’s applied in-between the child elements and not around them. In other words, there isn’t a 20-pixel gap between the edge of the .group element and its first and last children. Thus, you don’t need to account for that like you do with the other approaches, which specifically prevent a left margin from being applied to the first child <div>. This is especially nice because it removes the need for extra padding and negative margins, which are often used to ensure everything lines up correctly.

Consider this form field collection for specifying a date:

<div class="form-row">
    <div class="form-controls">
            <select name="month">
                <option value="">Month</option>
                <option value="01">January</option>
                <option value="12">December</option>
            <select name="day">
                <option value="">Day</option>
                <option value="01">01</option>
                <option value="31">31</option>
            <input type="text" name="year" placeholder="Year">

It consists of three form controls: a “Month” <select>, a “Day” <select>, and a text <input> for entering the year. Each control is contained inside a <div>. These are contained inside a <div> with the “form-controls” class, which itself is contained inside a <div> with the “form-row” class.

In this first example — the “old school” example — I’m using padding to create the space between the three form controls and a negative margin on the .form-controls element to ensure there’s no extra space before the “Month” <select>. (This approach is used by CSS grid systems like Bootstrap’s.)

This second example uses the same HTML, but now I’m using gap to create space between the three form controls. As a result, there’s no need for padding or negative margins to remove any unnecessary spacing because there is no unnecessary spacing. Plus, the code is simpler, which is always a win.

See the Pen Date Field with Gap by Jason Morehead (@jasonopus) on CodePen.

Another beautiful thing about the gap property is that it specifies both row and column gaps. Specifying a single value, like gap: 20px, sets the same value for both row and column gaps. Specifying two values, like gap: 20px 40px, sets the row and column gaps respectively.

This comes in handy for responsive layouts. Consider again the date field collection. With just some minor tweaks to the CSS, I can ensure that the collection’s layout is optimized for display on mobile and desktop alike. (Toggle the “CSS” pane on and off to see how the layout changes.)

See the Pen Date Field with Gap (Responsive) by Jason Morehead (@jasonopus) on CodePen.

Again, it’s certainly possible to achieve the above results using something other than gap, but that CSS would undoubtedly be more complicated and less intuitive.

I don’t envy those who maintain the HTML and CSS specs. I’m sure they receive numerous requests for features that might sound good and useful at first, but upon closer inspection, are too close to an existing feature, have unrealistic or ultra-narrow use cases, or address problems that have already been solved by other means (or weren’t really problems to begin with). No doubt, the spec maintainers want to keep things as simple as possible.

Which explains why the gap property is so great. It has a very specific purpose and addresses a very common need. And though you can certainly replicate the gap property’s effect with other valid approaches, its simplicity and elegance makes them feel like hacks in comparison.

Indeed, when it comes to consistently spacing out a group of elements, I’m hard-pressed to think of a reason why you wouldn’t use the gap property, especially considering its widespread browser support.

Many thanks to Jaymee Jarvis for peer reviewing this post.

Enjoy reading Opus? Want to support my writing? Become a subscriber for just $5/month or $50/year.
Subscribe Today
Return to the Opus homepage