Creating Nestable Dynamic Grids

Learn how to create flexible and fluid grids which make nesting at arbitrary depths easy, while allowing full freedom for how the grid behaves in all viewports.

Dynamic Grids

With the rapid addition of responsive design and development to our workflows, since Ethan Marcotte’s A List Apart article in 2010, a myriad of tools have emerged to attempt to make our lives easier in developing responsive sites.

All these tools, however, can be difficult to filter through in order to find a robust solution for our projects.

In this article I will outline methods I have developed for creating flexible and fluid grids which allow for nesting at arbitrary depths, while allowing full freedom for how the grid behaves in all viewports.

This is all possible with a big help from the excellent grid generator Gridpak, put together by the folks over at Erskine.

What We’ll Be Able To Do

Once we’re done here, we’ll have a grid which can do some of the following:

and can be used to manage our layouts in the real world like so:

Click here to see a this example in a new tab.

Prerequisites

Before we begin with how to achieve such a grid, we should outline everything we expect of the grid.

These are the points that are important to me when using a functional grid system:

  • columns are accurate and predictable at all widths
  • styles and markup are simple, and easy to use
  • device agnostic, and fully fluid
  • nestable to any depth
  • modular with full control of columns in all viewports
  • should work without a polyfill in legacy browsers

With regards to the last point, many people are happy to use respond.js to provide media query support to legacy browsers.

Legacy browsers, however, don’t require support for any viewport other than desktop, since they’re only found on desktops and laptops. Below I’ll outline how to target those dinosaurs while saving us an http request!

Now that we know what we want… let’s make it!

Setup

HTML Tag And IE Fallbacks

To begin with, we need to ensure we can easily target legacy browsers with our styles. The following method is inspired by html5 boilerplate, and is explained by Paul Irish in his post on Conditional stylesheets vs css hacks? Answer: Neither!

<!doctype html>
<!--[if IE 8]>    <html class="no-js ie8 oldie" lang="en"> <![endif]-->
<!--[if IE 9]>    <html class="no-js ie9 oldie" lang="en"> <![endif]-->
<!--[if gt IE 9]><!--> <html class="no-js" lang="en"> <!--<![endif]-->

All this does is add a class to the html tag depending on which browser a visitor is coming to our site with. I’ll only be supporting from Internet Explorer 8 for the purpose of this article.

From there it’s easy enough to target legacy versions of Internet Explorer with the following:

/* target only IE8 with the following class */
.ie8 .my-class { ... }

Base Styles And Rems

rems, or root em‘s, are an awesome unit to start making use of in your styles, especially when it comes to font sizing. rems are great because they are relative units, but are relative to only one declaration. This means all the calculation nightmares introduced by nesting ems completely disappear!

rems make this possible by basing all calculations relative to the size of the font declared at the body level.

This can be illustrated as follows:

html {
  ...
  font-size: 100%;
  ...
}

body {
  ...
  /* base font size: 16px (user agent dependant, but usually 16px) */
  font-size: 1em;
  ...
}

...

.padding {
  /* IE8 fallback */
  padding: 32px;
  padding: 2rem; /* 2 * 16px = 32px */
}

IE8 does not support rems, so a px fallback is a small price to pay for the benefit that they provide.

I’ll show you a little later how this magical unit will make our lives a little easier when it comes to creating our grid.

Now that that’s out of the way, let’s get to the bones of our grid!

Tiny break: 📬 Want to stay up to date with frontend and trends in web design? Subscribe and get our Collective newsletter twice a tweek.

Getting Gridpak

Go to gridpak.com, and play around with the options so that you are comfortable with the features.

For the purpose of this article, I’m going to download a grid with the following settings, but feel free to experiment:

  • viewports: 480px, 640px, 960px and up
  • 12 columns in each viewport (yup, 12 in mobile, tablet, and desktop!)
  • 0 padding
  • 24px gutter. Gridpak allows for viewport specific gutters, but I’m going to keep things simple with a global gutter.

Try keep your padding and gutter values as whole numbers resulting from simple multiplications of the body font size. This will help reduce decimal values, which browsers handle differently, as well as keep your styles a little neater.

i.e. if base font size is 16px, gutter can be

  • 8px (16 * .5)
  • 12px (16 * .75)
  • 24px (16 * 1.5)
  • 32px (16 * 2)

NB: This should ultimately be decided on what best suits your design.

Once you are happy with your settings, download your grid!

Breaking Gridpak Down

One of the best features of Gridpak is that you’ll have an incredibly robust and accurate grid with just a few lines of CSS.

Gridpak’s strength comes from leveraging the awesome power of setting all your elements to box-sizing: border-box. With border-box we can have two 50% columns always sit on the same line, accomodating whatever borders we apply to them.

With some clever CSS we can use transparent borders for gutters, giving us wonderfully accurate and managable columns:

.row {
  /*
   * Rows contain columns
   *
   * Pull only the left hand side of our rows 24px left (our gutter width)
   * to allow for the border magic that happens in .col below
   */
  margin-left: -24px;
}

.col {
  /*
   * Create a gutter using a transparent border at the specified gutter width.
   * This pushes our content back the distance the row pulled us left, while
   * allowing border-box calculations to be used.
   */
  border: 0px solid rgba(0,0,0,0);
  border-left-width: 24px;

  float: left;

  /*
   * Include the border in the calculation of the column width
   */
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;

  /*
   * Ensure that backgrounds set on columns begin just inside the border, not underneath
   */
  -webkit-background-clip: padding-box !important;
  -moz-background-clip: padding-box !important;
  background-clip: padding-box !important;
}

On top of this, Gridpak puts grids for each viewport in their own media queries, and then restricts grid styles to each media query with upper bounds:

/*
 * Declare a grid between 0 and 479px only
 */
@media screen and (min-width: 0px) and (max-width: 479px) {

  ...

  .span_1 { width:8.33333333333%;}
  .span_2 { width:16.6666666667%;}
  .span_3 { width:25.0%;}
  .span_4 { width:33.3333333333%;}
  .span_5 { width:41.6666666667%;}
  .span_6 { width:50.0%;}
  .span_7 { width:58.3333333333%;}
  .span_8 { width:66.6666666667%;}
  .span_9 { width:75.0%;}
  .span_10 { width:83.3333333333%;}
  .span_11 { width:91.6666666667%;}
  .span_12 {
    margin-left:0;
    width:100%;
  }

}

/*
 * Declare a grid between 480px and 639px only
 */
@media screen and (min-width: 480px) and (max-width: 639px) {
  ...
}

/* etc. */
...

If we had to specify different numbers of columns for viewports when deciding on our Gridpak settings – say 2 columns for mobile, 4 columns for tablet, and 12 for desktop – these media query specific declarations would grant us more flexibility over our columns than the general, “Let’s give mobile and tablet 1 column for everything” solutions most prevalent in grid systems.

i.e. one class can make a column occupy the full width of a row, half the width, and 1/6th of the width depending on the active viewport.

This is pretty powerful stuff – but we’re still being held back a little! I want those columns to do exactly what I want in every viewport!

Bending Gridpak To Our Will

Using rems For Easier Maintainability

First of all, let’s take the foundation of Gridpak and give it a nice modern boost (with a fallback for the old browsers):

.row {
  margin-left: -24px;
  margin-left: -1.5rem;
}

/* Reusable column setup */
.col {
  border: 0px solid transparent;
  border-left-width: 24px;
  border-left-width: 1.5rem;
  float: left;
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
  -webkit-background-clip: padding-box !important;
  -moz-background-clip: padding-box !important;
  background-clip: padding-box !important;
}

In future, when we don’t support IE8, we can simply drop the px fallbacks.

rems are free variables, and by making use of Sass, we can easily manage the IE8 px declarations with their own variable for easier maintainability.

Zooming And Cascading Grids With Media Query Blocks

Secondly, we want to make sure that when users zoom into our sites that the layouts don’t break. Lyza Gardner put together a case study for using proportional units on media query declarations.

Basically, pixel-based media query declarations don’t play well with zooming, so we use ems instead.

Additionally, media queries use the user agent’s base font size when declaring viewport widths. This means that, generally, we will be using 16px as a basis for our width calculations.

While we’re at it… let’s get rid of those max-width media query restrictions so we can harness some cascade power:

/*
 * Declare a grid which will be effective from 0px
 */
@media screen and (min-width: 0) {
  ...
}

/*
 * Declare a grid which will be effective from 480px
 * 480 / 16 = 30em
 */
@media screen and (min-width: 30em) {
  ...
}

/*
 * Declare a grid which will be effective from 640px
 * 640 / 16 = 40em
 */
@media screen and (min-width: 40em) {
  ...
}

...

In addition to fixing zooming, this setup allows column widths declared at larger viewports to override those of smaller viewports in true mobile-first fashion.

Namespacing Viewport Specific Columns

In order to effectively control our columns at all viewports, we need to give them unique names so that the style overrides only occur when we explicitly make it do so.

Using our conditionals, even IE8 gets its own grid declarations!

I’m going to append a name to the end of each span to assign it to the viewport from which it is effective:

/*
 * IE8 grid
 * This grid will only work in IE8, and at all widths
 */
.ie8 .span_1_ie8 { ... }
.ie8 .span_2_ie8 { ... }
...
.ie8 .span_1_ie8 { ... }

/*
 * VIEWPORT ALPHA
 * Declare a grid which will be effective from 0px
 */
@media screen and (min-width: 0) {

  ...

  .span_1_vpalpha { ... }
  .span_2_vpalpha { ... }
  ...
  .span_1_vpalpha { ...  }

}

/*
 * VIEWPORT BETA
 * Declare a grid which will be effective from 480px
 * 480 / 16 = 30em
 */
@media screen and (min-width: 20em) {

  ...

  .span_1_vpbeta { ... }
  .span_2_vpbeta { ... }
  ...
  .span_1_vpbeta { ... }

}

/*
 * VIEWPORT GAMMA
 * Declare a grid which will be effective from 640px
 * 640 / 16 = 40em
 */
...

And that’s that, we’re ready to put our grid into action!

Usage

Using Gridpak is easy. Groups of columns must have a parent that is a clearfixed row… and that’s all you need to know!

<!--
  create a row with 2 columns and the following features:

  mobile  -> 3 spans + 9 spans
  tablet  -> 6 spans + 6 spans
  desktop -> 4 spans + 8 spans
  IE8     -> 4 spans + 8 spans
-->
<div class="row cf">
  <div class="col span_3_vpalpha span_6_vpbeta span_4_vpgamma span_4_ie8">
    content...
  </div>
  <div class="col span_9_vpalpha span_6_vpbeta span_8_vpgamma span_8_ie8">
    content...
  </div>
</div>

Nesting columns follows the same pattern:

<!--
  create a row with 2 columns spanning half the width for all viewports.

  Have both columns then contain 2 columns each, both spanning half the width 
  of the parent column.
-->
<div class="row cf">
  <div class="col span_6_vpalpha span_6_ie8">
    <div class="row cf">
      <div class="col span_6_vpalpha span_6_ie8">
        content...
      </div>
      <div class="col span_6_vpalpha span_6_ie8">
        content...
      </div>
    </div>
  </div>

  <div class="col span_6_vpalpha span_6_ie8">
    <div class="row cf">
      <div class="col span_6_vpalpha span_6_ie8">
        content...
      </div>
      <div class="col span_6_vpalpha span_6_ie8">
        content...
      </div>
    </div>
  </div>
</div>

“Whoa! That’s a lotta classes!” you may be saying… but guess what… you’re not going to find (m?)any grids with this flexibility by using a couple classes and descendant selectors.

If your ‘classitis’ sense is still tingling… there are a number of excellent articles out there which debunk the generally poor arguments portraying the use of classes as ‘bad’:

Conclusion

Phew, it’s been quite a journey to get here, and there are quite a few methods and ideas to wrap our heads around!

All in all we’ve put together a concise grid that can be scaled to as many, or as few, viewports as we like. More than that, it’s easy to use, doesn’t require very much markup, and gives us the flexibility to nest grids within grids as far as we like!

With the new flexbox syntax finally reaching candidate recommendation, we can start looking to experiment with the future of grid layouts.

Unfortunately, until IE9 (and even IE10 with its partial support of the syntax) is out of our hair, we may not be able to fully rely on flexbox for our grids.

Until then, I’m happy to have come across a solution which I have found to give me all the flexibility in a grid layout I could ask for.

Give me a shout in the comments if you have any questions or if you’ve come across useful tools for creating powerful grid systems.

Larry Botha

Larry is a co-founder at Jurisfy and Fixate. He loves expanding his knowledge on front-end development, giving hugs and high fives, and writing his own biographies in the third person.

Stay in the loop: Get your dose of frontend twice a week

Fresh news, inspo, code demos, and UI animations—zero fluff, all quality. Make your Mondays and Thursdays creative!

Feedback 8

Comments are closed.
  1. First: Awesome article! But now a question: Lyza Gardner says that you have to refresh Chrome after a zoom to get the media query work probably. Is this still true? Seems to be a major drawback 🙁

    • Just took a look through the comments on Lyza’s article – seems as though both Firefox and Opera behave themselves and repaint without a refresh on zoom. Chrome, on the other hand, seems to have some quirks depending on which OS you’re working on – some even crashing Chrome completely O.o

      I found, checking the above fiddles, that Chrome 24 on Mac does repaint on zoom, but it seems to have some odd calculation behaviour on resize. Things are calculated one way, and then on media breakpoints calculations seem to change!

    • Finally had time to test this myself. On Firefox everything runs great. And as you said Chrome triggers a resize event on the [window] object if you zoom, but the media query won’t be revalidated 🙁