Skip to main content Accessibility Feedback

How to create a responsive grid system with CSS Grid

I recently updated the grid system on my site from Flexbox to CSS Grid. Today, I wanted to share how to use it, how it works under-the-hood, and why I made the switch.

Let’s dig in!

A responsive grid system

My websites tend to be pretty boring. Years ago, I decided to prioritize simple readability over flashy features, and that seems to resonate with a lot of people.

But I do occasionally need multi-column layouts, like the list of projects on the Lean Web Club homepage.

A lot of design systems use the “number of columns” as their naming system.

<div class="row">
	<div class="col-3">3 columns wide</div>
	<div class="col-6">6 columns wide</div>
	<div class="col-2">2 columns wide</div>
	<div class="col-1">1 column wide</div>
</div>

I hate that, personally. It means I have to constantly do awkward math against 12 (or more) columns to figure out how wide I want my stuff to be.

Instead, I use simple fractions for my grid sizes.

<div class="row">
	<div class="grid-third">1/3rd wide column</div>
	<div class="grid-two-thirds">2/3rds wide column</div>
</div>

<div class="row">
	<div class="grid-fourth">1/4th wide column</div>
	<div class="grid-half">1/2 wide column</div>
	<div class="grid-fourth">1/4th wide column</div>
</div>

This keeps the mental overhead really low, and makes it really easy to figure out how wide a thing is going to be relative to the other columns.

Mobile-first progressive enhancement

With any modern grid system, you generally progressively enhance your way into a column layout.

That means that by default, your layout is a single-column design. Once the viewport goes past a certain width, you layer in your grid.

This is great for two reasons:

  1. Mobile users automatically get a great experience.
  2. If someone’s on an older browser that doesn’t support CSS Grid (which is exceedingly rare these days), they still get something usable, albeit less pretty.

Creating a responsive grid with CSS Grid

Alright, let’s look at how to actually code this thing up!

First, I wrap my whole grid system in a media query. I generally start my grid systems once the viewport is about 40em (or 640px with the default font size), though I may nudge that a little up or down depending on the design.

@media (min-width: 40em) {
	/* The grid system goes here... */
}

Next, I add my .row wrapper class. This defines the element a grid with the display: grid property.

I also setup my rows and columns. I like to use a 12-column layout that lets me easily divide things into thirds, fourths, sixths, and so on. For that, I use the grid-template-columns property, and use the repeat() method to create 12 columns of 1fr (one fractional unit) each.

Yes, CSS has functions. It’s a programing language.

@media (min-width: 40em) {
	
	.row {
		display: grid;
		grid-template-columns: repeat(12, 1fr);
	}

}

I use the grid-template-rows property to define a row with a minimum size of 1fr, and an auto sized maximum.

I use the column-gap property to add some spacing between each column, but prefer to control the spacing between rows with utility classes instead.

.row {
	display: grid;
	grid-template-columns: repeat(12, 1fr);
	grid-template-rows: 1fr auto;
	column-gap: 0.75em;
}

Adding columns with CSS Grid

Next, I add my column classes, which all start with .grid-*.

On those, I define a grid-column property. The first part, auto / span, tells it to automatically choose where to start. This causes the columns to stack up next to each other rather than overlapping.

The number at the end is how many columns wide I want it to be, as a fraction of a 12-column system. A fourth would be 3 (12 / 4 = 3). Half would be 6 (12 / 2 = 6). You hopefully get the idea.

.grid-fourth {
	grid-column: auto / span 3;
}

.grid-third {
	grid-column: auto / span 4;
}

.grid-two-thirds {
	grid-column: auto / span 8;
}

.grid-half {
	grid-column: auto / span 6;
}

.grid-three-fourths {
	grid-column: auto / span 9;
}

Offset columns with CSS Grid

I occasionally want a column to start somewhere other than all the way to the left, or want to flip-flop two pieces of content on bigger viewports.

<div class="row">
	<div class="grid-half">I want this first on small screens, but second on big ones</div>
	<div class="grid-half">I want this second on small screens, but first on big ones</div>
</div>

For that, I use a .grid-start-* modifier class.

For all of them, I define a grid-row-start property of 1. This tells them to always base their positioning on the first row, and avoids any weirdness.

Then, I define a grid-column-start property of where I want them to actually start. To pull some content to the starting position, I can use the .grid-start-first class to set a grid-column-start value of 1.

To leave a fourth of the grid empty, I could add a .grid-start-fourth class, which would position the content at column 4.

[class*="grid-start-"] {
	grid-row-start: 1;
}

.grid-start-first {
	grid-column-start: 1;
}

.grid-start-fourth {
	grid-column-start: 4;
}

.grid-start-third {
	grid-column-start: 5;
}

.grid-start-half {
	grid-column-start: 7;
}

.grid-start-two-thirds {
	grid-column-start: 9;
}

.grid-start-three-fourths {
	grid-column-start: 10;
}

I can do things like this.

<div class="row">
	<div class="grid-half">I want this first on small screens, but second on big ones</div>
	<div class="grid-half grid-start-first">I want this second on small screens, but first on big ones</div>
</div>

<div class="row">
	<div class="grid-third grid-start-third">
		Positioned in the center.
		There's a 1/3rd gap on each side.
	</div>
</div>

Adjusting the gap

I occasionally want more or less of a gap between columns.

For that, I have two modifier classes that adjust the column-gap property.

.row-gap-large {
	column-gap: 1.1875em;
}

.row-no-gap {
	column-gap: 0;
}
<div class="row row-gap-large">
	<!-- Columns have bigger gaps between them -->
</div>

<div class="row row-no-gap">
	<!-- Columns have no gap at all -->
</div>

Automatically sizing columns

Every now and then, I have a layout where I just want to drop a bunch of content in and have the grid figure it out. For that, I use this trick from Stephanie Eckles wonderful Smol CSS website.

This tells the grid to automatically fit columns into the row. But, it sets some restrictions on the minimum and maximum size, preventing them from being less than 15em (you can change that to whatever you want).

.row-auto {
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 15em), 1fr));
}

Functionally, this means columns of about one-third on larger screens, half on mid-sized screens, and single column on small ones.

<div class="row row-auto">
	<div>1</div>
	<div>2</div>
	<div>3</div>
	<div>4</div>
	<div>5</div>
	<div>6</div>
	<div>7</div>
	<div>8</div>
</div>

If you found this interesting, I’ll be expanding the Lean Web Club with a lot more CSS and HTML content going forward. Join for free today!