Contact us
Contact
Blog

Development

9 min read

How to Build Modern Layouts With CSS Grid

Marina
Marina
Web Developer

You want to make compound yet modern layout designs? Great, let me show you how to achieve that easily using just the CSS grid. So get comfortable, grab some sweets and let’s start - many examples await. :)

Introduction

Unlike flexbox, the CSS grid is a two-dimensional system. It has vertical and horizontal lines that are defining rows and columns, which makes it easier to design web pages without having to use floats and positioning. A grid consists of one parent element - container, and one, or more child elements - items. The smallest unit in a grid is called a cell.

So far, these are the keywords you need to know. But let’s jump to the basic concepts to learn even more.

Basic concepts

First things first, you want to create a simple UI by placing six simple divs  (items) in the container. The container needs to be displayed as a grid. At this point, you won’t see any difference but make sure to check your browser once again. Inspect your page, your go-to elements, click on the layout tab, and mark grid overlays as checked. Now you should see the grid you created.

The next step is to define the rows and columns. To do that, you need to set the grid-template-columns and grid-template-rows properties to a certain value, e.g. 200px, 30%. You can do it in pixels, percentage, or any available unit you want in the container. If you like shorter formats, you can write only grid-template property with row/column values. For making space between cells, you can add the property grid-gap, or if you want to use the gap only between rows, use the grid-row-gap property (same applied for the column).

const Container = styled.div`
display: grid;
grid-template-rows: 100px 150px;
grid-template-columns: 100px 200px 100px;
// shorter version
// grid-template: 100px 150px / 100px 200px 100px;
grid-gap: 8px;
`

And voila, you created a grid containing 2 rows and 3 columns!


Now, try to resize your screen. You can notice that with the viewport change, those cells stay at the same size. Define the second column to be auto instead of value in pixels, and resize your screen once again. The first and third columns will stay the same and the second one is now flexible.

But, what if you want to make your grid even more responsive? Instead of using current column values, replace them with a new unit called a fraction. And there you go!

Let’s say we have 12 columns. Twelve times writing 1fr can’t be an elegant solution. Instead of that, you can use the repeat function which allows us to repeat columns as many times as we need. It takes two parameters to achieve this: number of repetitions and the size.

grid-template-columns: repeat(12, 1fr)

Simple Web page layout

Let’s imagine a simple web page layout design with a header on the top, a menu sidebar, some content, and of course a footer at the bottom of the page. To achieve this, we’ll use the second example, with just a few modifications.

Before we start managing our layout, you need to get to know two new properties, grid-column and grid-row. Using them you can define from which to which point you want to expand your row and column.

For example, grid-column: 1/3; means the column starts from the first line of the grid, it expands over two columns and it ends at the third line. A different way to define the same thing is to write grid-column: 1/span 2;.

You could also start counting lines from right to left - then it has a negative value. So, if you want your row to start from the first line and to stop at the very end, you can write: grid-column: 1/-1;

With this knowledge, you can make a simple layout for a web page. Just target every item and define the grid-column and grid-row properties for each.

// header
&:nth-child(1) {
grid-column: 1 / -1;
grid-row: 1 / 2;
}
// side menu
&:nth-child(2) {
grid-column: 1 / 2;
grid-row: 1 / 3;
}
// content
&:nth-child(3) {
grid-column: 2 / -1;
grid-row: 2 / 3;
}
// footer
&:nth-child(4) {
grid-column: 1 / -1;
grid-row: 3 / 4;
}

It will look something like this:

visualisation of header at the top, content in the middle, footer at the bottom and menu on the left

Grid template areas

If the previous example was a bit messy due to counting lines, maybe you’ll find this one more helpful. There’s no counting at all and it’s pretty visual. :D

Instead of writing the grid-column and grid-row properties, just replace them with a single line property called grid-area. Give it a value - any name you wish, for example, h for header, m for menu, or f for footer. If you write a dot, the cell will be empty.

const Box = styled.div<{ color:="" string="" }="">`</{>
&:nth-child(1) {
grid-area: h;
}
&:nth-child(2) {
grid-area: m;
}
&:nth-child(3) {
grid-area: c;
}
&:nth-child(4) {
grid-area: f;
}
`

And one last thing to set: in the parent element define the property grid-template-areas in a way you want the elements to be displayed. Every body line represents a single row:

grid-template-areas:
'm m h h h h'
'm m c c c c'
'm m f f f .'

And the result is right here. OK, maybe this design is not the best looking, but you get the point.

visualisation of header at the top, content in the middle, a smaller footer at the bottom, a wide menu on the left

Minmax function

So far, you’ve learned that using fractions will make the grid responsive and cells will expand as long as you increase the resolution on our screen, but also shrink as long as you decrease it. (The first grid image has a width of 300px and the second one 600px.)

grid-template-columns: repeat(3, 1fr);
grid-template-rows: 100px 100px;
visualisation of a grid with eight upright rectangles in two rows
visualisation of a grid with eight landscape rectangles in two rows

But, what if you don’t want to expand the cells that much? What if you just want to have a minimum and maximum width of the column?

For that purpose, you can use the minmax function. Try with the divs from the first example, and let’s see how it’s going to change. You just have to define rows and columns. Columns will repeat and they’re going to have a minimum width size of 100px and a maximum width size of 1fr. That means, if you increase the resolution, as soon as the row has 100px free space, the next cell will take place right there.

visualisation of a grid with six equal rectangles in two rows and two smaller rectangles in a third row

Notice that the third row is not looking the same as the previous two and that’s because we defined only two rows, while every other will have the size of its content. So, to fix that, just replace grid-template-rows with grid-auto-rows, give it one value that will be equal for every cell and that’s it!

grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
grid-auto-rows: 100px;
visualisation of a grid with eight rectangles in three rows

Fit vs Fill

Auto-fit or auto-fill? What’s the difference between those two?

visualisation of two grids with six rectangles over two rows

You are probably having trouble finding the difference between these two. Well, currently there’s none. But, if you change the viewport and turn on the grid overlays, you will see it.

visualisation of two grids with six rectangles over two rows

The Auto-fit property expands the columns so they will take up any available space. The Auto-fill property fills the row with as many columns as it can fit, which means a newly added column will be empty. By turning on grid overlays, you can see how the two extra columns are being created while expanding the grid to a certain resolution.

Gallery

Now, let's use the knowledge from the previous examples and build a cool and responsive gallery. This is your last task.

First, create an array of objects where each object will have an image and a size property. Map through them and display the images. Set the grid with rows and columns - the fourth example might help with this. And this will be the result you get:

grid of eleven images over two rows

Let’s say you want horizontal, vertical, normal, and big images. At this point, they’re all a normal size. A horizontal image will take 2 columns and 1 row, a vertical 1 column and 2 rows, a big one 2 rows and 2 columns, and the normal 1 row and 1 column.

To do that, you just need to define the grid-column and grid-row property for each size. So, for example, the big image will have grid-column: span 2; and grid-row: span 2;.

Use the same logic for defining others.

const Box = styled.img<{ name:="" string="" }="">`</{>
width: 100%;
height: 100%;
${(props) =>
props.name === 'big' &&
css`
grid-column: span 2;
grid-row: span 2;
`};
${(props) =>
props.name === 'horizontal' &&
css`
grid-column: span 2;
grid-row: span 1;
`};
${(props) =>
props.name === 'vertical' &&
css`
grid-column: span 1;
grid-row: span 2;
`};
`

const Gallery = () => {
return (
<container></container>
{gallery.map((item) => (
<box src="{item.img}" name="{item.name}"></box>
))}

)
}

Now it looks like a nice gallery. But what about all those empty spaces? Can normal size pictures be placed here? Of course they can!

grid of pictures with different sizes and empty spaces between them

We just have to define one more property called grid-auto-flow. By default, it’s set to row, but if we change it to dense value it will fill all empty spaces for us.

const Container = styled.div`
display: grid;
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
grid-auto-rows: 100px;
grid-gap: 8px;
grid-auto-flow: dense;
`

And now it looks great, even on mobile devices!

grid of pictures with different sizes
grid of pictures with different sizes on mobile

To conclude

I hope I’ve helped you learn some basics about the CSS grid and maybe even more.

Before I forget, did you know you can use a grid with the flexbox? Cool, right? Now jump into more practicing and experimenting. I wish you luck. :)

Like what you just read?

Feel free to spread the news!

About the author

Marina is a Frontend Developer at COBE.

Marina

Web Developer

Write
Marina
Write COBE
Related Articles

Still interested? Take a look at the following articles.