Lucid: A smarter CSS grid for Compass

Philosophy

CSS grids make web development easier... except when they don't. Wrapping <div>s, .alpha .omega madness, and fighting with gutters to get layouts aligned... These are the compromises that developers are forced live with.

Lucid is an effort to make CSS grids sane again by taking full advantage of SASS and Compass.

The Basics

  • Configure grid dimensions / columns instantly through variables
  • Fixed width (px based) for finer control
  • Tested in IE6+, Chrome, FF

The Specifics

  • Add borders and padding directly to your grid elements without using nested <div>s or ugly overrides
  • Use "gutterless" grid elements for nesting and precise styling
  • Create new rows without wrapping <div>s
  • Cater to multiple screen widths by initializing new grid dimensions within media queries
  • Achieve leaner compiled CSS than other SASS grids, due to Lucid's internal @extend logic

Documentation

Installation

(sudo) gem install compass-lucid-grid
compass help -r lucid lucid

Then

cd your_existing_project
echo "require 'lucid'" >> config.rb
compass install -r lucid lucid

Setup +grid-init

After installation, @import _grid.scss or copy its contents into your base file. Defaults (safe to remove) are shown below:

// Remove the following line if Compass has already been included
@import "compass/utilities/general/clearfix";

// Grid dimensions
$grid-width: 990px;
$grid-columns: 12;

// The amount of margin to apply to each side of a grid element
$grid-gutter: 15px;

// The distance between the grid container and the first grid element
$grid-outer-gutter: 15px;

// Use "pie-clearfix", "overflow", or "none"?
$grid-clearfix: "pie-clearfix";

// Center rows?
$grid-centering: true;

// Include Lucid
@import 'lucid';

// Output 4 CSS classes that Lucid uses as @extend "hooks"
@include grid-init;

Basic Layout +container, +columns( $columns (int) )

Now we're ready to style. All you need is a grid container and its child elements to start.

Note: Instead of applying repetative styles directly to each and every element, Lucid groups selectors that contain the same styles. This results in MUCH leaner compiled CSS.

/* SCSS */

.blue-box {
  @include container;     // container element to "contain" child elements
  background: blue;

  .sidebar {
    @include columns(4);  // 4 column element (1/3 row)
  }

  .main {
    @include columns(8);  // 8 column element (2/3 row)
  }

  .custom {
    @include columns(0);  // custom width element (includes only float and gutters)
    width: 123px;
  }
}

.red-box {
  @include container;     // multiple containers are fine and dandy
  background: red;
}


/* Compiled CSS */

.grid-clearfix:after,     // clearfix styles
.grid-container:after,
.blue-box:after,
.red-box:after {
  content: "";
  display: table;
  clear: both;
}

.grid-container,          // shared container styles
.blue-box,
.red-box {
  margin-left: auto;
  margin-right: auto;
  width: 990px;
}

.blue-box {               // unique container styles
  background: blue;
}

.red-box {
  background: red;
}

.grid-element,            // shared grid element styles
.blue-box .sidebar,
.blue-box .main,
.blue-box .custom {
  display: inline;
  float: left;
  margin-left: 15px;
  margin-right: 15px;
}

.blue-box .sidebar {      // 4 column width
  width: 300px; 
}

.blue-box .main {         // 8 column width
  width: 630px;
}

.blue-box .custom {       // custom width
  width: 123px;
}

Adjusting for Borders and Padding +columns( $columns (int), $adjustment (px) )

Grids typically don't play nice with borders and padding - these properties affect the width of elements, causing layouts to break. Using a wrapping <div> was often the cleanest method to accomodate styling.

With Lucid, this practice is no longer necessary - you can now adjust the width of grid elements individually. Just add your total borders / padding together and pass the negative value as a mixin parameter.

Note: Adjusting the width of a grid element makes it less suitable as a parent element for nesting (it can no longer contain the full expected width).

Old Way:

<div class="container">
  <div class="wrapper">
    <div class="safe-to-style">
      <!-- content -->
    </div>
  </div>
</div>
.wrapper {
  // grid float, width, styles
}

.safe-to-style {
  border: 1px solid #ccc;
  padding: 19px;
}

With Lucid:

<div class="container">
  <div class="look-ma-no-wrapper">
    <!-- content -->
  </div>
</div>
.look-ma-no-wrapper {
  @include columns(3, -40px);  // (1px + 19px) * 2

  border: 1px solid #ccc;
  padding: 19px;
}

Gutterless Elements +columns( $columns (int), $adjustment (px), $gutters (0 || none) )

Sometimes, it's convenient to have grid elements without gutter margins. This is especially useful for nesting elements or defining custom margins.

To turn off gutters, just pass a third parameter:

.gutterless {                     // a gutterless element
  @include columns(9, 0, none);   // $gutters can accept 'none' or '0'

  .nested {
    @include column(3);
  }

  .also-nested {
    @include column(6);
  }
}

Offset Positioning +offset( $offset (int), $gutters (0 || none) )

Layouts oftentimes call for a bit of whitespace. Not a problem with Lucid. Just remember to specify whether the element you're including to has gutters or not - this is factored into the calculation.

Unlike other grids that use padding or relative positioning to achieve this, Lucid does it with just margin-left. That means the element itself can still safely recieve background styling.

.offset-to-the-right {
  @include columns(6);
  @include offset(3);         // shifts element to the right 3 columns
}

.offset-to-the-left {
  @include columns(6);
  @include offset(-3);        // shifts element to the left 3 columns
}

.offset-gutterless {
  @include columns(6, 0, none);
  @include offset(3, none);   // include 'none' or '0' when grid element is gutterless
}

New Rows +row-break

Elements that exceed the width of the container will automatically wrap into the next row. If you want to insert a "row break" manually, it's dead simple:

.container {
  @include container;       // this 12 column container
                            // would normally accomodate all 4 children
  .on-row-1 {
    @include columns(3);
  }

  .also-on-row-1 {
    @include columns(3);
  }

  .on-row-2 {
    @include columns(3);    // this would have been on row 1
    @include row-break;     // +row-break puts it on row 2
  }

  .also-on-row-2 {
    @include columns(3);    // all following elements affected as well
  }
}

Advanced

Media Queries and Grid Reformatting +grid-reinit

Lucid uses pixels, which means that it's a fixed-width grid (percentages aren't precise enough). Fortunately, this doesn't mean that your sites have to be fixed-width.

"Reinitialize" Lucid inside a media query (or any other a wrapping container) to adjust the size of grid elements within the container. Just preface it with a new $grid-width and/or $grid-columns.

/* Full width grid */

@include grid-init;

.container { @include container; }
.sidebar { @include column(3); }
.main { @include column(9); }

/* Miniature grid within media query */

@media screen and (max-width: 480px) {

  // redeclare one or more variables
  $grid-width: 480px;
  $grid-columns: 6;

  @include grid-reinit;

  .container { @include container; }
  .sidebar { @include column(2); }
  .main { @include column(4); }

}

Modifying @extend Hooks

Lucid uses @extend internally to achieve leaner stylesheets. In order to do this, +grid-init outputs four benign selectors for @extend to "hook" onto. These selectors can be modified (defaults shown):

$grid-hook-clearfix: ".grid-clearfix";
$grid-hook-container: ".grid-container";
$grid-hook-element: ".grid-element";
$grid-hook-gutterless: ".grid-column-gutterless";

@include grid-init;

Unsemantic Class Names +grid-classes( $gutterless (bool), $prefix (str), $prefix-gutterless (str) )

For testing purposes, or for those who are unwilling to part with old ways, Lucid provides a class name generator mixin:

// where # is the number of columns

// outputs .g1 => .g#
@include grid-classes;

// outputs gutterless classes in addition (.gl1 => .gl#);
@include grid-classes(true);

// changes the class prefixes (outputs .grid-1 => .grid-# and .gutterless-1 => .gutterless-#)
@include grid-classes(true, 'grid-', 'gutterless-');

Your Turn

Love it? Hate it? Bugs? Suggestions?

I'd love to know. Thanks for stopping by!

-Y