Converting a Flexbox layout to a Grid layout

Matti Bar-Zeev - Oct 15 '21 - - Dev Community

In this post you will join me as I convert an application layout from using CSS Flexbox into using CSS Grid. As always, my example will be conducted on my WordSearch game which I’m experimenting on (god knows it can take the beating ;).

So what we have is this game board which is divided into 3 main parts:

  • masthead
  • game-body
    • words-panel
    • board
  • footer

The markup for this looks like this:



<div className="App">
    <Masthead />
    <div className="game-body">
        <WordsPanel />
        <Board />
    </div>
    ...
</div>


Enter fullscreen mode Exit fullscreen mode

And this is how Flexbox was integrated with it -
The App which is the root of the application was in flex display and set to use a column direction. The masthead is a simple Flexbox and it should remain as such since this serves well the purpose of in a single dimensional layout.
Moving down we have the game-body which is made of 2 sections: words-panel and board. Game-body is a Flexbox itself, with a horizontal direction.
At the bottom there is the footer which is a Flexbox that serves it well and should not be changed.
See the image below to understand better how the parts currently are arranged (I used devtools Flexbox indications to emphasis it better):

Image description

Now, this all works well, but it has too many flexboxes, hard to wrap your head around how the markp is arranged, and to be honest it really calls out for a Grid display, so let’s start converting it into such.

This is the current style of the .app class:



.App {
   display: flex;
   flex-direction: column;
   height: 100%;
}


Enter fullscreen mode Exit fullscreen mode

And I will modify it to a be a grid display:



.App {
   display: grid;
   width: 100vw;
   height: 100vh;
}


Enter fullscreen mode Exit fullscreen mode

I’m using the 100vw and 100vh since I would like this root Grid to take the entire width and height of the viewport.
That seemed to work well, but I lost some of the desired alignment of the game-board and the footer takes more height than it should:

Image description

The concept of grid-template-areas seems to be very suitable for this case, since you can see right away how the different parts/areas will be arranged, and they all have semantic ids to them which makes it easier to understand and maintain.
I would like to take the different parts mentioned above and give them grid-area ids so I can later manipulate their layout on the top level grid CSS rule.
Here are the CSS classes, just with the grid-area id added, still having the old CSS rules:



.game-body {
   grid-area: gamebody;
   display: flex;
   height: 100%;
}

.words-panel {
   grid-area: wordspanel;
   width: 230px;
   list-style: none;
}

.board {
   grid-area: board;
   position: relative;
   flex: 1 1 100%;
   margin: 16px;
}

.footer {
   grid-area: footer;
   display: flex;
   justify-content: flex-start;
   padding: 6px;
   background-color: darkslategrey;
   color: white;
}


Enter fullscreen mode Exit fullscreen mode

But now that these classes have a grid-area rule, the grid tries to lay them out and it looks like an entire mess:

Image description

No worries, I’m on it.
I’m going to start arranging the different sections using the grid-area way.
I first set a grid-template-rows, so the grid will be splitted into 2 rows, the upper one with the height of 60px (the masthead height including padding) and the bottom one should stretch the full length. Taking 1 part of the available space:



.App {
   ...
   grid-template-rows: 60px 1fr;
}


Enter fullscreen mode Exit fullscreen mode

Now for the columns - I have 2 columns. The first one holds the words-panel (set to 230px) and the second one holds the game-board which takes the remaining fraction of the grid. Let’s put that on the grid definition as well:



.App {
   ...
   grid-template-columns: 230px 1fr;
}


Enter fullscreen mode Exit fullscreen mode

Now we can start to arrange the different areas we got. The nice thing about it is that you arrange it in a very visual kind of way, a somewhat declarative manner:



.App {
   ...
   grid-template-areas:    "masthead masthead"
                           "wordspanel gamebody"
                           "footer footer";
}


Enter fullscreen mode Exit fullscreen mode

Ok, it’s starting to get into shape. We have the masthead and footer laid out nicely, but the words-panel and the board are off.
I’m assuming that this is due to the fact that the words-panel and the board are nested inside the game-body div (a must if we want to achieve this layout using Flexbox). You can already see that the grid-area gamebody is redundant and can be replaced with the board grid-area, so replacing it is:



.App {
   ...
   grid-template-areas:    "masthead masthead"
                           "wordspanel board"
                           "footer footer";
}


Enter fullscreen mode Exit fullscreen mode

And adjust the markup accordingly:



<div className="App">
    <Masthead />
    <WordsPanel />
    <Board />
    ...
</div>


Enter fullscreen mode Exit fullscreen mode

And with that I’ve reached the initial layout the game was in, but with less markup, less flexbox rules and a much easier way to understand how the display is laid out!
(I’ve marked the grid lines and areas for better understanding)

Image description

Here is how the CSS looks like now:



.App {
   display: grid;
   width: 100vw;
   height: 100vh;

   grid-template-rows: 60px 1fr;
   grid-template-columns: 230px 1fr;

   grid-template-areas:    "masthead masthead"
                           "wordspanel board"
                           "footer footer";
}

.masthead {
   grid-area: masthead;
   display: flex;
   justify-content: space-between;
   align-items: center;
   padding: 10px;
   background-color: darkslategrey;
   color: white;

   Button {
       margin: 0 6px;
       align-self: flex-end;
   }
}

.words-panel {
   grid-area: wordspanel;
   width: 230px;
   list-style: none;
}

.board {
   grid-area: board;
   position: relative;
   flex: 1 1 100%;
   margin: 16px;
}

.footer {
   grid-area: footer;
   display: flex;
   justify-content: flex-start;
   padding: 6px;
   background-color: darkslategrey;
   color: white;
}


Enter fullscreen mode Exit fullscreen mode

As always, if you have any ideas on how to make this better or any other technique, be sure to share with the rest of us!

Cheers

Hey! If you liked what you've just read be sure to also visit me on twitter :) Follow @mattibarzeev 🍻

Photo by Sigmund on Unsplash

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .