Building a modern grid system
ZURB's Jonathan Smiley looks at what it takes to create a grid system that will help us build faster, better and with an eye to the future.
- Knowledge needed: Intermediate CSS, intermediate HTML
- Requires: Code editor, browser
- Project Time: 1 hour
- Support file
Building websites is, frankly, awesome. We get to create rich, dynamic experiences that are seen by hundreds, thousands, millions of people all over the world. We get to play with new toys, work with all the best companies, and geek out without coming across as nerds (you know it’s true). That’s the good stuff.
The bad (okay, the challenging) is that we have to contend with more and more types of browsers and devices, and build more and more complex and full-featured sites than we ever have before. That’s why we need grid systems. They help us build things faster and better, and with an eye to the future of our industry. Let’s look at what a modern grid system needs, then build it.
01. The ubiquity of grid systems
Grids aren’t new. There are, conservatively, several dozen perfectly viable grid systems already out there to help you get things done. There are things like 960.gs, Blueprint, Bootstrap, 320 and Up, Golden Grid, semantic.gs ... etc ... etc, ad nauseam. Each brings something to the table, each leaves some things to be desired.
The reason grids are so popular is that they work. When you’re creating a layout for the web, nine times out of 10 a reasonably flexible grid system will get your layout 90 per cent of the way there and save you hours of custom coding. There are legitimate concerns that grid systems limit our thinking, and constrain us to things we ought to be avoiding, but with a little discipline and a solid design process we can dodge that.
02. What we need from a grid
What are we looking for in a grid system? What we’re going to look at here is not a solution to every problem, but it is a forward-thinking (and pretty cool) way of attacking the problem.
Here’s what we need from our grid:
- We need flexibility for real world layouts. We need to have enough columns in a configuration that allows us to cover the majority of our possible layout requirements. That means something divisible in a lot of groups without getting too granular. For our purposes today, we’ll use 12. This lets us easily divvy the page up into halves, thirds, and quarters.
- We need to be able to nest the grid. A single level of columns gets us part of the way there, but having something you can nest down to N levels gives you incredible flexibility.
- We need the grid to be responsive. This may seem like it comes out of left field, but trust me, it’s a requirement. There are, literally, hundreds of different devices your site needs to support and that number is only increasing. 960 grids are dead, and good riddance. Our grid should be fluid (percentage based) and configured to scale down on smaller devices.
- We need the grid to be modifiable. One of the biggest complaints you’ll see from users of a responsive grid system is the difficulty of modifying things such as gutter size. Recalculating percentages based on a change of 4.4% to 3.6% gutters is not a fun way to spend an hour. We’re going to accommodate this by using a relatively little-used CSS property we’ll discuss in a bit. It’s pretty cool.
- We need the grid to be backwards compatible. To a point. The whole world doesn’t use Chrome, or iOS5 Mobile Safari. That being said, our target today will be support for Android, iOS 4+, Webkit browsers, Firefox, and IE8+.
03. Getting started
To get started, we need a blank CSS file called grid.css. We’ll be using this file for our grid, so it can be dropped into any project. As a side note, the grid we’re building today will be exactly (or very close to) the grid used in Foundation 3 (currently on 2.2.1).
First, let’s set up the syntax for the grid (it’s very simple). Basically, we create rows, which hold columns. We’ll use those words, to keep this human readable.
.row { }
.column, .columns { }
.row .one { }
.row .two { }
.row .three { }
.row .four { }
.row .five { }
.row .six { }
.row .seven { }
.row .eight { }
.row .nine { }
.row .ten { }
.row .eleven { }
.row .twelve { }
With these empty declarations, you can see what our syntax would look like in HTML. To create a row with three equally sized columns, we’d do this:
Get top Black Friday deals sent straight to your inbox: Sign up now!
We curate the best offers on creative kit and give our expert recommendations to save you time this Black Friday. Upgrade your setup for less with Creative Bloq.
<div class="row">
<div class="four columns">
...
</div>
<div class="four columns">
...
</div>
<div class="four columns">
...
</div>
</div>
Each of those columns now could contain whatever arbitrary content we want. Now, we need to have those columns and rows actually work. Add these properties to the .row and .column, .columns declarations:
.row { width: 1000px; max-width: 100%; min-width: 768px; margin: 0 auto; }
.column, .columns { float: left; min-height: 1px; padding: 0 15px; position: relative; }
Okay, let’s explain what’s going on here:
- Row width: On the row element, we’ve set the row to be 1,000px wide (our base page width). However, we’ve also said the max-width is 100%, meaning that as the browser scales down (for example, on an iPad or iPhone) the row cannot exceed the width of the window. Our min-width will ensure rows don’t get comically small in IE8, which doesn’t support media queries.
- Row margin: By setting margin: 0 auto we have centered our rows on the page.
- Column positioning: Our columns are all floated left, right now, so they’ll stack up against each other.
- Column min-height: The min-height property makes sure that empty column still impact the float order.
- Column padding: This padding property is actually our column gutter. By setting it to 15px, we’re putting 15px on the left and right of each column, creating 30px gutters between columns.
This is obviously only part of what we need for our grid to work. Obviously, we need to declare the width of our columns. Since we have a 12 column grid, those widths are the number of columns wide divided into 12, as a percentage of 100. For example: 6 / 12 = 0.5, 50%. Our widths look like this:
.row .one { width: 8.33% }
.row .two { width: 16.66% }
.row .three { width: 25% }
.row .four { width: 33.33% }
.row .five { width: 41.66% }
.row .six { width: 50% }
.row .seven { width: 58.33% }
.row .eight { width: 66.66% }
.row .nine { width: 75% }
.row .ten { width: 83.33% }
.row .eleven { width: 91.66% }
.row .twelve { width: 100% }
Our columns all have widths representative of the size defined by their column count. What you may be thinking is, “if we make columns sized simply based on their division out of 100, won’t the padding we put on them make them wider and screw up our grid?” Normally, yes. Let’s look at a critical piece of this that we haven’t yet, and that’s a simple declaration you’ll make for every element on the page:
* { border-box; box-sizing: border-box; }
This little property does something both awesome and ironic for us. What it says is that elements on the page should not append padding and borders to the width of an object, but should consider them part of the width. Thus an object with 50% width and no padding will be the same width as one with 50% width and 15px of inside padding. Pretty cool right?
Yes. It is cool. It’s also the IE6 box model. Ironic, right? All this time, and all this crapping on IE6 (and rightly so) and in the end IE6 actually handled the box model in a much more sane way than any subsequent browser. Thank the W3C for that one. Without this CSS property, or without the IE6 box model, it’s impossible to create an object that’s 50% width, but with 15px of padding. (Note: not entirely true. But support for calc() in CSS is basically non-existent right now.)
Now, to get this to really work we need some vendor prefixed properties as well (iOS 4 doesn’t support it without -webkit, and Firefox needs -moz). The full declaration looks like this:
* { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; }
If you want to dive in further on border-box, Paul Irish wrote up a great post on the benefits of using border-box on everything.
Let’s recap before we dive into some of the grittier pieces. Our grid, right now, should look like this:
* { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; }
.row { width: 1000px; max-width: 100%; min-width: 768px; margin: 0 auto; }
.column, .columns { float: left; min-height: 1px; padding: 0 15px; position: relative; }
.row .one { width: 8.33% }
.row .two { width: 16.66% }
.row .three { width: 25% }
.row .four { width: 33.33% }
.row .five { width: 41.66% }
.row .six { width: 50% }
.row .seven { width: 58.33% }
.row .eight { width: 66.66% }
.row .nine { width: 75% }
.row .ten { width: 83.33% }
.row .eleven { width: 91.66% }
.row .twelve { width: 100% }
Making progress, but it’s not going to work just yet. First things first, let’s configure our grid to support nesting. Since we’re using percentages, this is dead simple. Any nested row will have some specific overrides, and otherwise will just … work. Add this line after the .row declaration:
.row .row { width: auto; max-width: none; min-width: 0; margin: 0 -15px; }
The only tricky thing happening here is that we’re setting a negative left and right margin on the row. We do this because the container row has padding on it, and so do the nested columns – but we don’t want to double up on that paddingor our nested rows will all be indented. The negative margin and width: auto means the row will expand to the edges of the container row, outside the padding of the containing column.
Remarkably, that’s all we need to support nesting. We can now have syntax in the HTML that looks like this:
<div class="row">
<div class="four columns">
<div class="row">
<div class="three columns">
...
</div>
<div class="nine columns">
...
</div>
</div>
</div>
<div class="four columns">
...
</div>
<div class="four columns">
...
</div>
</div>
That simple change opens up a huge range of layout options for us. Sadly, our grid is still broke as hell without a few more changes. Next, let’s make sure our rows actually stack correctly despite only containing floated elements (which would normally mean the row has no height). We don’t want to use overflow: hidden on the .row, just in case we need things like box-shadows to come out. Instead, use this:
/* Nicolas Gallagher's micro clearfix */
.row:before, .row:after, .clearfix:before, .clearfix:after { content:""; display:table; }
.row:after, .clearfix:after { clear: both; }
.row, .clearfix { zoom: 1; }
As you can see, props to Nicolas Gallagher for this snippet of code. This ensures that our rows get a real height, and that rows stacked on each other won’t overlap their columns or do anything else unfortunate. It relies on the behaviour of table displays, as well as on content: after to provide something that clears out our float with each row.
With this in place, our grid is actually working. We can create rows and columns and fill them with arbitrary content – even other rows. One last thing, a nicety that helps our grid look really sweet. Place these lines right after the .column, .columns declaration:
[class*="column"] + [class*="column"]:last-child { float: right; }
[class*="column"] + [class*="column"].end { float: left; }
If you’ve not used selectors like this before, this might look absurd. Let’s break it down:
- [class*=”column”] – This is a little seen selector that targets the value of an HTML attribute. In this case, the “class” attribute. Basically any HTML element with a class that contains “column” will be matched here. You have to be very careful with these types of selectors, because they’ll also match a class of “sidebar-column” or “sidebarcolumn”.
- [class*=”column”] + [class*=”column”] – This sibling selector means we only want to target columns that come after another column. This basically helps us omit single columns, for instance a single centered column or a twelve column block.
- [class*=”column”] + [class*=”column”]:last-child – Finally, this means we only want the last column that comes after another column. Otherwise we’d get every column except the first in a row.
- float: right – This helps us resolve a problem with percentage width grids: browsers don’t round them the same way. Without this, you’ll notice that rows don’t always end in quite the same place. If a width of 66.66% means 666.6 pixels, some browsers will floor that value every time (666px only, Chrome), some will swap floor and ceiling so that one is rounded up, the next down, etc. (Firefox) and some will even floor the percentage first. Floating the last column right means that, while the gutter may be a pixel or two larger, the right hand edge will always be the same.
- [class*=”column”] + [class*=”column”].end – You actually may not want the last column to go right every time. If you create a row with only two .four.column blocks with deliberate empty space to the right, you can add a class of .end to make sure it doesn’t float over. This is usually less likely, hence it being the one you have to opt in to.
Whew! That’s a weighty explanation for a couple lines, but they’re cool CSS selectors and worth a little bit of breakdown. And now we’re done (for the moment). Let’s recap again. Here’s what our grid should look like:
* { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; }
.row { width: 1000px; max-width: 100%; min-width: 768px; margin: 0 auto; }
.row .row { width: auto; max-width: none; min-width: 0; margin: 0 -15px; }
.column, .columns { float: left; min-height: 1px; padding: 0 15px; position: relative; }
[class*="column"] + [class*="column"]:last-child { float: right; }
[class*="column"] + [class*="column"].end { float: left; }
.row .one { width: 8.33% }
.row .two { width: 16.66% }
.row .three { width: 25% }
.row .four { width: 33.33% }
.row .five { width: 41.66% }
.row .six { width: 50% }
.row .seven { width: 58.33% }
.row .eight { width: 66.66% }
.row .nine { width: 75% }
.row .ten { width: 83.33% }
.row .eleven { width: 91.66% }
.row .twelve { width: 100% }
/* Nicolas Gallagher's micro clearfix */
.row:before, .row:after, .clearfix:before, .clearfix:after { content:""; display:table; }
.row:after, .clearfix:after { clear: both; }
.row, .clearfix { zoom: 1; }
Does this meet our criteria for a modern grid? Let’s check:
- Flexibility: Yup, we have enough columns and nesting to get the job done.
- Nesting: Check, see above.
- Responsive: Not yet. Hmmm. Let’s do that.
04. Making it responsive
For this tutorial, we’re going to look at the bare minimum of making this responsive: linearising the grid on small devices and making every column stack on top of the preceding one. We’ll use a CSS3 media query for this, which is supported in all of our targets except IE8:
@media only screen and (max-width: 767px) {
...
}
We’ll match our small device media query to smaller than an iPad (which reports its width as 768px, in any orientation, on regular or retina displays). Inside that media query we need to do some overriding:
.row { width: auto; min-width: 0; margin-left: 0; margin-right: 0; }
.column, .columns { width: auto !important; float: none; }
.column:last-child, .columns:last-child { float: none; }
[class*="column"] + [class*="column"]:last-child { float: none; }
.column:before, .columns:before, .column:after, .columns:after { content:""; display:table; }
.column:after, .columns:after { clear: both; }
There’s not much here we need to explain in too much detail: we’re basically telling the browser that, on a small display, don’t float the columns, don’t give them a min-width, don’t give them margins. We rely on the same padding as above to create a gap between the content and the edge of the screen, and we clear out the float on each column as we go for good measure. It stacks our grid, and our % based design takes care of the rest for us.
Okay, now how are we doing on the checklist:
- Flexibility: Still good.
- Nesting: Rock ‘n roll.
- Responsive: Yup. There’s more we could do (and in the Foundation 3 grid, on Github, we do) but this gets the job done for now.
- Modifiable: Yes, easily. By changing the padding on columns, and the negative margins on rows, we can modify our gutter size with two values. If we want a 24 column grid it’s simple math to divide 1, 2, 3 into 24 instead of twelve. Sweet.
- Backwards compatible: Yes. box-sizing actually works as far back as IE8 (go figure). There’s a behaviour file you can use to enable the box-sizing property in IE7, but it has some serious issues with auto widths. Hopefully that will be resolved soon – if it is, our grid is ready to go in IE7.
And that’s jenga. Go forth and create sweet layouts.
05. Hang on. This isn’t semantic
Yeah, it’s not. You got us. Here’s the thing: you can create a semantic (clean separation of data and display) grid, like semantic.gs, but it requires a preprocessor like Sass. Beyond the need for another tool (and really, Sass and tools like it are pretty cool) the output CSS of a semantic grid is, quite frankly, insane. You end up with either comma-delimited selectors that go way beyond the ability to read and modify them, or with properties repeated over and over and over and over. Semantic is awesome, really, and in the future this will start to become important, but we’re not there now. Building a better site for customers means building quickly and iterating, and you can build faster by not worrying about this (too much).
06. Next steps
A robust, complete grid system needs things like offsets, source ordering, N-object grids like a three-up block grid, and more. You can check out the full version of this grid (and see it evolve as needed) on Github. Since Foundation is open source, play around with this grid, check out the full one, and then help us make it even better. Our industry is evolving every day, and the tools should too.
Jonathan has been a huge geek for as long as he can remember, dating back to playing that old online maze game on Prodigy through to a career in digital media design. He worked in financial services software in Alabama for a little while before coming way out to the west coast for ZURB: www.zurb.com
Liked this? Read these!
- How to build an app
- Free graphic design software available to you right now!
- Brilliant Wordpress tutorial selection
- Our favourite web fonts - and they don't cost a penny
Thank you for reading 5 articles this month* Join now for unlimited access
Enjoy your first month for just £1 / $1 / €1
*Read 5 free articles per month without a subscription
Join now for unlimited access
Try first month for just £1 / $1 / €1
The Creative Bloq team is made up of a group of design fans, and has changed and evolved since Creative Bloq began back in 2012. The current website team consists of eight full-time members of staff: Editor Georgia Coggan, Deputy Editor Rosie Hilder, Ecommerce Editor Beren Neale, Senior News Editor Daniel Piper, Editor, Digital Art and 3D Ian Dean, Tech Reviews Editor Erlingur Einarsson and Ecommerce Writer Beth Nicholls and Staff Writer Natalie Fear, as well as a roster of freelancers from around the world. The 3D World and ImagineFX magazine teams also pitch in, ensuring that content from 3D World and ImagineFX is represented on Creative Bloq.