How do CSS grids work?
15 Jul 2012
When I started with a new web page I, like (probably?) most of us, didn't start from the very beginning. Why? Because there is a lot of ceremony involved in setting up the basic structure of a web app. We need a navigation. A content area. A header. A footer. And we probably arrange them in a grid. And of course we need to apply standard styling to buttons, forms, tables, and typography. Luckily, we have CSS frameworks that take out most of the guess-work and lay a good foundation for us. While it works, I always wondered how to these grids work? Is it magic?
It' (not) magic!
Let me give you an example. In
Twitter's Bootstrap we can arrange content with a few divs:
<div class="row">
<div class="offset2 span4">
I'm offset two columns and four columns wide
</div>
<div class="span4">
I'm also four columns wide
</div>
</div>
<div class="row">
<div class="offset2 span8">
And I'm on a new row!
</div>
</div>
Other frameworks use similar syntax. So... how do they work?
Bootstrap, 960 Grid System
There are a few CSS frameworks around, but I will compare Twitter's Bootstrap and
960 Grid System. I already showed a basic example with Bootstrap. Now let's compare it with 960.gs:
<div class="grid_4 prefix_2">
I'm offset two columns and four columns wide
</div>
<div class="grid_4">
I'm also four columns wide
</div>
<div class="clear"></div>
<div class="grid_8 prefix_2">
And I'm on a new row!
</div>
It works similar to Bootstrap, in that we define the length with
"grid_4" compared to
"span4". Also we can define the offsets with
"prefix_2" (
"offset2" in Bootstrap). But there is a difference how we define a new row. Whereas Bootstrap uses an outer
div with class
"row", 960.gs relies on a clearfix
div on the same level.
Here I like Bootstraps approach better, because it is easier to walk through the HTML when I can collapse a whole row and don't have to search on possibly hundreds of divs on the same level to find the cell in the middle of the document.
How these grids actually work
Thanks for all the syntax, but I still didn't talk about how the magic happens. Let's look at Bootstrap first.
Bootstrap SPAN
If we take a
look at bootstraps source code, we can find a file called
grid.less*. The only thing this file does is call the #grid core mixin. There is also an import for the responsive version of the grid, but we will ignore that one for this walk-through.
// Fixed (940px)
#grid > .core(@gridColumnWidth, @gridGutterWidth);
(See also
on Github)
We can find the core mixin under, surprise, surprise,
mixins.less. Bootstrap defines a mixin called
.spanX (@index). It recursively creates all the
.span12 .span11 etc. classes in the compiled CSS.
.spanX (@index) when (@index > 0) {
(~".span@{index}") { .span(@index); }
.spanX(@index - 1);
}
.spanX (0) {}
(
On Github)
The line
(~".span@{index}") { .span(@index); } uses
Javascript Evaluation to define the class. The rules will be the LESS mixin
.span (line 2), so lets look at this next.
.span (@columns) {
width: (@gridColumnWidth * @columns) + (@gridGutterWidth * (@columns - 1));
}
(
On Github)
This sets the width of the
.spanX to the width of the columns plus the gutter in between the columns it spans. But wait! That can't be all! We can define multiple spans in a single row! So we have some more CSS!
[class*="span"] {
float: left;
margin-left: @gridGutterWidth;
}
(
Github)
All classes that start with
.span will be floated to the left. Additionally, the gutter is applied as margin on the left, so the columns have a bit distance between each other.
So that about covers the
.span. What about
.offset?
OFFSETing Bootstrap
Offsetting works roughly the same as spanning the grid elements:
.offsetX (@index) when (@index > 0) {
(~".offset@{index}") { .offset(@index); }
.offsetX(@index - 1);
}
.offsetX (0) {}
.offset (@columns) {
margin-left: (@gridColumnWidth * @columns) + (@gridGutterWidth * (@columns + 1));
}
(
Github)
Again, we have the
.offsetX-mixin that creates the possible offsets. This will load the
.offset mixin (note the missing 'X'), which calculates the, well, offsets (highlighted lines).
All the classes I used in the example except
.row have been explained. Let's look at the rows.
Bootstrapping rows
This one will be easy:
.row {
margin-left: @gridGutterWidth * -1;
.clearfix();
}
(
Github)
It adds a margin to each row and then loads the
.clearfix mixin. The clearfix code requires some trickery since clearfix and browsers tend to hate each other (
as described in the comment).
.clearfix {
*zoom: 1;
&:before,
&:after {
display: table;
content: "";
}
&:after {
clear: both;
}
}
(
Github)
The Bootstrap Conclusion
So how does bootstrap generate the grid? Four steps are needed:
- Create all possible .spanX classes, where the length is calculated based on column width and gutter. Also float them to the left.
- Create all possible .offset classes with the length calculated from column width and gutter.
- Create a .row class that acts as a clearfix.
And now we have a grid system!
So what about 960.gs?
I didn't forget! So let's do a check if they use the same system.
Go to the 960.gs demo page and see for yourself.
.grid_2 is defined as
width: 140px and all grid classes are floated to the left and have a gutter applied. The only difference is that 960.gs seems to apply the gutter to left and right, whereas Bootstrap just applies it to the left margin. The
"clear" class is a simple clearfix (who would have thought?). The
.prefix_X classes have a
.padding applied to the left. Again a small difference, 960.gs applies a padding, but Bootstrap relies on margin. So which one is right? ;)
So there you have it
Both systems work roughly the same, with small differences. Both use floated cells with a clearfix to make rows. Both define a cell length and a cell offset. One uses margin, the other padding.
Did I miss something interesting? And are there any CSS frameworks out there which use a different system?
* Which means Bootstrap is written with
LESS, a CSS preprocessor like SASS.