
The layout below looks very simple, doesn’t it? A wrapper with dynamic width which contains 6 equally sized boxes, 3 in each row, divided with the same sized gap (given in pixels), except on the edges. Yet it was really complicated to achieve with pure CSS for a long time.

Some of you might remember the good old days when website
layouts were built completely on tables. To create a layout like that
was challenging; you had to calculate widths perfectly and also play
with cellspacing, paddings, border width… yikes.
Luckily tables were replaced with the mighty <div>
tag, but this issue remained unsolved. There were a few workarounds but
they were still a pain to write. For example you had to: float the
divs, add an extra class to each first box in a new row to clear left,
add left and bottom margins (for gaps), remove left margin of boxes in
the first column, remove bottom margins in the last row by adding
negative margin to the wrapper. Also you had to define box widths in
perfectly calculated pixels or you could go with percents, but then you
had to use percents for margins as well to fit the wrapper dynamically.
Not cool.
CSS3 to the rescue
With CSS3 many new, useful features have arrived. First the new column-count
and column-gap
properties looked promising for me, but then I quickly realized that
it’s only suitable for splitting single text blocks into columns.
Then I found flexbox which was a step into the right direction. The sample code below creates our desired layout with a reasonable amount of code:
<style> .columns-3 { display: flex; flex-direction: row; flex-wrap: wrap; justify-content: space-between; } .columns-3 > div { box-sizing: border-box; margin-bottom: 30px; width: calc(1/3*100% - (1 - 1/3)*30px); } .columns-3 div:nth-last-child(-n+3){ margin-bottom: 0px; } /* only for visual feedback */ .columns-3{ background-color: #ddd; } .columns-3 div{ background-color: #4184ff; } </style> <div class="columns-3"> <div>1</div> <div>2</div> <div>3</div> <div>4</div> <div>5</div> <div>6</div> </div>
With calc()
function we can dynamically calculate box widths and subtract the 30px margin. The :nth-last-child(-n+3)
selector targets the last 3 boxes to remove their bottom margins.
Not bad, but what if I want a 1/3 and a 2/3 box in a row? More child targeting, more calculation, more code. No thanks. There is an even more simple way with less code.
The ultimate solution: grid
Finally I stumbled upon the grid. With 3 simple lines we can achieve our column setup, perfectly aligned to the parent, wrapper element:
<style> .columns-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; grid-gap: 30px; } /* only for visual feedback */ .columns-3{ background-color: #ddd; } .columns-3 div{ background-color: #4184ff; } </style> <div class="columns-3"> <div>1</div> <div>2</div> <div>3</div> <div>4</div> <div>5</div> <div>6</div> </div>
The fr
unit represents a fraction of the leftover space in the grid container. So do you want a 2/3 and 1/3 box in a row? No problem! Just change the grid-template-columns
value to 2fr 1fr
and that’s it! Do you want to add padding to the columns? No problem, just add the padding and that’s it. No more recalculations or hacks. You don’t even have to use box-sizing: border box;
to maintain the exact same box size. It will automatically calculate the optimal width for you! Do you need gaps only between the columns or the rows? Replace grid-gap
with the grid-column-gap
or grid-row-gap
property.
Obviously the CSS grid system has way more cool features, but this one is my favorite. You can find more info about the CSS grid here.
Happy coding!