Are you building your CSS layouts wrong? Start using Flexbox and Grid!

Oscar Puente
Frontend at Accenture
9 min readJun 7, 2022

Have you ever been in the middle of building a page layout when suddenly a thought crosses your mind: “There may be a better way to do this...”? Well, guess what? If it crossed your mind, most likely there’s a better way. If it didn’t...well, there probably is too but you’re thinking of something else.

Modern CSS layout models have been around for quite a while now, but they often get overlooked, either because of the use of Frontend frameworks or because of the use of outdated CSS under the premise of “if it’s not broken, don’t fix it” and the reluctance to move onto new trends.

I’d like to talk to you about two of these models: Flexbox and Grid, and then go a little bit further and discuss some of the most common questions people have, such as “when should I use them?”, “which one is better?” or “why did the chicken walk to cross the road?” You should have the answer to all these questions by the end of this article.

We will talk about some properties and concepts behind both Flexbox and Grid. However, this shouldn’t be considered an in-depth guide, but more of a presentation of their capabilities and a comparison between them.

Flexbox

Flexbox is the oldest of the two. It came to do what we were trying to do with floats at the time, providing a better way to align our content. It gives a container the ability to modify the properties of its child elements to fit within the available space. The key differentiator of flexbox is that it’s unidimensional. This means that it requires a direction to be set in its context, either vertical or horizontal, and it will only work in that direction.

Let’s use the following example:

<div class=”container”>
<div class=”child”>Lorem ipsum dolor sit amet.</div>
<div class=”child”>Sed aliquam tincidunt arcu at maximus.</div>
<div class=”child”>Vivamus varius velit sit amet tellus...</div>
</div>
.container {
display: flex;
}
Basic Flexbox implementation

As you can see in this example, it’s as easy to set up as to declare the property display:flex in a container, but don’t let this fool you. Even if you don’t see them, there are properties doing magic behind the scenes that you need to be aware of to get a better understanding of this method. Some of them are:

flex-direction: row. This is the default direction applied to the container and it’s what makes the items inside align to the side of each other. It can also be set to flex-direction: column.

flex-wrap: nowrap. This property restricts the child elements to stay on a single line. They may overflow out of the container, get their content to stack vertically, or even implode creating a new timeline, before they can even consider using an additional line. If you’re feeling kind though, you can change its value to wrap.

flex-shrink: 1. If you’re not allowed to wrap to a different line, you may as well shrink. This default value allows the child elements to reduce their width if they don’t fit in the container.

These properties (and many more) make the flex context work. The thing is that some of them apply to the container while others apply to the child elements. To remember which goes where, you can think of it as a parent-child relationship. The container (parent) delimits the playground and general rules, and the children do whatever they want under those conditions.

.container {
display: flex;
flex-wrap: wrap;
}
.child {
flex-grow: 1;
}
Flexbox implementation using flex-wrap and flex-grow

In this example, you can see a different flex context. It allows their items to wrap to multiple lines and grow if they have space available (the opposite of flex-shrink). There’s a question that this may arise, though:

Why are we seeing an apparent bidimensional layout if flexbox is supposed to be unidimensional?

The answer is simple: I lied to you.

No, not really, it’s still unidimensional. When the flex container allows the child elements to wrap to multiple lines, every single one of those lines acts as its own container; they’re not aware of the existence of each other. That’s the reason we can easily make dynamic layouts that have a different number of elements on each row with flexbox.

As I mentioned at the beginning, Flexbox provides a better and easier way to align content and this usually is the main reason why people use it. You can align content along the chosen axis in flex-direction but also in the opposite one.

Keep in mind that aligning content is a way to deal with the available space in your container, which means that you will have to choose between this and other features such as flex-grow. In order to show an example of this, let’s change the content of the child elements for numbers.

.container {
display: flex;
height: 100px;
background: lightgray;
}

You should note two things here. One, there’s a set height for the container; the child elements are stretching to fill that space. That happens due to the property align-items which by default is set to stretch. This property acts upon the opposite direction of your context. Two, there’s space available because the child elements are too small to fill the whole container. Let’s change things up a little.

.container {
display: flex;
height: 100px;
background: lightgray;
justify-content: flex-end;
align-items: center;
}

As you see, justify-content can be used to align the child elements in the set direction (horizontal in this case), while align-items does it vertically. Other possible values for justify-content can be space-around, space-between and flex-start (this one applies to align-items as well).

Important: align-items is not the exact equivalent of justify-content. Look up align-content.

Grid

Grid was born as a CSS solution created specifically for solving layout problems. A lot of what we used for the same purpose before (tables, floats and even flexbox in some use cases) was nothing more than a bunch of hacks resulting from the lack of an actual grid implementation.

The key feature of Grid is the ability to create bidimensional layouts. Let’s add a fourth element to our HTML markup to show you how it goes:

<div class=”container”>
<div class=”child”>Lorem ipsum dolor sit amet.</div>
<div class=”child”>Sed aliquam tincidunt arcu at maximus.</div>
<div class=”child”>Vivamus varius velit sit amet tellus...</div>
<div class=”child”>Lorem ipsum dolor sit amet.</div>
</div>
.container {
display: grid;
grid-template-columns: 1fr 100px 40%;
}
Basic Grid implementation

As you can see in the example, working with two dimensions requires a little bit more setup, you can’t have default values until you have given information for one of those dimensions at least.

In this case, we’re using grid-template-columns, which is a very interesting property. It receives a set of values separated by a blank space. Each of these values represent the width of one of the columns in whatever unit you want to use. The number of values provided will be the number of columns added. So, we have 3 columns with a width of 1fr, 100px and 40% of the container’s width, respectively. Since we specified only 3 columns, the fourth child element jumps to the next row. But…

What on earth is 1fr?

This unit is a great addition to CSS but it only works in a grid context. It stands for fraction and, as the name suggests, it’s equivalent to a fraction of the available space. All the values using the fractional unit are added together and the result is used as the denominator of the fraction. For example:

.container {
display: grid;
grid-template-columns: 1fr 2fr 1fr;
}
Grid implementation using fractional units

1 + 2 +1 = 4. So, each of these columns’ width will be 1/4, 2/4 and 1/4 respectively.

I know, I know, you may be thinking that you can perfectly do this using 25% 50% 25%, and you may be right. Fractional units, though, are especially useful when used in conjunction with margins, gaps or elements using absolute units for their width. All those values will be calculated first, and then the remaining space will be distributed among the elements using fractional units. That’s something you can’t get done very well with percentages.

You can also use the repeat function in grid-template-columns to avoid having to type in the same value for multiple columns, and the grid-template-rows property to define a height for each row if the default one doesn’t work for you.

.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: 300px 100px;
}

What makes CSS grid truly great is the ability to play with the elements’ size and position within the grid, thanks to the use of lines. A line is the space at the start, end, and in between each element of a grid container. You can see each of these marked with a number in the inspector tool, by clicking on the grid button right next to the HTML tag of the grid container.

Inspecting grid container with Firefox

You can set specific properties for each grid element to allow them to span across the vertical or horizontal lines. This can be done by either typing the number of lines it will span across or the specific lines where it will start and end. The rest of the items will be pushed or pulled to a different row or column as needed.

.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
.child:first-child {
grid-column: span 2;
grid-row: span 2;
}
.child:last-child {
grid-column: 1/4;
}

Now, if all this weren’t enough, you could go even further and accomplish bigger and more complex layouts that are easier to maintain by using grid areas.

Each element is given a name with the property grid-area. Then, the page’s structure is represented visually with the property grid-template-areas. You could also use the shorthand grid-template which includes the area name and the size of each individual column and row.

Other additional things to note here are the ability to specify empty areas and the convenience of having your whole layout in a single property for easier responsive design.

The big questions

If, by any chance, you already feel confident about the differences and use cases of Flexbox or Grid, then you’re more than good to go, you’re the chosen one. If you’re not, don’t worry, that’s perfectly normal.

In some cases, some of the basic and most notable differences between these two models can help you know when to use each or at least get you closer to a decision. It could be that you’re specifically looking to build a unidimensional layout, or that you want to build a full-page layout that can be easily adapted to different screen sizes. However, none of these should automatically exclude one of the two options. They both can shine in specific use case scenarios, and they can also accomplish very similar results in different ways. They could even be used together.

There is one thing though, that has helped me come to a decision before, and it may be useful for you, and that’s the question:

Do I want the content to shape the layout or the layout to shape the content?

If you didn’t notice, I didn’t declare any explicit width or height in the example flexbox layout; the content inside each element determined its size. On the example grid layout, I did declare the size of each column and sometimes the rows too.

Just keep in mind that if you feel like you’re going against the method and constantly overriding default behavior, then that should be a very good indicator that you may be better taking a different path. With due time and practice, it will come to you naturally.

Oh, and I almost forgot about the chicken. To be honest, I don’t know why it crossed the road walking. I guess it didn’t want to float…I’ll show myself out.

--

--