A pragmatic, responsive, flexible, liquid CSS grid system using Compass, SASS, and Modernizr.
We needed a css grid framework to support our newer mobile-friendly projects. After reviewing a whole bunch of excellent solutions, we ended up creating our own.
The goal of this framework is to allow for flexible CSS grids that:
- Support the CSS Flexible Box Layout Module if available
- Fall back to normal, static grid widths if not
- Can choose not to use flexbox if desired
- Support media queries for different grid layouts, if available
- Support subgrids
- Maintain consistent gutter widths, even in subgrids
- Work with Compass and SASS, our CSS preprocessor of choice
- Work with CSS3 PIE
We realized that we not only needed to support flexbox on modern browsers, but also needed a fallback for non–flexbox-friendly browsers like IE. We also needed a solution that would display something more reasonable than the bare minimum to browsers that don’t support media queries.
Some of the decisions we made were to:
- build on the great work at 320 and up for media query support and basic mobile philosophy
- use a negative left margin apporach to the non-flexbox grid for better legacy browser and PIE support
- leverage Compass and SASS as much as possible
- compromise and use a “row” function, so we could support better subgrids.
First, you need to use the SASS CSS preprocessor. You also need to use Compass, which is a collection of pre-made SASS mixins and helper functions. We also rely on Modernizr to help us figure out browser support for specific grid technologies.
We’re going to assume you already know how to work with Modernizr, SASS and Compass. If you don’t, you will want to familiarize yourself with those packages first.
Grass tries to use the flexible box model if it can. It relies on Modernizr to decide if your page can support this. So you need to include Modernizr in your document’s head. And if Modernizr decides your browser can’t handle the flexible box model, or you have JS turned off, Grass falls back to a fixed-width grid.
Let’s set up a simple, three-column layout. Here’s the HTML we’re going to add our grid to:
<html>
<head>
<title>Simple Three-Column Example</title>
<link rel="stylesheet" href="css/styles.css" />
<script src="javascript/modernizr-2.6.1.js"></script>
</head>
<body>
<div class="row">
<div class="left-column">
<p>Left Column</p>
</div>
<div class="middle-column">
<p>Middle Column</p>
</div>
<div class="right-column">
<p>Right Column</p>
</div>
</div>
</body>
</html>
Let’s assume we’re making a new SASS/Compass stylesheet. We’ll call it
styles.scss
.
Here’s our imaginary file structure:
example.html <- our HTML page
config.rb <- SASS config file
javascript/ <- where your javascripts live
modernizr-2.6.1.js <- Modernizr
css/ <- where SASS will put our CSS
styles.css <- generated by SASS
scss/ <- our SASS files
partials/ <- partials and includes
_grid.scss <- Grass grid system
styles.scss <- our custom styles
First you would import Compass’s CSS3 module, and our
_grid.scss
partial into your SASS project, where you want to start
using the grid framework.
@import "compass/css3";
@import "partials/grid";
Compass’s compass/css3
module makes sure that we have access to the
Compass mixins for the flexible box model.
Grass’s _grid.scss
is typically imported from your
partials
folder, usually as-is.
Now you need to define your grid. Let‘s set up a basic 24 column grid, with 30px-wide columns, and 10px of gutter between columns. That will give us a 950px-wide page.
$column-width: 30px;
$gutter-width: 10px;
$columns: 24;
If you don’t set these, Grass will use its own defaults:
$column-width: 60px !default;
$gutter-width: 20px !default;
$columns: 12 !default;
Now we can start applying the styling.
To accommodate browsers without the flexible box model, we have to
set up a “row” div for our columns. In our case, we’ll use the .row
div.
Next we set up the columns. If the row containing the columns spans all the grid system’s columns (in our case 24) then you can just tell each column how many grid units wide it needs to be.
.row {
@include row();
.left-column {
@include column(4);
}
.middle-column {
@include column(12);
}
.right-column {
@include column(6);
}
}
Tada! We’re done.
Grass can nest grids inside other grids. And when we do this, it will keep the gutter widths consistent, even when it’s flexible.
Here’s a new HTML page:
<html>
<head>
<title>Simple Nested Example</title>
<link rel="stylesheet" href="css/styles.css" />
<script src="javascript/modernizr-2.6.1.js"></script>
</head>
<body>
<div class="row">
<div class="left-column">
<p>Left Column</p>
</div>
<div class="middle-column">
<div class="middle-row">
<div class="middle-left">
<p>Middle Left</p>
</div>
<div class="middle-right">
<p>Middle Right</p>
</div>
</div>
</div>
<div class="right-column">
<p>Right Column</p>
</div>
</div>
</body>
</html>
Let’s assume we’re using the same grid definitions as before. We can nest another
row inside our .middle-column
div, and put two columns inside it. The only
complication is that we need to pass in the number of columns that our parent
row uses, so that Grass can calculate the widths and ratios correctly.
This gets passed in as a second parameter to the @column
mixin.
So for a 6-unit column inside a 12-unit row, you would use this:
@import column(6,12);
Here’s the new, nested SCSS.
.row {
@include row();
.left-column {
@include column(4);
}
.middle-column {
@include column(12);
.middle-row {
@include row();
.middle-left {
@column(6,12);
}
.middle-right {
@column(6,12);
}
}
}
.right-column {
@include column(6);
}
}
Sometimes you want one or more of your columns to stay a fixed width,
while the other columns can stretch. You can do that by passing true
as a third parameter to the column include to tell it not to allow that
column to flex. This means you have to pass all three parameters, even
if your row is full-width.
Using our first example, if we wanted the left and right columns to be fixed, we’d do this:
.row {
@include row();
.left-column {
@include column(4,24,true);
}
.middle-column {
@include column(12,24,true);
}
.right-column {
@include column(6,24,true);
}
}
You can redefine the grid variables at any time, or even pass them through to the various functions. As long as you’re careful with the specificity of your selectors, you can redefine your grid to your heart’s content. Our demo page is an example of that.
Sometimes you will need to turn off column or row settings to change
up your grid. You can do that by passing false
to either the row or
the column include.
@include row(false);
@include column(false);
You may not want your grid to be stretchy forever. You can put a cap on it with the “grid-width” include. Typically, you apply this to a div that contains your whole page, and it caps your page at the width of the current grid definitions.
So for this HTML:
<html>
<head>...</head>
<body>
<div class="page-area">
<div class="row">
<div class="left-column">
<p>Left Column</p>
</div>
<div class="right-column">
<p>Right Column</p>
</div>
</div>
</div>
</body>
</html>
You could set the max-width of the page up like this:
@import "compass/css3";
@import "partials/grid";
$column-width: 30px;
$gutter-width: 10px;
$columns: 24;
.page-area {
@include grid-width(true);
margin: 0 auto;
.row { ... }
}
This would make the .page-area
max out at the full grid width of
950px.
If you want to assign an arbitrary max-width that’s different from the calculated one, you can. The fall-back non-flexbox version will still use the normal calculated grid system width, however.
@include grid-width(1024px);
If you don’t pass in true
or a width, grid-width
will set the
width of the div to the calculated grid width for fallback browsers,
but set it to 100%
for flexbox-capable browsers.
@include grid-width();
Here is a breakdown of the includes and parameters that come with Grass.
/* Grass defaults */
$column-width: 60px !default; // single grid unit
$gutter-width: 20px !default; // space between units
$columns: 12 !default; // total grid units across
$use-flexbox: true !default; // if true, use flexbox if avaiable
$grid-debug: false !default; // if true, outputs simple debug info
// flexbox max-width set to 100%, fallback set to calculated grid width
@include grid-width();
// flexbox max-width and fallback set to calculated grid width
@include grid-width(true);
// flexbox max-width set to 1024px, fallback set to calculated grid width
@include grid-width(1024px);
@include row(); // establish row
@include row(false); // turn off row
@include column(3); // 3 unit column, full grid-width row
@include column(3,6); // 3 unit column inside a 6 unit row
@include column(3,6,true); // fixed width 3 unit column inside a 6 unit row
@include column(false); // turn off column settings
This function builds a bunch of pre-made column classes, like more traditional
grid systems. If you just call it without any parameters, it will produce a set
of classes prefixed with .cols-
for all the possible unit widths in your
grid settings.
Example:
$column-width: 60px;
$gutter-width: 20px;
$columns: 12;
@include column-classes();
Produces the following CSS classes you can use:
.cols-1 {...}
.cols-2 {...}
.cols-3 {...}
.cols-4 {...}
.cols-5 {...}
.cols-6 {...}
.cols-7 {...}
.cols-8 {...}
.cols-9 {...}
.cols-10 {...}
.cols-11 {...}
.cols-11 {...}
.cols-12 {...}