In the beginning, there was the document flow, and there were block and inline elements, and lo, it was sort of OK, and if it wasn’t you could always use float, and developers went “but how do we get a header here and a footer there and a left and right hand navigation and have the whole thing responsive across different layouts” and the World Wide Web Consortium went “we don’t know maybe you could do it using tables?”

And then there was the 12-column layout, and the CSS flexbox, and it was good, and developers went “yeah, this is all awesome but what if I want to a layout that’s responsive in two different directions at the same time?” and the WHAT-WG, which by now had replaced the World Wide Web Consortium when it came to This Kind Of Thing, went “what, you want, like, flexbox, but with rows and columns?” and the developers went “yes!” and the WHAT-WG went “Oh, ok, I guess we could build some sort of grid system” and developers went “YES A GRID SYSTEM!”… and so CSS Grid was born.

Why Use CSS Grid?

The first thing to remember — and this applies to just about everything we’ve seen so far in this course — is that none of it is mutually exclusive. Choosing CSS layout modules isn’t like choosing Python vs nodeJS, or React vs Angular; you can mix and match. You can absolutely have a site where you have individual grid-based components used in flex-based sections in a page that’s using a legacy 12-column layout grid; in fact, many of the examples I’ve shown so far in this course use a flex or a grid just to lay out a handful of elements inside a div or a section.

Understanding Flow vs Grid vs Flex

There’s a lot of overlap between flex and grid; they share many of the same concepts and use the same syntax for properties like gap. The fundamental difference between them is that flex always tries to fill the container, whereas grid always respects rows and columns.

From time to time, somebody will ask online “I’m using CSS flexbox with wrap; how do I stop the last item stretching to fill the row?” and the answer comes back “use a CSS grid”:

	* { box-sizing: border-box; margin: 0; padding: 0; }

	div {
		background: gold;
		padding: 10px;
		font-family: Consolas, 'Courier New', Courier, monospace;
		border: 2px solid royalblue;
	}

	section[style*="display: flex"] {
		flex-wrap: wrap;
		div { flex: 1 1 50%; }
	}

	section[style*="display: grid"] {
		grid: auto / 1fr 1fr;
	}
<article> <section> <div>1</div> <div>2</div> <div>3</div> <div>4</div> <div>5</div> </section> <section style="display: flex;"> <div>1</div> <div>2</div> <div>3</div> <div>4</div> <div>5</div> </section> <section style="display: grid;"> <div>1</div> <div>2</div> <div>3</div> <div>4</div> <div>5</div> </section> </article> <article> <pre>flow</pre> <pre>flex</pre> <pre>grid</pre> </article>
flow-vs-grid-vs-flex.html

Basic CSS Grid

Very broadly speaking, CSS grid boils down to three things.

  1. Set display: grid or display: inline-grid on the container element
  2. Define the rows and columns on the container
  3. Override those if required for specific grid items

I learned most of what I know about using CSS grid from Chris House’s excellent CSS Grid Layout Guide over at css-tricks.com, which also has a one-sheet quick-reference guide covering all the grid layout properties and values.

The rows and columns in a CSS grid layout are collectively known as tracks, so when you see a reference to a track or track size, we’re talking about something which is either a row or a column.

CSS Grid Tracks
CSS Grid Tracks

The boundaries around the grid and between adjacent tracks are known as grid lines - and before you ask, there’s no way to use CSS to make them visible the way you can with table cell borders.

CSS Grid Tracks
CSS grid: columns, rows, and lines

If it helps, think of the grid cells like city blocks, and the lines as the streets between them. The tracks in a grid are specified using the grid-template-rows and grid-template-columns properties; each property is a list of track sizes - absolute units, relative units, or the special fr unit which represents a proportion of the available space.

fr is officially short for for fraction but when I first saw it I thought “oh, OK, fr for ‘free space’” and that’s stuck in my head now.

	section {

		display: grid;
		grid-template-rows: 3fr 2fr 1fr;
		grid-template-columns: 30px 3fr 1fr 50%;
		gap: 10px;

		height: 20rem;
		padding: 10px;
		background-color: royalblue;

		div { background-color: gold; }
	}
grid-template-rows-and-columns.html

You can also use the repeat function to repeat the same track size:

	section {

		display: grid;
		grid-template-rows: repeat(4, 1fr);
		grid-template-columns: 3fr repeat(5,1fr) 3fr;
		gap: 10px;

		height: 20rem;
		padding: 10px;
		background-color: royalblue;

		div { background-color: gold; }
	}
grid-template-rows-and-columns-repeat.html

Explicit vs Implicit Grids

If there are more items in the grid than you’ve allocated spaces for, the grid layout module will add extra rows or columns to accommodate the extra elements.

By default, the grid layout adds additional rows to the end of the grid.

These examples are interactive - click the button to add extra items to the grid:

	section {
		display: grid;
		grid-template-columns: 1fr 1fr;
		grid-template-rows: 1fr 1fr;
	}
grid-implicit-rows.html

To add additional columns, add grid-auto-flow: column to the grid container; this tells the layout module to add elements by filling each column first, and adding more columns as needed:

	section {
		display: grid;
		grid-template-columns: 1fr 1fr;
		grid-template-rows: 1fr 1fr;
		grid-auto-flow: column;
	}
grid-implicit-columns.html

To control the size of the implicit tracks, use grid-auto-rows or grid-auto-columns:

	section {
		display: grid;
		grid-template-columns: 1fr 1fr;
		grid-template-rows: 1fr 1fr;
		grid-auto-flow: column;
		grid-auto-columns: 200px;
	}
grid-auto-columns.html

Items within the grid can target specific rows and columns using the grid-column-start, grid-column-end, grid-row-start and grid-row-end properties.

Something which often trips people up when they first encounter this syntax: we’re not specifying the row or column number, we’re specifying the line between them. I think one of the reasons there’s so much confusion here is that there’s some really misleading behaviour baked into the CSS grid spec. Take a look at this code and the resulting output:

	section {
		display: grid;
		padding: 10px;
		gap: 10px;
		grid-template-columns: repeat(4, 1fr);
		grid-template-rows: repeat(4, 1fr);

		div#example {
			grid-column-start: 3;
			grid-column-end: 3;
			grid-row-start: 2;
			grid-row-end: 2;
			background-color: crimson;
			color: white;
		}
	}
grid-start-end-edge-case.html

Div #1 - the red one - clearly starts and ends in column 3, and starts and ends in row 2… right?

No. This is actually invalid syntax. The numbers refer to lines, not rows/columns, so an item which starts and ends at column grid line 1 actually has zero width… but there’s a fallback rule in the CSS grid spec which says that if the combination of a start and an end rule would create an element with zero (or negative) size, ignore the end rule… and because the default is to span a single row/column, it looks like it worked.

Here’s an example that specifies start and end lines correctly. This example also creates some overlapping elements; these will be stacked in the order they appear in the HTML, unless you override this using a z-index:

	section {
		display: grid;
		grid-template-rows: repeat(5, 1fr);
		grid-template-columns: repeat(5, 1fr);
		gap: 10px;
		padding: 10px;
	}
	div#alpha {
		background-color: crimson;
		color: #fff;
		grid-column-start: 2;
		grid-column-end: 5;
		grid-row-start: 2;
		grid-row-end: 4;
		z-index: 1;
	}
	div#beta {
		background-color: turquoise;
		grid-column-start: 1;
		grid-column-end: 3;
		grid-row-start: 1;
		grid-row-end: 5;
	}
	div#gamma {
		background-color: lime;
		grid-row-start: 3;
		grid-row-end: 6;
		grid-column-start: 4;
		grid-column-end: 6;
	}
	div#delta {
		background-color: orangered;
		grid-column-start: 2;
		grid-column-end: 4;
	}
grid-item-start-end.html

Named Tracks and Template Areas

Part of the power of CSS grid is the ability to name parts of the grid, and then use those names to allocate other elements in your document to named grid areas.

First up, let’s take a look at named lines. To create a named line, put the name in [] square brackets between two tracks in the row or column template property.

  • Remember, you aren’t naming the tracks, you’re naming the lines between them.

  • A single line can have more than one name - useful when the end of one area also marks the start of another area, like when the nav-end

  • A name can be applied to more than one line, and then used by qualifying it with a number - apparently useful if you put named lines into a repeat() statement.1

	body {
		display: grid;
		gap: 10px;
		grid-template-columns:
			[nav-start] 180px [nav-end main-start] 1fr [main-end ads-start] 180px [ads-end];

		nav {
			grid-column-start: nav-start;
			grid-column-end: nav-end;
		}

		main {
			grid-column-start: main-start;
			grid-column-end: main-end;
		}

		aside#adverts {
			grid-column-start: ads-start;
			grid-column-end: ads-end;
		}
	}
grid-named-columns.html

Naming lines is just a prelude to the really good stuff: named grid areas. You can take whole chunks of the grid, give them names using a relatively simple format, and then link chunks of content to those areas.

How simple? Imagine the dumbest thing you can possibly think of: ASCII art. Actually, even simpler than ASCII art, because there aren’t any lines: just names. Given a 3x5 grid layout, we can divide into areas like this:

div { 
	grid-template-areas: 
        "header     header    header"
        "site-nav  site-nav   adverts"
        "page-nav    main     adverts"
        "page-nav    main    comments"
        "footer     footer    footer";
}

The resulting layout ends up like this:

CSS Grid Tracks
CSS grid areas

and then by specifying the grid-area property on the various elements that make up the page, we can lay those elements directly onto the grid:

	* {
		box-sizing: border-box;
		margin: 0;
		padding: 0;
	}

	body {
		height: 100vh;
		display: grid;
		grid-template-rows: 100px 30px 200px 1fr 120px;
		grid-template-columns: clamp(100px, 15vw, 200px) 1fr 200px;
		gap: 5px;

		grid-template-areas:
			" header    header     header "
			"site-nav  site-nav    adverts"
			"page-nav    main      adverts"
			"page-nav    main     comments"
			" footer    footer     footer";

		header { grid-area: header; }
		nav#site-nav { grid-area: site-nav; }
		nav#page-nav { grid-area: page-nav; }
		main { grid-area: main; }
		aside#comments { grid-area: comments; }
		aside#adverts { grid-area: adverts; }
		footer { grid-area: footer; }

		>* {
			background-color: silver;
			display: flex;
			align-items: center;
			justify-content: center;
			font-family: Consolas, 'Courier New', Courier, monospace;
		}
	}
grid-named-areas.html

Subgrid and Masonry Grids

As well as specifying track sizes, the CSS grid-template-row and grid-template-column properties can take the value subgrid

Subgrid is used when a grid item is itself a grid container; it causes the tracks and lines of the child element to align with the grid of the parent element. Other grid properties, like gap, are not inherited, and can be declared separately on the child container:

	section {
		font-size: 1.2vw;
		width: 40vw;
		aspect-ratio: 1;
		display: grid;
		grid-template-columns: 1fr 2fr 3fr 4fr 1fr;
		grid-template-rows: 1fr 2fr 3fr 4fr 1fr;
		gap: 5px;

		div {
			background-color: crimson;
			padding: 5px;
			grid-column: span 3;
			grid-row: span 3;
			display: grid;
			grid-template-columns: 2fr 3fr 4fr;
			grid-template-rows: 2fr 3fr 4fr;
			gap: 10px;

			&.subgrid {
				grid-template-columns: subgrid;
				grid-template-rows: subgrid;
			}
		}
	}
<section> <span></span><span></span><span></span><span></span><span></span> <span></span> <div> <span>Alice Cooper</span> <span>Bon Jovi</span> <span>Creedence Clearwater Revival</span> <span>Dire Straits</span> <span>Eagles</span> <span>Foo Fighters</span> <span>Galahad</span> <span>Heart</span> <span>Iron Maiden</span> </div> <span></span> <span></span><span></span> <span></span><span></span> <span></span><span></span><span></span><span></span><span></span> </section> <section> <span></span><span></span><span></span><span></span><span></span> <span></span> <div class="subgrid"> <span>Alice Cooper</span> <span>Bon Jovi</span> <span>Creedence Clearwater Revival</span> <span>Dire Straits</span> <span>Eagles</span> <span>Foo Fighters</span> <span>Galahad</span> <span>Heart</span> <span>Iron Maiden</span> </div> <span></span> <span></span><span></span> <span></span><span></span> <span></span><span></span><span></span><span></span><span></span> </section>
subgrid.html

There’s also a popular layout used on a lot of sites, most notably Pinterest, which is known as a masonry layout; a way to arrange irregular content in columns such that it doesn’t leave any gaps:

Masonry Layout
Example of a Masonry Layout

There is a proposal to add a masonry grid to CSS, so you could build a layout like the one above by specifying the grid columns as usual and setting grid-template-rows to masonry; however, at the time I’m writing this, it’s only available as a technology preview in experimental versions of Safari, so I reckon it’ll be mid-2026 at the earliest before we see any support across Chrome and/or Firefox for masonry grids; in the meantime, if you’re trying to create this kind of layout, the only option is to use JavaScript to

Grid Shorthand

The property grid is a shorthand syntax covering:

  • grid-auto-columns
  • grid-auto-flow
  • grid-auto-rows
  • grid-template-areas
  • grid-template-columns
  • grid-template-rows

Like most CSS shorthand properties, it’s redundant if you’re only specifying a single property, useful if you’re specifying two or three properties, and rapidly becomes unreadable if you pack in many more than that.

Justify, Align, and Grid Item Properties

Like CSS flexbox, grid gives us a whole range of options when it comes to aligning and justifying items in the container, and for allowing individual items to override container-level styling.

  1. Don’t ask me why anybody would do this. I have no idea. 


This site uses Just the Docs, a documentation theme for Jekyll.