Rolling Your Own Responsive Grid System With LESS

We have been studying Responsive Web Design and the mobile first mentality as the statistics on mobile usage are crazy! Hundreds of devices now access the web, from the traditional desktops and laptops to smartphones, tablets, game systems and TVs. This poses interesting challenges for designers as the viewport sizes of these devices range dramatically.

One solution is to have one site adapt itself to the various viewports using the power of CSS3 media queries placed atop of a flexible grid system. Conceptually this is not too complicated, but implementation can be fairly tricky, in both the design phase and writing the actual code. There are many existing open source grid systems in the wild, but to really understand how they work we wanted to get our hands dirty and create our own.

This article aims to dissect our experience and provide a toolkit that will help enable you to do the same. It does not provide a step by step tutorial due to prerequisites involved and feeling light-headed from being in Day 6 of The Master Cleanse detox!

Let's start by outlining the goals of our grid system, dubbed The Organic Grid.

Our Goals

  • Use the LESS CSS preprocessor to handle math
  • Two grid modes, either fixed or fluid
  • Easily count columns to tailor the grid as opposed to thinking in pixels and percentages
  • Variable number of columns
  • Variable column width
  • Variable column margin
  • Offset (push and pull) columns
  • Easily integrate CSS3 media queries to manipulate the grid for different device viewports
  • Quickly generate base classes for non-semantic HTML (traditional usage)
  • Use LESS to generate project specific styles for semantic HTML or for less common circumstances such as nesting
  • Avoid the requirement of special classes that nudge the first and last columns into alignment
  • Keep the system light and modular to be used as a stand alone or integrated into a workflow or framework

Building The Organic Grid

We start by declaring a handful of variables that specify the grid structure. The variables are named intuitively to indicate the value they should contain.

  • @colCount is the total number of columns in the grid
  • @colWidth is the width of each column without the margin (gutter)
  • @colMargin is the margin of the each column, half is applied to each side of a given column
  • @colCombo is the sum of the column width and margin and is not necessary per se, but simplifies the mixins by reducing the parentheses and operators
  • @gridWidth is the total width of the grid as specified in the above variables. This variable is also not a requirement, but improves readability of the mixins

Now let's assign the variables some typical values that create the common 960 pixel grid. We'll use this a target to measure our development against, particularly to verify the LESS mixin calculations.

@colCount:  12;
@colWidth:  60;
@colMargin: 20;
@colCombo:  @colWidth + @colMargin;
@gridWidth: @colCount * @colCombo;

This value of @gridWidth at this point is 12 * (60 + 20) = 960. Although the units are in pixels, they are not specified (yet), as one requirement of this particular design is to function in both a fixed width pixel mode and a percentage mode.

Like most grids, The Organic Grid consists of a series of rows and columns. All columns belong to a row, that is to say, every column is contained by a row. The width summation of all the columns in a given row must equal the row width that contains them (except when using offsets). All this really entails is counting columns.

For example a row with a width of @colCount or 12 must contain some combination of columns that add to 12, such as, an 8 width column and a 4 width column (the Golden Ratio).

This is important, all rows are a certain width (measured in columns) and the width of the columns contained need to sum up to match the row width.

With the variables in place we are ready to develop our mixins that actually use the variable values. It is worth mentioning here that mixins, like creating functions in other languages, can be defined with parameters and these parameters may contain default values. Also, be aware of the variable's scope (local vs global).

We will create two versions of each mixin, one for fixed width pixel mode and the other for fluid percentage based mode. We considered creating one version of each mixin with an argument that would accept a unit of measurement (px or %), but the code becomes more difficult to understand and troubleshoot. So even though this may violate DRY principles, it made life easier!

We will detail the basics of the fixed width methodology here that has been slightly modified to ease the learning curve. Then we will followup with a short summary how logic behind the fluid width mixins.

.doRow()
{
    margin: 0 (1px * -0.5 * @colMargin);
}

.doCol(@span)
{
    width: 1px * ((@span * @colCombo) - @colMargin);
    margin: 0 (1px * 0.5 * @colMargin)
}

.doOffset(@span)
{
    margin-left: 1px * ((@span * @colCombo) + (0.5 * @colMargin));
}

The .doRow() mixin sets a negative margin on each row that shifts itself (left) by half of the @colMargin value. This technique is used to avoid targeting first and last columns and shifting them into alignment. The code is straight forward.

Next, the .doCol() mixin does two things. First it sets the width of each column as determined by the @span passed as a parameter. So .doCol(6) would create a column spanning 6 the equivalent of 460px as determined by 6 * (60 + 20) which totals 480, then subtracting 20.

This mixin then sets the column margin on each side of it, which in this case would yield the equivalent of 10px on the left and 10px on the right as determined by @colMargin global variable being divided by two.

The final .doOffset() mixin to complete the fixed width system creates whitespace or reverses element order for SEO by giving a column more or less left-margin. The offset also takes a @span parameter, but this time @span expects either a positive or negative integer to indicate how much and in what direction the column should move. So in using our current example, .doOffset(3) would push a column the equivalent of 250px to the right. This is determined by (3 * (60 + 20)) + (20 / 2). A negative value passed will pull the column to the left. Note, in either case, this affects any other sibling columns it in the document flow.

The fluid grid needs to take the pixel widths and convert them to percentages. It does this by Ethan Marcotte's simply phrased formula of target divided by context equals result.

The concept is the same as the above, except the parent container's width (in terms of number of columns) needs to be supplied to the mixins. Much of the time this will be 12, the value of @colCount in our example. Instead of providing detail let's summarize by saying the main difference in the math is the supplied @parentSpan is used in place of the @colCount and each mixin name is appended the word Fluid. To put this process into words is probably more difficult than looking at the code.

.doRowFluid(@parentSpan: @colCount)
{
    margin: 0 (-50% * @colMargin) / (@parentSpan * @colCombo);
}

.doColFluid(@span, @parentSpan: @colCount)
{
    width: 100% * ((@span * @colCombo) - @colMargin) / (@parentSpan * @colCombo);
    margin: 0 (50% * @colMargin) / (@parentSpan * @colCombo);
}

.doOffsetFluid(@span, @parentSpan)
{
    margin-left: 100% * ((@span * @colCombo) + (0.5 * @colMargin)) / (@parentSpan * @colCombo);
}

Nesting the grid within a grid simply consists of the same process of creating any ordinary row of columns, except the base reference of @colCount (12 columns in our example) becomes an ancestor, not a parent, of the nested rows and columns. Therefore, you need to pass its parent's column count as the @parentSpan in the .doRowFluid() mixin. As before explained, the nested columns width summation will need to match its parent's width.

Nesting is by far the most complex part of this process, but with a bit of practice you should be able to wrap your head around it.

The Organic Grid is an infant and yet to be battle tested, but serves as a starting point. There are browser rounding issues which will impact your design regardless of any percentage-based or sub-pixel grid system (as far as we know) because there is not a rounding standard applied by browser vendors. Opera suffers the worst of those we tested, but version 12 (alpha) greatly improves this.

Well this concludes the write-up and hopefully you find the approach useful and learned a thing or two. The code is still being polished, but we wanted to put my collection of thoughts and notes together into something useful that may help others. Let us know if you use The Organic Grid in any of your projects or have taken it apart and modified it!

If nothing else check out LESS and CodeKit (Mac application) as they have drastically altered our workflow.

We may write a followup with details as to how to use The Organic Grid specifically. An RSS feed is setup if you would like to be notified when future news is released.

Respect And Resources

  • Thanks to Jussi Jokinen for feedback and ideas presented in the Centage! framework
  • Joshua Johnson of Design Shack has a great step by step tutorial to create a fixed width with LESS
  • The amazing publications from A Book Apart that succinctly explain modern web design in exciting ways!
  • Tyler Tate's work, The 1KB CSS Grid, is an excellent light weight grid with clever use of negative margin on the parent to align children. The Semantic Grid system, which as the name suggests, keeps purists happy by moving the presentational classes from HTML to CSS (LESS), which creates semantic and tidy HTML. If you decide to skip all this complexity and give the middle finger to the future, we suggest running with The 1KB CSS Grid.
  • Nathan Smith's Old faithful, The 960 Grid System, deserves a shout out, especially for providing the Photoshop, OmniGraffle and many other various templates. It has been our standard for sometime.
  • Knight Digital Media Center has an excellent tutorial to create a grid in Photoshop for pre-production. Once your grid is created you can then save a cropped 1px version to be used as a background image that repeats on the y axis to underlay the grid elements and assist in alignment during development.
  • Also, we would not have attempted any of this without the Alexis Sellier's LESS extension of CSS and Bryan Jones's LESS.app (Mac) and its successor CodeKit (which compiles the LESS into CSS, among other useful things, including updating your CSS files and browser when a change to less is made for more realtime debugging). The LESS.app is free, but we recommend getting CodeKit instead which comes with small price tag. It will help out the developer to keep the both apps running as smoothly as they currently do!
  • A nice finishing touch is David Cochran's CSS3 Media Query Reporter, which appends the "pixel value range" of the browser's current size (specified in your media queries) to the bottom of the page. This is a pretty sweet tool to identify when a breakpoint has been triggered.
  • The folks at Twitter Bootstrap have made the code available to you and it is very impressive! We really liked the application of a bootstrap file in their architecture. This bootstrap file (commonly used in the Model, View, Controller design pattern) funnels action through one file and can be adopted into your site's CSS setup.
    Use LESS to import other LESS files before compiling them into CSS file. This may have an advantage over using purely CSS because a each import requires a resource request from the server. However, because LESS is compiled this process happens before the CSS file is generated, thereby saving requests which again should improve site performance. But someone will have to double check that!
  • Chris Coyier's snippet summarizing Media Query for Standard Devices.
  • A table summaring media query browser support is found at When Can I Use and using something like Modernizr helps older browsers handle newer features.
  • Of course a hat tip to Ethan Marcotte for helping pave our digital future!

These ideas are certainly not all original, we owe a lot to the great community out there sharing their work and ideas! Please see the code in the downloadable file for examples and checkout the live demo to view The Organic Grid in action!

Go Kings!