CSS Grid VS Flexbox: A Practical Comparison
Not too long ago, the layout for all HTML pages was done via tables, floats, and other CSS properties that were not well suited for styling complex web pages.
Then came flexbox – a layout mode that was specifically designed for creating robust responsive pages. Flexbox made it easy to properly align elements and their content, and is now the preferred CSS system of most web developers.
Now we have a new contender for the best-system-to-build-html-layouts trophy (trophy title is a work in progress). It is the mighty CSS Grid, and by the end of this month, it will be available natively in Firefox 52 and Chrome 57, with other browsers (hopefully) following soon.
A Basic Layout Test
To get a sense of what it’s like to build layouts with each system, we’ve build the same HTML page twice – once with flexbox, and another time with the CSS grid. You can download both projects from the Download button near the top of the article, or inspect them in this online demo:
The design is pretty basic – it consists of a centered container, inside of which we have a header, main section, sidebar, and a footer. Here are the main “challenges” that we’ll have to solve, while keeping CSS and HTML as clean as possible:
- Position the four major sections of the layout.
- Make the page responsive (the sidebar goes below the main content on smaller screens).
- Align the contents of the header – navigation to the left, button to the right.
As you can see, for the sake of the comparison we’ve kept everything very simple. Let’s start with problem number one.
Challenge 1: Position The Page Sections
Flexbox Solution
We’ll start off with the flexbox solution. We add display: flex
to the container and direction its children vertically. This will position all the sections one under another.
.container { display: flex; flex-direction: column; }
Now we need to make the main section and the sidebar stand next to each other. Since flex containers are generally one-directional, we will need to add a wrapper element.
<header></header> <div class="main-and-sidebar-wrapper"> <section class="main"></section> <aside class="sidebar"></aside> </div> <footer></footer>
We then make the wrapper display:flex
and flex-direction
it in the opposite direction.
.main-and-sidebar-wrapper { display: flex; flex-direction: row; }
The last step is to set the size of the main section and the sidebar. We want the main content to be three times the size of the sidebar, which isn’t hard to do with flex
or percentages.
.main { flex: 3; margin-right: 60px; } .sidebar { flex: 1; }
As you can see flexbox did pretty well, but we still needed quite a lot of CSS properties + an additional HTML element. Let’s see how the CSS grid will do.
CSS Grid Solution
There are a couple of different ways to use the CSS grid, but we will go with the grid-template-areas syntax, as it seems most suitable for what we are trying to accomplish.
First we will define four grid-area
-s, one for each page section:
<header></header> <!-- Notice there isn't a wrapper this time --> <section class="main"></section> <aside class="sidebar"></aside> <footer></footer>
header { grid-area: header; } .main { grid-area: main; } .sidebar { grid-area: sidebar; } footer { grid-area: footer; }
Then we can set up our grid and assign the placement of each area. The code below may seem quite complicated at first, but once you get to know the grid system it becomes really easy to grasp.
.container { display: grid; /* Define the size and number of columns in our grid. The fr unit works similar to flex: fr columns will share the free space in the row in proportion to their value. We will have 2 columns - the first will be 3x the size of the second. */ grid-template-columns: 3fr 1fr; /* Assign the grid areas we did earlier to specific places on the grid. First row is all header. Second row is shared between main and sidebar. Last row is all footer. */ grid-template-areas: "header header" "main sidebar" "footer footer"; /* The gutters between each grid cell will be 60 pixels. */ grid-gap: 60px; }
And that’s it! Our layout will now follow the above structure, and because of the way we’ve set it up we don’t even have to deal with any margins or paddings.
Challenge 2: Make Page Responsive
Flexbox Solution
The execution of this step is strongly connected to the previous one. For the flexbox solution we will have to change the flex-direction
of the wrapper, and adjust some margins.
@media (max-width: 600px) { .main-and-sidebar-wrapper { flex-direction: column; } .main { margin-right: 0; margin-bottom: 60px; } }
Our page is really simple so there isn’t much to rework in the media query, but in a more complex layout there will be lots and lots of stuff to redefine.
CSS Grid Solution
Since we already have the grid-areas
defined, we just need to reorder them in a media-query. We can use the same column setup.
@media (max-width: 600px) { .container { /* Realign the grid areas for a mobile layout. */ grid-template-areas: "header header" "main main" "sidebar sidebar" "footer footer"; } }
Or, we can redefine the entire layout from scratch if we think that’s a cleaner solution.
@media (max-width: 600px) { .container { /* Redefine the grid into a single column layout. */ grid-template-columns: 1fr; grid-template-areas: "header" "main" "sidebar" "footer"; } }
Challenge 3: Align Header Components
Flexbox Solution
Our header includes some links for navigation and a button. We want the nav to be on the left and the button on the right. The links inside the nav have to be aligned properly next to each other.
<header> <nav> <li><a href="#"><h1>Logo</h1></a></li> <li><a href="#">Link</a></li> <li><a href="#">Link</a></li> </nav> <button>Button</button> </header>
We’ve already done a similar layout with flexbox in one of our older articles – The Easiest Way To Make Responsive Headers. The technique is really simple:
header { display: flex; justify-content: space-between; }
Now the navigation list and the button are properly aligned. All that is left is to make the items inside the <nav>
go horizontally. It’s easiest to use display: inline-block
here, but since we are going full flexbox, let’s apply a flexbox-only solution:
header nav { display: flex; align-items: baseline; }
Only two lines! Not bad at all. Let’s see how CSS grid handles it.
CSS Grid Solution
To split the nav and the button we will have to make the header display: grid
and set up a 2-column grid. We will also need two extra lines of CSS to position them at the respective borders.
header{ display: grid; grid-template-columns: 1fr 1fr; } header nav { justify-self: start; } header button { justify-self: end; }
As for the inline links inside the navigation – we couldn’t get it quite right with CSS grid. Here is how our best try looks:
The links are inline but they can’t be properly aligned, since there isn’t a baseline
option like in flexbox’s align-items
. We also had to define yet another subgrid.
header nav { display: grid; grid-template-columns: auto 1fr 1fr; align-items: end; }
It’s clear that the CSS grid struggled with this part of the layout, but that isn’t too surprising – it’s focus is on aligning containers, not so much the content inside them. It just isn’t meant for doing finishing touches.
Conclusion
If you’ve made it through the whole article (in which case great job!), the conclusion shouldn’t come as a surprise to you. The truth is, there isn’t a better system – both flexbox and the CSS grid are good at different things and should be used together, not as alternatives to one another.
For those of you who skip directly to the conclusion of articles (no worries, we do it too), here is a summary of the comparison:
- CSS grids are great for building the bigger picture. They makes it really easy to manage the layout of the page, and can even handle more unorthodox and asymmetrical designs.
- Flexbox is great at aligning the content inside elements. Use flex to position the smaller details of a design.
- Use CSS grids for 2D layouts (rows AND columns).
- Flexbox works better in one dimension only (rows OR columns).
- There is no reason to use only CSS grids or only flexbox. Learn both and use them together.