Pixel perfect columns with CSS3

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!

Recent articles

loading
×