Skip to main content Accessibility Feedback

How to build a reusable grid system with CSS grid

Yesterday, we learned the basics of CSS Grid. Today, we’re going to learn how to create a reusable grid system that makes building a variety of website layouts faster and easier.

Let’s dig in!

The single row, 12-column grid

To support a variety of layouts, we’re going to build a CSS Grid layout that’s just a single row with 12 columns in it.

Then, we’ll create classes that let us specify how many columns wide any item in our layout should be, and which column it should start at.

Why 12 columns?

Because it divides easily into a variety of patterns. You can split it in half for two even columns, or into thirds, or fourths. It also supports some irregular or asymmetrical layouts.

In short, it’s really flexible.

Why only one row?

It makes coding easier and more reusable.

Each row in your HTML will have its own parent element. We can stack as many of them as we need to create unique layouts.

Let’s get started!

Defining a row

For this system, we’ll use the very uncreative class name .row for our row of content.

We’ll set display to grid, set our grid-template-columns to 12 repeating 1fr wide columns, and a grid-template-rows property of 1fr.

We’ll also add a column-gap, but handle the spacing between rows separately (maybe with some utility classes or something).

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

Defining columns: the simple way

On my personal projects, I like to use fraction-based names for my columns.

Things like .col-third and .col-fourth make it incredibly obvious how big the column is.

For each .col-* class, we’ll define a grid-column property with a starting grid line of auto, and an ending grid line of span {width}, where {width} is the number of columns it should span.

This stacks the columns in order, but tells our CSS how wide to make each item.

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

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

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

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

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

Now, we can do stuff like this…

<div class="row">
	<div class="col-third">Third</div>
	<div class="col-two-thirds">Two Thirds</div>
</div>

<div class="row">
	<div class="col-fourth">Fourth</div>
	<div class="col-half">Half</div>
	<div class="col-fourth">Fourth</div>
</div>

Here’s a demo.

Defining columns: the more complex way

For more robust systems, you might instead define your column classes by the number of classes they span.

For example, .col-third would become .col-4, as it spans four columns. The .col-half class would become .col-6, because it spans six columns.

This gives you a lot more control, but requires you to think a bit more when building your layouts.

.col-1 {
	grid-column: auto / span 1;
}

.col-2 {
	grid-column: auto / span 2;
}

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

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

.col-5 {
	grid-column: auto / span 5;
}

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

.col-7 {
	grid-column: auto / span 7;
}

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

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

.col-10 {
	grid-column: auto / span 10;
}

.col-11 {
	grid-column: auto / span 11;
}

Controlling where items start

Being able to define where in a layout an item starts lets you do more interesting things with your layout.

For this, we’ll create a handful of .col-start-* classes. For each one, we’ll define a grid-column-start property, with the grid line on which the item should start.

We also need to define a grid-row-start of 1 for each item. I’m using an attribute selector to make this easier.

Here, I’m again using simple fraction-based name for ease.

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

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

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

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

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

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

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

Now, you can do stuff like this…

<div class="row">
	<div class="col-two-thirds col-start-third">Two Thirds</div>
</div>

<div class="row">
	<div class="col-fourth">Fourth</div>
	<div class="col-fourth col-start-three-fourths">Fourth</div>
</div>

<div class="row">
	<div class="col-third">First in HTML</div>
	<div class="col-two-thirds col-start-first">Second in HTML</div>
</div>

Here’s another demo.

Single-column on small viewports

I typically wrap my grid system in a media query…

@media (min-width: 40rem) {
	/* Start the grid system CSS */
}

On smaller viewports, users get a single-column layout. Once they hit a large-enough viewport, the grid kicks in and shifts to a multi-column layout.

Modifiers and utility classes

I like to use utility classes to control spacing between rows.

<div class="row margin-bottom-small">
	<!-- ... -->
</div>

<div class="row margin-bottom-large">
	<!-- ... -->
</div>

I also will often include modifiers for my .row class to increase or decrease the column-gap.

.row-gap-large {
	column-gap: 2rem;
}

.row-no-gap {
	column-gap: 0;
}

Feel free to add additional classes to nudge-and-tweak things as needed.