Skip to main content

Blog post layout using CSS grids

Recap#

Welcome back!

In the last article, I explained how I tried to achieve a typical blog-post layout using flexbox. Here is the link to the final layout in CodePen.

I mentioned that there were a couple of hacks we had to do to make it work, because of the way flexbox works. It is unidirectional in nature, which means, at any time you can only affect the layout in one direction, row or column. If you wish to make a complex layout with sections spanning across multiple rows and columns in a random fashion, then the best option (and somewhat of the only option) is CSS grids.

The last time I checked the compatibility for CSS grid layout in CanIUse.com, it is hovering around 95%. It is a very good coverage, you can confidently use CSS grids for your layout needs and this number will only become better going forward.

So, why don't we go ahead and implement the same layout in CSS grids?

Final result#

Just like the previous one, we achieve the same UI, no difference at all. But internally, it is a lot cleaner and easy to understand.

Here is the codepen.

Demo#

blog-article-layout-using-css-grid

This is it, that is what we get. Not so much different from the previous flexbox example, much simpler internally.

Let's look at the improvements#

Addressing the double h1 problem#

We have done some structural changes to the HTML. It no longer has two h1s, which is good for our SEO stuff and semantics.

Now the structure looks like this:

<!-- content wrapper -->
<!-- the h1 title -->
<!-- the left nav -->
<!-- the reading column -->
<!-- content wrapper ends -->

This is so much simpler than the one in flexbox version.

We use the beauty of CSS grids and specifically grid-template-areas property to achieve our desired layouts, both in desktop and mobile views.

To make grids work, we apply display: grid to the root level .content-wrapper div.

Unlike flexbox which might require multiple parent divs to achieve complex layouts, the grid system does require only one parent div and all the sections can be immediate children of that parent.

Lets see the magic of grid-template-areas.

In desktop view:#

The content wrapper div looks like this:

.title {
grid-area: title; // this is the grid area label
}
.left-nav {
grid-area: leftnav;
position: relative;
max-width: 100%;
}
.reading-column {
grid-area: reading;
}
.content-wrapper {
display: grid;
grid-gap: 0 2rem;
grid-template-areas:
". title"
"leftnav reading";
grid-template-columns: minmax(250px, 1fr) 3fr;
width: 100%;
}

We firstly provide grid-area label to each of the siblings and use those names in grid-template-areas rule of the parent to decide the layout.

There are two strings in the value of grid-template-areas, they represent two rows. And the contents of each string represent the columns. Each string has two tokens, so two columns each.

So, the first row has nothing in the first column (because . represents nothing) and has the header in the second column.

The second row has the leftnav in the first column space and the reading section in the second column space.

This is what we want.

Mobile view:#

Now lets look at the grid-template-areas value in case of mobile view:

@media only screen and (max-width: 900px) {
.content-wrapper {
grid-template-areas:
"title"
"leftnav"
"reading";
grid-template-columns: 1fr;
}
}

Here there are three strings, so three rows. And each string has only one token, so only one column. As simple as that.

Controlling width of the left nav section#

Notice this line:

.content-wrapper {
...
grid-template-columns: minmax(250px, 1fr) 3fr;
...
}

There is a function we are using here called minmax. What this does is it fixes the minimum and maximum width values for each column (since it is grid-template-columns). In this particular case, we want the first column of the .content-wrapper to have a minimum of 250px and a maximum of 1fr. 1fr means one fraction.

The second column has 3fr always (no minmax applied), so as long as the total width of .content-wrapper is such that the total fractions (1fr + 3fr = 4fr) is more than 4 times 250px, the width of the leftnav column will be that much (total width / 4). But as soon as that becomes 250px or less, the leftnav takes up 250px and the rest is divided to three fractions (3fr).

So, when the overall width is 1200px, the left column width is 1fr (out of 4fr) which is 1/4 of 1200px which is 300px.

Whereas when the overall width is just 900px, the leftnav takes up 250px and the reading section width is 650px (900 - 250).

Summing up, you can notice that the size of the leftnav increases slightly as you keep increasing the window width.

In the mobile view, there is only one column and all the rows have 1fr width, so they occupy complete width.

Reading column width#

There are .reading-column__sections within the reading column and thanks to the below rules, the maximum width of these sections does not exceed 60ch even though the column itself is spanning till the right side edge.

.reading-column__section {
width: 100%;
max-width: 60ch;
}

Conclusion#

If you are new to flexbox and grids in CSS, it is high time you learn them. There are many good resources for you to understand them. The browser support is getting better and better.

Please do suggest if you want me to achieve any other feature in this particular example. Or if you like it, please let me know what you learned.

Subscribe to my YouTube channel for many such developer tips.

Last updated on