Using grid areas to control layout
August 31, 2022
A while ago, I wrote about using named grid lines to control layout, but I didn't realise that I had not also written about my favourite way of laying out grids - grid-template-areas. Time to fix that.
Grid areas are pretty darn awesome - instead of specifying how many columns and rows you want a child item to occupy in a grid, or setting the start and end lines, you just say "put this here" with the grid-area
property.
.card {
display: grid;
grid-template-columns: 6rem 1fr;
grid-template-rows: 2rem 6rem;
grid-template-areas: "name name"
"picture description";
}
.card .card-name {
grid-area: name;
}
.card .card-picture {
grid-area: picture;
}
.card .card-description {
grid-area: description;
}
<div class="card">
<div class="card-name"></div>
<div class="card-picture"></div>
<div class="card-description"></div>
</div>
You can arrange areas within a grid anyway you like, with a couple of caveats:
- All areas must be rectangular;
- Each row must have the same number of columns.
However, you do not have to name every cell. The .
character can be used to indicate an empty space within your grid. This is a rather extreme example:
.card {
display: grid;
grid-template-columns: 6rem 1fr;
grid-template-rows: 2rem 6rem;
grid-template-columns: 6rem repeat(3, 1fr);
grid-template-rows: 2rem repeat(3, 6rem);
grid-template-areas: "name name name name"
"picture . . ."
"picture . description ."
"picture . . .";
}
Free named lines
One neat thing about using grid areas is that you get some free named lines thrown in. Each named area gets a *-start
and *-end
line you can reference:
.card {
display: grid;
grid-template-columns: 6rem 1fr;
grid-template-rows: 2rem 6rem;
grid-template-columns: 6rem repeat(2, 1fr);
grid-template-rows: 2rem repeat(2, 6rem);
grid-template-areas: "name name name"
"picture description description"
"picture description description";
}
.card .card-name {
grid-area: name;
}
.card .card-picture {
grid-area: picture;
}
.card .card-description {
grid-row: description-start / description-end;
}
<div class="example-03">
<div class="card-name"></div>
<div class="card-picture"></div>
<div class="card-description"></div>
<div class="card-description"></div>
</div>
Grid areas and document order
Say we want to surround a hero image with other smaller ones:
.gallery {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(4, max-content);
grid-template-areas: ". . . ."
". hero hero ."
". hero hero ."
". . . .";
}
.gallery .hero {
grid-area: hero;
}
It might be tempting to write the HTML in "grid order" (please assume that there are good alt
descriptions set for each of these images):
<div class="gallery">
<!-- Top row -->
<img src="first.jpg"/>
<img src="second.jpg"/>
<img src="third.jpg"/>
<img src="fourth.jpg"/>
<!-- Second row -->
<img src="fifth.jpg"/>
<img src="hero.jpg" class="hero"/>
<img src="sixth.jpg"/>
<!-- Third row -->
<img src="seventh.jpg"/>
<img src="eighth.jpg"/>
<!-- Bottom row -->
<img src="ninth.jpg"/>
<img src="tenth.jpg"/>
<img src="eleventh.jpg"/>
<img src="twelfth.jpg"/>
</div>
There's a few reasons not to do it this way:
- Screen readers will simply follow document order - meaning that screen reader users will not know about your hero until after they've gone through several other images already.
- Similarly, if you wanted to display this on a narrow screen, the easiest thing to do is make the gallery container a simple
block
, but the hero image will be buried below several other images. - Finally, if you want to change the layout in
grid-template-areas
in the future, the grid order changes.
If something is visually distinct within a document, it is probably a good idea to place it near the top of the document (as a general rule, and there will always be exceptions):
<div class="gallery">
<img src="hero.jpg" class="hero"/>
<!-- Top row -->
<img src="first.jpg"/>
<img src="second.jpg"/>
<img src="third.jpg"/>
<img src="fourth.jpg"/>
<img src="fifth.jpg"/>
<img src="sixth.jpg"/>
<img src="seventh.jpg"/>
<img src="eighth.jpg"/>
<img src="ninth.jpg"/>
<img src="tenth.jpg"/>
<img src="eleventh.jpg"/>
<img src="twelfth.jpg"/>
</div>
Responsive design
As mentioned above, the simplest thing to do with a grid as a screen/window gets smaller is to collapse it by changing the container's display property to block
. This will immediately invalidate all the other grid properties, and the contents will render in the document order.
But it's also possibly to adapt the layout in grid-template-areas
instead, so we can do things like this:
.gallery {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(4, min-content);
grid-template-areas: ". . . ."
". hero hero ."
". hero hero ."
". . . .";
}
@media (max-width: 768px) {
.gallery {
grid-template-columns: repeat(2, 1fr);
grid-template-rows: repeat(auto-fit, min-content);
grid-template-areas: "hero hero";
}
}
.gallery .hero {
grid-area: hero;
}
Or you could try something like this:
In both cases, the only thing we have to change to be responsive is the parent's grid properties - the child elements will automatically move around as the window resizes.
A plugin for Tailwind CSS
While I'm happy to work with vanilla CSS, I also use Tailwind CSS a lot in various projects. Because of this, I created a plugin that helps create utilities for grid areas - Grid Areas for Tailwind CSS.
// tailwind.config.js
{
//...
theme: {
extend: {
gridTemplateAreas: {
'layout': [
'header header header',
'nav main main',
'nav footer footer',
],
},
gridTemplateColumns: {
'layout': '24rem 1fr 2rem',
},
gridTemplateRows: {
'layout': '6rem
3rem
1fr
auto',
},
}
//...
}
<body class="grid grid-areas-layout grid-cols-layout grid-rows-layout h-full">
<header class="grid-in-header"></header>
<nav class="grid-in-nav"></nav>
<main class="grid-in-main"></main>
<footer class="grid-in-footer"></footer>
</body>