A Case for the 1-Dimensional Grid

This article, part of the writing collection, was published on and last updated on .

Reaching for Grid might feel like overkill for a one-column layout, but I hope this technique shows how useful it is in understanding what your CSS is doing!

Container Queries are stable now in modern browsers, so I’ve revised that section of this article.

By and large, it feels like nowadays the general advice is to use Flex for 1-dimensional layouts and Grid for 2-dimensional layouts. This is mainly due to Flex only being able to define a flow of content in either the block or inline directions, whereas Grid can operate on both directions.

We’ll use short block of HTML as an example, and we need to figure out how to represent the content responsively, with two columns of content for wider browsers and one column for smaller browsers:

Sorry, this code snippet failed to load, but you can still check it out over on CodePen!

<main class="parent">
	<h1 class="title">Title</h1>
	<article class="content">Content</article>
	<aside class="sidebar">Sidebar</aside>
</main>

Sorry, this code snippet failed to load, but you can still check it out over on CodePen!

When it comes to architecting the columns of a website responsively across browsers, here are a few display options that we’ve reached for over the years.

Block Permalink

By defining each element that needs to slide into the column as display: block, they will naturally stack in the page’s block direction (code is a rough approximation):

.sidebar {
	float: left;
}
@media (max-width: 500px) {
	.title,
	.sidebar,
	.content {
		float: none;
		display: block;
	}
}

However, without further display options, this doesn’t offer us a clean method of moving the sidebar to where we want it to be, visually. That's where Flex came in…

Flex Permalink

When display: flex became an option, we suddenly had a bunch of new tools in our kit to exercise control over the flow of content. In particular, the ability to change the order of items in the flow allows us to do powerful things like have the sidebar come sequentially after the content but visually appear before it in the browser (code is a rough approximation).

.parent {
	display: flex;
	flex-direction: column;
}
@media (min-width: 501px) {
	.parent {
		flex-direction: row;
	}
	.sidebar {
		order: 1;
	}
}

Grid Permalink

We can take this idea of reordering things on the page with Flex to the next level with Grid.

Let’s start by making some simple declarations about where we want our grid children to live, and this is where this method really shines:

.title {
	grid-column: main;
	grid-row: title;
}

.sidebar {
	grid-column: aside;
	grid-row: sidebar;
}

.content {
	grid-column: main;
	grid-row: content;
}

Because we have the ability to define the name of our grid areas, we can tell the title, sidebar, and content blocks where to live and reading the code gives you a clear understanding of where they live… but where do they live?

Let’s start with the more complicated part, the desktop/wider layout:

.parent {
	display: grid;
	grid-template-columns:
		[aside-start] 1fr [aside-end main-start] 3fr [main-end];
	grid-template-rows:
		[sidebar-start title-start]
		auto
		[title-end content-start]
		auto
		[content-end sidebar-end];
}

In the same way that we’ve told the grid’s children where to live, here we’re defining the order and dimensions of the grid’s columns and rows with some defined names.

In the column/inline direction, we’ve defined an aside column that’s 1fr wide and a main column that’s 3fr wide.

In the row/block direction, we’ve defined a sidebar row, title row, and content row, each with a height of auto.

Now let’s work out how to use the same named areas for mobile/thinner browsers:

.parent {
	display: grid;
	grid-template-columns: 1fr;
	grid-template-rows:
		[title-start]
		auto
		[title-end sidebar-start]
		auto
		[sidebar-end content-start]
		auto
		[content-end];
}

In the column/inline direction, we’ve defined a single column with a width of 1fr. You may notice that we haven’t defined any names here, as we did before. This means that each grid child element will be automatically matched, and their grid-column declarations will have no effect. But that’s fine because this does exactly what we want with less moving parts. In fact, you can completely remove the entire grid-template-columns: 1fr; declaration here because the browser can make an inference and apply this on its own.

In the row/block direction, things look very much the same as desktop/wider code, but the arrangement of start and end named areas is different. For this mobile/thinner layout, we don’t have any overlapping rows anymore and are stacking the title block on top of the sidebar block on top of the content block.

So how do we combine everything here to create a responsive layout?

.parent {
	display: grid;
	grid-template-rows:
		[title-start]
		auto
		[title-end sidebar-start]
		auto
		[sidebar-end content-start]
		auto
		[content-end];
}
@media (min-width: 501px) {
	.parent {
		grid-template-columns:
			[aside-start] 1fr [aside-end main-start] 3fr [main-end];
		grid-template-rows:
			[sidebar-start title-start]
			auto
			[title-end content-start]
			auto
			[content-end sidebar-end];
	}
}

Because we’ve made the part of the code that tells an element where to live (grid-column and grid-row) completely rely on areas defined by us, we don’t need to make any declarations about layout within the title, sidebar, or content blocks, a really useful difference from using Flex’s order.

The other clear advantage is not having to contend with a tangled mess of order declarations, reminiscient of z-index woes of days gone past.

Even if we need to make more complex additions to a layout built like this, the complication at least lives inside the grid parent’s code, rather than spread throughout the code of its children. The process is quite clear from the code that we’ve laid out, too: define where your addition should live with grid-column and grid-row; create and/or redefine your named areas and their dimensions as necessary to accomodate.

Useful tip: you don’t necessarily need to fill a named area! Depending on the arrangement of columns and rows, an area can have no width, no height, and even both. This can be really handy if you wanted to do something like show the sidebar above the content on some pages and below on others: you could define a sidebar-top row and a sidebar-bottom row and control which one your sidebar should live in with some extra CSS.

Container Queries Permalink

Since Container Queries are now stable in modern browsers, we can use them as a replacement for the Media Queries that we used to toggle between the desktop/wider layout and mobile/thinner layout:

body {
	container: page / inline-size;
}
.parent {
	display: grid;
	grid-template-rows:
		[title-start]
		auto
		[title-end sidebar-start]
		auto
		[sidebar-end content-start]
		auto
		[content-end];
}
@container (inline-size > 500px) {
	.parent {
		grid-template-columns:
			[aside-start] 1fr [aside-end main-start] 3fr [main-end];
		grid-template-rows:
			[sidebar-start title-start]
			auto
			[title-end content-start]
			auto
			[content-end sidebar-end];
	}
}

You can also make an anonymonous reply (using Quill and Comment Parade).

1 Response

  1. 1 Link