Heygrady (there's a new blog)

I don't get It.

Compass Grid Plugin and a New Fluid Grid Option

Permalink

In a previous post on using Sass with the 1KB CSS Grid, I reviewed in depth the concept of using Sass and Compass to remove unnecessary classes from your HTML markup, and recommended usimg mixins instead. After using this grid on my own projects for the past year I've finally created a Ruby Gem called compass-grid-plugin and I've updated the GitHub repository to include a fluid grid option as well. For now I'm calling it “Grid” because that's what I've been calling it. The fluid grid will be documented below.

Introducing Grid

Although this grid plugin is inspired by the 1KB CSS Grid, it's not simply the Sass version of that grid. So I've opted to rename the grid to simply “Grid” and gobbled up the “compass-grid-plugin” gem name because some Austrian guy already has “compass-grid” (it's helpful that all of the other Compass grids use “-plugin” in the gem name also). That's the unimaginative name I'm going with until someone asks me to use a name less confusingly generic. I could maybe be convinced to call it “Pancakes” or “Simple.”

What does Grid do?

The point of Grid is to make grid math easier and make working with grids much more flexible. Old-school CSS-only grids require classes to be added to the markup and do not make working with the grid very easy. What happens when you need padding or a border on your column? What happens when the designer needs to cheat the grid in a few places? Compass Grid makes all of these situations much easier to handle because the measurements are not baked into the CSS and don't require hard-coded classes in the markup.

How is Grid different than other Compass grids?

There's a few other notable Compass grids. Of course Compass has always shipped with Blueprint pre-installed. There's also a port of 960.gs as well as a relatively new entrant, Susy. Someone has also ported Columnal to Compass. The original author of the 1KB CSS Grid has created The Semantic Grid System to take advantage of Sass/Less and remove the need for classes in the markup. All of these Compass-powered grids are great and you should use them! But they all are a little more complicated than “just a grid” (with the exception of The Semantic Grid System). At a glance, I think the main differences between most other grids and Grid are as follows:

  • Grid does not require Alpha and Omega. This is actually a big win for simplicity. Grid uses negative margins on the rows to accomplish the same thing.
  • Grid is designed specifically for cheating the grid using the $plus argument.
  • Grid is designed specifically to support row nesting.
  • Grid only does grid measurements and has no other capabilities.

Installing Grid

Install the Ruby Gem.

gem install compass-grid-plugin

Existing Compass Projects

You can include it in any existing Compass project by adding it to your config.rb file.

# Require any additional compass plugins here.
require 'compass-grid'

New Compass Projects

compass create my_project -r compass-grid -u compass-grid

You can install the grid plugin as part of a new Compass project.

Introducing Fluid Grid

While the original grid was pointedly not fluid, this new version does support fluid percentage-based grids. The original 1KB CSS Grid series by Tyler Tate pointed out that nesting fluid grids can be difficult. Very similar to Susy and Columnal, the fluid grid requires context in order to nest columns. This is because of the relative nature of percentages: 25% is a totally meaningless number by itself.

Basic HTML

Just like the previous article, we'll use a relatively simple 3-column layout for our page. The HTML below is a basic web page with a header, footer, and main area. For this example only the main area will contain columns. The .main-column will have two columns nested inside of it.

layout.htmlGist page
...
<div id="container">
<header>
<h1>Header</h1>
<nav><a href="#">Nav Link</a></nav>
</header>
<div role="main">
<div id="left-column">Left Column</div>
<div id="main-column">
<section class="hero">
Hero Space, I take the full width
</section>
<section>
<article id="content">Actual Content</article>
<aside id="right-column">Right Column</aside>
</section>
</div>
</div>
<footer>
<p>&copy; Copyright 2011</p>
</footer>
</div>
...

Using a Fixed Grid

Before diving into the fluid grid, it's important to see how it works with the static grid because the two are highly related. Below is the SCSS for a normal fixed grid similar to the example from the previous post]. Borders have been added to some of the elements to demonstrate how to use the grid correction features using the $plus argument available in all of the functions.

fixed.scssGist page
$grid-clearfix-class: false;
// import the grid
@import "grid";
// establishes page width and centers
#container {
@include grid-page;
}
// main page sections
[role="main"] {
@include grid-row(true); //true indicates a row is directly inside a page
}
// header and footer don't have columns in this example
header, footer {
margin: 0 $grid-gutter-width/2; // margins, like full-width columns
border: 1px solid black;
margin-bottom: $grid-gutter-width;
}
// side columns
#left-column, #right-column {
@include grid-column(3, -2px); //3 columns wide, adjust for border
border: 1px solid red;
}
// main column
#main-column {
@include grid-column(9); //9 columns wide
// sections in the main column are rows
> section {
@include grid-row;
margin-bottom: $grid-gutter-width;
}
// hero sections don't have columns in this example
> section.hero {
border: 1px solid black;
margin-left: 0; // remove the right and left margins because this isn't a row
margin-right: 0;
}
}
// center column
#content {
@include grid-column(6, -2px); //6 columns wide, adjust for border
border: 1px solid blue;
}
.clearfix { @include pie-clearfix; }

Notes:

  • As you can see, @include grid-column(6, -2px); subtracts 2px from the calculated column width to account for the borders on the column. A six-columns-wide column is normally 460px but using the $plus argument of -2px, it becomes 458px.
  • Although all section elements in the .main-column are set to be rows, the section.hero doesn't have any sub-columns in this example so the normal negative margins are removed.

Using a Fluid Grid

The fluid grid is set up similarly to the fixed grid, except the word “grid” is replaced by the word “fluid” in all of the mixins and functions. The fluid grid relies on the fixed grid for most of the width calculations and it also uses the same configuration options. The nested columns, #right-column and #content, require additional parameters to provide context because they are nested in the #main-column.

fluid.scssGist page
$grid-clearfix-class: false; // don't auto create the clearfix
// import the grid
@import "grid/fluid";
// establishes page width and centers
#container {
@include fluid-page;
}
// main page sections
[role="main"] {
@include fluid-row(true); // row is directly inside a page
}
// header and footer don't have columns in this example
header, footer {
@include fluid-gutters; // gutters, like full-width columns
border: 1px solid black;
margin-bottom: $grid-gutter-width;
}
// column rules so that float and border-box aren't repeated
.column {
@include fluid-column($with-gutters: false); // omit gutters
}
// side columns
#left-column, #right-column {
@extend .column;
border: 1px solid red;
}
#left-column {
@include fluid(3); //3 columns wide, don't adjust for border
@include fluid-gutters; // gutters
}
#right-column {
@include fluid(3, 0, 9); //3 columns wide, 9 column parent, don't adjust for border
@include fluid-gutters(9); // gutters in a 9 column parent
}
// main column
#main-column {
@extend .column;
@include fluid(9); //9 columns wide
@include fluid-gutters; // gutters
// sections in the main column are rows
> section {
@include fluid-row(false, 9);
margin-bottom: $grid-gutter-width;
}
// hero sections don't have columns in this example
> section.hero {
border: 1px solid black;
margin-left: 0; // remove the right and left margins because this isn't a row
margin-right: 0;
}
}
// center column
#content {
@extend .column;
@include fluid(6, 0, 9); //6 columns wide, don't adjust for border
@include fluid-gutters(9); // gutters
border: 1px solid blue;
}
// define our own clearfix
.clearfix { @include pie-clearfix; }

Notes:

  • Measurements that affect horizontal spacing, like width, margin-left/right and padding-left/right, should be provided in percentages as well to make sure everything scales well with the browser.
  • The fluid-gutters mixin returns left and right margins for a column.
    • The fluid-width function, combined with the grid-column-width function can be used to calculate margins as well.
    • Example: margin: 0 fluid-width($grid-gutter-width/2, grid-column-width(9, $grid-gutter-width));
    • Fluid gutters are different for rows and columns. Columns are relative to the parent row (a row is wider by the width of the gutter). Rows are relative to the parent column which is narrower than a row by the width of the gutter.
  • When calculating percentages on a child of a row, like .row > *, it's important to remember that a row's width is the width of its containing column plus the $grid-gutter-width.
    • A nine-columns-wide column: grid-column-width(9)
    • A nine-columns-wide row: grid-column-width(9, $grid-gutter-width)
  • There's no need to account for border or padding because all rows and columns have box-sizing: border-box applied. You can read more about box-sizing on MDN.
  • Chrome/Safari/Opera, Firefox/IE8/IE9 and IE6/IE7 all disagree on how to handle fractional pixels.
    • Firefox and IE8/9 re-balance the columns so that some columns may be 1px too wide but the total of the columns is exact.
    • Chrome/Safari/Opera simply round down, resulting in all columns being 1px too narrow. This gets really messy when working with deeply nested columns.
    • IE6/7 rounds up, making all of the columns too wide and causing one to wrap.
  • For now the fluid grid is designed for IE8 and above. Adding fixes for IE6 and IE7 may come later although there'll never be a way to bring in box-sizing support.

Generated CSS

For those that are curious, the CSS from the examples above looks like this.

Fixed Grid Generated CSS

fixed.cssGist page
#container {
width: 960px;
margin: 0 auto;
}
[role="main"] {
width: auto;
margin: 0 0;
}
header, footer {
margin: 0 10px;
border: 1px solid black;
margin-bottom: 20px;
}
#left-column, #right-column {
margin: 0 10px;
float: left;
width: 218px;
border: 1px solid red;
}
#main-column {
margin: 0 10px;
float: left;
width: 700px;
}
#main-column > section {
width: auto;
margin: 0 -10px;
margin-bottom: 20px;
}
#main-column > section.hero {
border: 1px solid black;
margin-left: 0;
margin-right: 0;
}
#content {
margin: 0 10px;
float: left;
width: 458px;
border: 1px solid blue;
}
.clearfix, #container, [role="main"], #main-column > section {
*zoom: 1;
}
.clearfix:after, #container:after, [role="main"]:after, #main-column > section:after {
content: "";
display: table;
clear: both;
}

Fluid Grid Generated CSS

fluid.cssGist page
#container {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
max-width: 960px;
width: 100%;
margin: 0 auto;
}
[role="main"] {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
width: auto;
margin: 0 0;
}
header, footer {
margin: 0 1.0416667%;
border: 1px solid black;
margin-bottom: 20px;
}
.column, #left-column, #right-column, #main-column, #content {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
float: left;
}
#left-column, #right-column {
border: 1px solid red;
}
#left-column {
width: 22.9166667%;
margin: 0 1.0416667%;
}
#right-column {
width: 30.5555556%;
margin: 0 1.3888889%;
}
#main-column {
width: 72.9166667%;
margin: 0 1.0416667%;
}
#main-column > section {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
width: auto;
margin: 0 -1.4285714%;
margin-bottom: 20px;
}
#main-column > section.hero {
border: 1px solid black;
margin-left: 0;
margin-right: 0;
}
#content {
width: 63.8888889%;
margin: 0 1.3888889%;
border: 1px solid blue;
}
.clearfix, #container, [role="main"], #main-column > section {
*zoom: 1;
}
.clearfix:after, #container:after, [role="main"]:after, #main-column > section:after {
content: "";
display: table;
clear: both;
}

Comments