Create a responsive grid layout

Even with no media-queries, we can create a flexible and powerful responsive layout.


Embracing the flexible nature of the web gives us resilient user interfaces. Instead of using prescribed, specific sizes, we give elements sensible boundaries and let them auto flow or resize where possible. A good way to think of this is that we provide browsers with some rules and regulations, then let them decide what’s best—using them as a guide.

What we’re making permalink

In this tutorial, we’re going to take this flexible mindset and produce a responsive grid layout that rules and regulations, then renders the best possible grid for the device that asks for it.

Here it is in action:

See the Pen Responsive grid layout: Example 1 by Andy Bell (@piccalilli) on CodePen.

It’s a fully responsive grid that uses no media queries to work across all viewports and it’s all thanks to CSS Grid.

How it works permalink

First of all, let’s take a look at the code:

Code language
css
.auto-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--auto-grid-min-size, 16rem), 1fr));
  grid-gap: 1rem;
}

CSS Grid is incredibly powerful for layout but usually, this type of flexible layout utility would use Flexbox, because of its flexibility. The reason we use grid for this particular utility, though, is the magic function, minmax.

The minmax function allows us to clamp our grid items to maintain just the right amount of control. We achieve this by telling the function what the smallest size is for our items, by using a CSS Custom Property, which has a fallback of 16rem. Then we tell it what the maximum size should be for each item. We declare that as 1fr, which takes 1 portion of the remaining available space.

Because each item in this grid uses 1fr, the remaining space is split up equally between the other items, so if there are 10 items, 1fr is equal to 10% of the remaining available space. This is how we get that nice flexibility.

How can we improve this layout utility? permalink

Because this .auto-grid utility will keep trying to fill remaining available space, it’s useful to either give the layout itself a maximum width or wrap it with a shared wrapper utility. I prefer the latter approach because explicitly sizing the grid itself will reduce its own flexibility.

It’s fine to explicitly set a width sometimes, sure, but creating specific components that only deal with page-wide composition, like the wrapper element make working with flexible component systems a dream!

Here’s the HTML code for the wrapper element:

Code language
html
<div class="wrapper">
  <ul class="auto-grid">
    <!-- items go here -->
  </ul>
</div>

And then the CSS:

Code language
css
.wrapper {
  max-width: 65rem;
  margin-left: auto;
  margin-right: auto;
  padding: 0 1rem;
}

Here it is in action:

See the Pen Responsive grid layout: Example 2 - Wrapper by Andy Bell (@piccalilli) on CodePen.

Making the wrapper a separate utility means that we can use it wherever we need it, which is some good ol’ portability!

Because the wrapper has a max-width set, it’ll support all viewport sizes too, without using media queries. The padding provides a gutter, so at tiny viewports, you get a 1rem sized gap at each side, which is ideal.

Progressive enhancement permalink

CSS Grid support, at the time of writing, is 94.69%, so the vast majority of browsers have support for it. We can improve our code to provide a default experience for the minority of users that don’t have support for it with a minimum viable experience, though.

See the Pen https://codepen.io/andybelldesign/pen/XWbeoMz by Andy Bell (@piccalilli) on CodePen.

For this layout, the minimum viable experience is stacked items with some vertical margin between each one, which for the 5.31% of browsers that don’t support CSS grid:

Code language
css
.auto-grid > * {
  max-width: 25rem;
  margin-left: auto;
  margin-right: auto;
}

.auto-grid > * + * {
  margin-top: 1rem;
}

@supports (display: grid) {
  .auto-grid {
    display: grid;
    grid-template-columns: repeat(
      auto-fill,
      minmax(var(--auto-grid-min-size, 16rem), 1fr)
    );
    grid-gap: 1rem;
  }

  .auto-grid > * {
    max-width: unset;
    margin: unset;
  }
}

As the code block demonstrates, the minimum viable experience removes that default style using @supports which detects CSS Grid support. This sample will work fine all the way back to super old IE browser versions. You could even use floats and Flexbox if you absolutely wanted a grid-like layout for all browsers and a real power move would be to use something like this auto-scrolling approach.

Utilising the forgiving nature of CSS to build up, rather than fix and break down will very likely result in you writing better, lighter code, so finding the lowest-tech approach to compatibility issues is only ever going to be a good thing.

The minimum viable experience in IE11

The minimum viable experience in IE11 shows how although the grid isn’t present, the sensible defaults make the layout look smart. The user will probably never know that it is supposed to be a grid because it’s not broken. This is the magic of progressive enhancement.

Wrapping up permalink

I use this auto-grid all the time. I also use the wrapper utility, extensively in every project that I work on. Hopefully this tutorial has demonstrated the power of modern CSS and how we can write very little to achieve a lot. It really is a powerful language when you give it room to breathe.

Even the heavier, progressively enhanced version that supports all browsers is tiny and importantly, so much tinier than writing hacks and specific browser CSS!