Create a SASS grid system part 2

Posted on 14th May 2014

SASS Grid System

In my previous post I discussed how you can use SASS to create a simple responsive grid system where you define a few variables and let SASS worry about the calculations. The result of which you can see below.

Whilst the grid system we've already created is responsive, in a live project it wouldn't be very practical to use. As you can see in the above, the columns will always be stuck at their initial grid size no matter how much the window is resized. A 3 column layout may be great for higher resolutions, but less so for lower.

In this post I'll be showing you how you can improve your grid system by building in functionality allowing you to redefine the width of the columns as they get smaller. To achieve this, we'll be using media queries and creating what I'll be referring too as Breakpoint Classes.

1) Defining the Breakpoints

First things first, let's create a few SASS variables and define the breakpoints we will use in our media queries. In terms of which breakpoints to use that's a whole other topic in itself, so I'm just going to use these four for example's sake. Feel free to change them as you see fit.

// Define Media Query Breakpoints
$bp-xlrg: 1280px;
$bp-lrg: 924px;
$bp-med: 640px;
$bp-sml: 480px;

2) How Breakpoint Classes Work

When we create columns in our grid system, we assign them classes based on how big we want them to be. Below is an example of a 3 column row using the default 12 column grid system.

<div class="container">
    <div class="clear grid4">Grid 4</div>
    <div class="grid4">Grid 4</div>
    <div class="grid4">Grid 4</div>
</div>

Breakpoint Classes will be exactly the same as the standard classes. The only differences are that they will have a prefix to distinguish themselves and will be placed within Media Queries with breakpoints defined by whatever you have chosen as your variables.

With this in mind, if we tag a breakpoint class to one of our columns it will override the width of the standard class, and because the breakpoint class will be within a media query that override won't kick in until the browser passes your breakpoint. Therefore the column could be 4 columns width at one resolution yet 8 columns width at another!

3) Creating the SASS

We're going to use the SASS created in my previous post as the starting point.

Since all we're wanting to do is duplicate the code generated by this SASS and place it within media queries, it would make sense to first wrap it within a mixin so we don't have to repeat the same chunk of SASS several times over.

Let's do just that.

// Set Grid (1170gs default)
$column-width: 70px;
$gutter: 30px;
$columns: 12;
$padding: $gutter / 2;

// Should it be responsive?
$responsive: true;

// Set container width relative to user variables
$width: ($column-width * $columns) + ($gutter * ($columns - 1));

// Set Base Container
.container
{
// Set container width if responsive
@if $responsive == true
{
    max-width:$width;
}
@else
{
    width:$width;
}
margin:0px auto;
position:relative;
padding: 0 $padding 0 $padding;
}

// Create Grid Mixin to save us from having to clone the code over and over
@mixin create_grid($prefix)
{

// Define Individual Grid Classes
@for $i from 1 to $columns
{
    // Calculate grid size in pixels
    $grid: ($column-width * $i) + ($gutter * ($i - 1));

    // Calculate responsive grid sizes if required
    @if $responsive == true
    {
        // Calculate grid size in percentage
        $grid: ($grid / $width) * 100%;

        // Get Gutter Percentage for Grids
        $gutter-percentage: ($gutter / $width) * 100%;

        .#{$prefix}#{$i}
            {
            width: $grid;
            float: left;
            clear:none;
            margin-left:$gutter-percentage;
            }

    }
    @else
    {
        .grid#{$i}
            {
            width: $grid;
            float: left;
            margin-left:$gutter;
            }
    }
}

}

In addition to wrapping the grid generating code in a mixin, we want to give each of our Breakpoint Classes a prefix so they don't conflict with Breakpoint Classes within other media queries. Therefore we allow the mixin to accept an argument named $prefix.

We also need to define Breakpoint Clear and Full Width classes which will allow you to redefine rows as well as column sizes.

// Define Additional Full Width Variants & Clear Classes For Responsive Classes Only
@if $prefix != "grid"
{        
    .#{$prefix}-full
    {    
    width:100%;
    float:none;
    margin-left:0;
    }

    .#{$prefix}-clear
    {
    margin-left: 0px;
    clear: left;
    }
}
@else
{
    .clear
    {
    margin-left: 0px;
    clear: left;
    }
}

4) Generating the CSS

So now we've got our mixin in place, all that remains is to generate the CSS and place it within some media queries.

First things first, we need to use the mixin to generate the normal grid classes we've been using all along. We'll give these a prefix of "grid" so the final CSS should still read grid1, grid2, grid3.

// Generate Grid Classes
@include create_grid("grid");
Now we're going to create the Breakpoint Classes and place them within media queries using the breakpoint variables we defined way back in step 1.

As the grid system allows you to define if or not it should be responsive, we're going wrap the whole thing within an IF statement using the responsive variable.

// Generate additional responsive classes. We only want these to be created if we've stated we want it to be responsive
@if $responsive == true
{
    @media screen and (max-width: $bp-xlrg)
    {
        @include create_grid("xlrg");
    }

    @media screen and (max-width: $bp-lrg)
    {
        @include create_grid("lrg");
    }

    @media screen and (max-width: $bp-med)
    {
        @include create_grid("med");
    }

    @media screen and (max-width: $bp-sml)
    {
        @include create_grid("sml");
    }
}

I've given them prefixes relative to their placement, so for example within the $bp-med media query the Breakpoint Classes should all read med1, med2, med-clear, med-full and so forth. The great thing about this is that you can set the prefixes to whatever takes your fancy.

5) Final Code

So here we have the final SASS code and the CSS it generates.

SASS

// Set Grid (1170gs default)
$column-width: 70px;
$gutter: 30px;
$columns: 12;
$padding: $gutter / 2;

// Define Media Query Breakpoints
$bp-xlrg: 1280px;
$bp-lrg: 924px;
$bp-med: 640px;
$bp-sml: 480px;

// Should it be responsive?
$responsive: true;

// Set container width relative to user variables
$width: ($column-width * $columns) + ($gutter * ($columns - 1));

// Set Base Container
.container
{
// Set container width if responsive
@if $responsive == true
{
    max-width:$width;
}
@else
{
    width:$width;
}
margin:0px auto;
position:relative;
padding: 0 $padding 0 $padding;
}

// Create Grid Mixin to save us from having to clone the code over and over
@mixin create_grid($prefix)
{

// Define Individual Grid Classes
@for $i from 1 to $columns
{
    // Calculate grid size in pixels
    $grid: ($column-width * $i) + ($gutter * ($i - 1));

    // Calculate responsive grid sizes if required
    @if $responsive == true
    {
        // Calculate grid size in percentage
        $grid: ($grid / $width) * 100%;

        // Get Gutter Percentage for Grids
        $gutter-percentage: ($gutter / $width) * 100%;

        .#{$prefix}#{$i}
            {
            width: $grid;
            float: left;
            clear:none;
            margin-left:$gutter-percentage;
            }

    }
    @else
    {
        .grid#{$i}
            {
            width: $grid;
            float: left;
            margin-left:$gutter;
            }
    }
}

  // Define Additional Full Width Variants & Clear Classes For Responsive Classes Only
@if $prefix != "grid"
{        
    .#{$prefix}-full
    {    
    width:100%;
    float:none;
    margin-left:0;
    }

    .#{$prefix}-clear
    {
    margin-left: 0px;
    clear: left;
    }
}
@else
{
    .clear
    {
    margin-left: 0px;
    clear: left;
    }
}

}

// Generate Grid Classes
@include create_grid("grid");

// Generate additional responsive classes. We only want these to be created if we've stated we want it to be responsive
@if $responsive == true
{
    @media screen and (max-width: $bp-xlrg)
    {
        @include create_grid("xlrg");
    }

    @media screen and (max-width: $bp-lrg)
    {
        @include create_grid("lrg");
    }

    @media screen and (max-width: $bp-med)
    {
       @include create_grid("med");
    }

    @media screen and (max-width: $bp-sml)
    {
       @include create_grid("sml");
    }
}

####CSS

.container {
  max-width: 1170px;
  margin: 0px auto;
  position: relative;
  padding: 0 15px 0 15px;
}

.grid1 {
  width: 5.98291%;
  float: left;
  clear: none;
  margin-left: 2.5641%;
}

.grid2 {
  width: 14.52991%;
  float: left;
  clear: none;
  margin-left: 2.5641%;
}

.grid3 {
  width: 23.07692%;
  float: left;
  clear: none;
  margin-left: 2.5641%;
}

.grid4 {
  width: 31.62393%;
  float: left;
  clear: none;
  margin-left: 2.5641%;
}

.grid5 {
  width: 40.17094%;
  float: left;
  clear: none;
  margin-left: 2.5641%;
}

.grid6 {
  width: 48.71795%;
  float: left;
  clear: none;
  margin-left: 2.5641%;
}

.grid7 {
  width: 57.26496%;
  float: left;
  clear: none;
  margin-left: 2.5641%;
}

.grid8 {
  width: 65.81197%;
  float: left;
  clear: none;
  margin-left: 2.5641%;
}

.grid9 {
  width: 74.35897%;
  float: left;
  clear: none;
  margin-left: 2.5641%;
}

.grid10 {
  width: 82.90598%;
  float: left;
  clear: none;
  margin-left: 2.5641%;
}

.grid11 {
  width: 91.45299%;
  float: left;
  clear: none;
  margin-left: 2.5641%;
}

.clear {
  margin-left: 0px;
  clear: left;
}

@media screen and (max-width: 1280px) {
  .xlrg1 {
    width: 5.98291%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .xlrg2 {
    width: 14.52991%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .xlrg3 {
    width: 23.07692%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .xlrg4 {
    width: 31.62393%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .xlrg5 {
    width: 40.17094%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .xlrg6 {
    width: 48.71795%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .xlrg7 {
    width: 57.26496%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .xlrg8 {
    width: 65.81197%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .xlrg9 {
    width: 74.35897%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .xlrg10 {
    width: 82.90598%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .xlrg11 {
    width: 91.45299%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .xlrg-full {
    width: 100%;
    float: none;
    margin-left: 0;
  }

  .xlrg-clear {
    margin-left: 0px;
    clear: left;
  }
}
@media screen and (max-width: 924px) {
  .lrg1 {
    width: 5.98291%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .lrg2 {
    width: 14.52991%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .lrg3 {
    width: 23.07692%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .lrg4 {
    width: 31.62393%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .lrg5 {
    width: 40.17094%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .lrg6 {
    width: 48.71795%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .lrg7 {
    width: 57.26496%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .lrg8 {
    width: 65.81197%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .lrg9 {
    width: 74.35897%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .lrg10 {
    width: 82.90598%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .lrg11 {
    width: 91.45299%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .lrg-full {
    width: 100%;
    float: none;
    margin-left: 0;
  }

  .lrg-clear {
    margin-left: 0px;
    clear: left;
  }
}
@media screen and (max-width: 640px) {
  .med1 {
    width: 5.98291%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .med2 {
    width: 14.52991%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .med3 {
    width: 23.07692%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .med4 {
    width: 31.62393%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .med5 {
    width: 40.17094%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .med6 {
    width: 48.71795%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .med7 {
    width: 57.26496%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .med8 {
    width: 65.81197%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .med9 {
    width: 74.35897%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .med10 {
    width: 82.90598%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .med11 {
    width: 91.45299%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .med-full {
    width: 100%;
    float: none;
    margin-left: 0;
  }

  .med-clear {
    margin-left: 0px;
    clear: left;
  }
}
@media screen and (max-width: 480px) {
  .sml1 {
    width: 5.98291%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .sml2 {
    width: 14.52991%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .sml3 {
    width: 23.07692%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .sml4 {
    width: 31.62393%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .sml5 {
    width: 40.17094%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .sml6 {
    width: 48.71795%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .sml7 {
    width: 57.26496%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .sml8 {
    width: 65.81197%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .sml9 {
    width: 74.35897%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .sml10 {
    width: 82.90598%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .sml11 {
    width: 91.45299%;
    float: left;
    clear: none;
    margin-left: 2.5641%;
  }

  .sml-full {
    width: 100%;
    float: none;
    margin-left: 0;
  }

  .sml-clear {
    margin-left: 0px;
    clear: left;
  }
}

6) See it in action

So here we have it, the fruits of our labour. Click on the pen below and then resize your browser window. See how the column structure jumps around? Handy.