Responsive, full-width columns without a grid

Dennis Best
4 min readJul 23, 2016

--

I’ve been doing front-end development for a long time and I’ve never liked grid systems. So, anytime I start a new project, I resist. Here are my primary objections:

They don’t feel modern

The concept of the grid comes from the print world and their web counterparts pre-date the mobile revolution. Defining a large canvas with vast array of columns is not a modern (mobile-first) way to think.

I usually don’t need a “system”

The vast majority of layouts on the web are quite simple. We’re talking about one, two, or three columns in a couple of places. I don’t need a 12 or 16 column grid, javascript, and CSS bloat to make this happen.

You need a decoder ring

Most grid systems require me to add classes to my markup like grid_7 prefix_ 1 to a parent element and grid_2 alpha to a child element. (I think these particular classes translate to “Buy more Ovaltine!”) No, thanks.

Some systems require specific markup

Bootstrap’s grid wants markup like this:

<div class="container">
<div class="row">
<div class="col-*-*"></div>
</div>
<div class="row">
<div class="col-*-*"></div>
<div class="col-*-*"></div>
<div class="col-*-*"></div>
</div>
<div class="row">
...
</div>
</div>

This isn’t horrible, but the content isn’t determining the markup as much as the demands of the layout are. If I want to adjust the appearance of my page, I have to edit the markup, not the styles. That’s gross.

Not that there’s anything wrong with that…

I realize these objections aren’t huge. And people are fine with them. But they gnaw at me and keep me awake at night. There are great solutions like Neat that keep the layout where it belongs, but I wanted to see if could come up with something a bit simpler.

So here’s my solution

The gnawing has stopped and I can sleep at night. If you find this useful, great. If not, remember, it’s just one approach.

NOTE: The only thing we need is Sass, but Autoprefixer is nice to have so we don’t need to mess with vendor prefixes.

We’re gonna use flexbox. So if you care deeply about old versions of IE, you’ll need to make some adjustments — and you have my deepest condolences. I tested this on a Mac in Chrome, Safari, and Firefox. I also tested it in IE 11 and it looked great. (I didn’t want to push my luck much beyond that. Test your own dang site!)

Anyway, let’s get on with it and make columns.

Padding

If we’re building a columnar layout, we might want space between elements. You can adjust this to your needs by changing the default-padding variable. We’ll start with this variable and some sanity:

$default-padding: 4rem;
*{box-sizing: border-box;}

Then we add a simple mixin:

@mixin columns($col-number) {
display: flex;
flex-direction: row;
flex-wrap: wrap;
width: calc(100% + #{$def-pad} / 2);
margin-left: calc((#{$def-pad} / 2)* -1);
>*{
flex: $col-number 1 (100% / $col-number);
max-width: 100% / $col-number;
padding: $def-pad / 2;
}
}

The magic here is that we are solving a problem when you add padding to flexbox columns. Normally you get padding AROUND the parent element and things don’t line up. This bothers people, including me. So we solve it with math, moving the parent over and widening it. I know support for the calc function can be spotty, but it works beautifully here. (If it fails, there’s no harm, we just wind up with some padding on the parent.)

In theory, we could also do something like this…

&:nth-child(#{$col-number}n+1) { padding-left: 0; }
&:nth-child(#{$col-number}+#{$col-number}) { padding-left: 0; }

… but that makes the outer columns a bit wider than the others. (Still, it’s kind of a fun trick.)

Anyway, the columns mixin applies a columnar layout to any parent and the children line up like little Von Trapps. No special classes required. Using it is as easy as this:

.my-container-thingy {
@include columns(4);
}

That’s it. Four columns. Change that 4 to anything you like. We’ll make it responsive — in a mobile-first way! — in a bit.

Here’s the markup.

<section class="my-container-thingy">
<div>Bacon</div>
<div>Bacon</div>
<div>Bacon</div>
<div>Bacon</div>
<div>Bacon</div>
<div>Bacon</div>
<div>Bacon</div>
<div>Bacon</div>
<div>Bacon</div>
</section>

You’ll notice we have more than four items. Good eye there. That’s the idea. These columns wrap … and the stragglers don’t fill the next row as will happen with flexbox.

Making it responsive

So this is nice, but let’s make it responsive. Basically, I want to think of four columns (or whatever) as a maximum. I probably want one column on mobile and maybe two or three on tablets, etc.

First, I start with a fairly common breakpoint mixin like this:

$breakpoints: (
"(min-width : 600px)" "medium",
"(min-width : 1025px)" "large",
"(min-width: 1281px)" "xlarge"
);
@mixin breakpoint($point) {
@each $breakpoint in $breakpoints {
$query: nth($breakpoint, 1);
$name: nth($breakpoint, 2);
@if ($name == $point) {
@media #{$query} { @content }
}
}
}

Then I change the my styles like so:

.my-container-thingy {
@include breakpoint(medium) { @include columns(2); }
@include breakpoint(large) { @include columns(3); }
@include breakpoint(xlarge) { @include columns(4); }
}

So the number of columns gets reduced or increased depending on the browser width. There is no “small” because … well… mobile-first. It’s gonna be one column on mobile.

Here’s an embedded fiddle. Click the result tab — there’s no javascript — and resize to your heart’s desire.

Of course, if you’re already on a small screen, you won’t see the other columns.

--

--