Advertisement

A Simple Responsive Grid, Made Even Better With Sass

by

This Cyber Monday Tuts+ courses will be reduced to just $3 (usually $15). Don't miss out.

In this tutorial we're going to build a simple, responsive grid system using CSS. Once we're familiar with how it works, we'll improve it with Sass, learning some core Sass features along the way.

Getting Started With the CSS

Let's quickly examine what our grid system will look like. There are cleverer ways of building grids, ones which are less intrusive on the markup needed, but this grid gives us a great opportunity to practice simple, yet useful Sass techniques. It uses one wrapping element, then other elements within that which act as rows, then more elements within those which act as our columns.

A bit trippy, but you get the idea..

Grid.css

To begin with, all we need is a CSS file, so open up a new document in your code editor and name it "grid.css".

The first thing we're going to add is a global reset and box-sizing rule. This will make sure that padding applied to our elements is added within their calculated dimensions, giving us far more freedom for defining column widths.

*,
*:after,
*:before {
	margin: 0;
	padding: 0;
	-webkit-box-sizing: border-box;
	-moz-box-sizing: border-box;
	box-sizing: border-box; 
	}

Next we'll add a rule to make sure any images used behave fluidly.

img {
	width: auto;
	max-width: 100%;
	height: auto !important; 
	}

The Wrapper

Now some rules to make sure our wrapper element behaves itself.

.wrapper {
	width: 100%;
	margin: 0 auto;
}

Note: Bear in mind we're working mobile first. Our container starts out at 100% viewport width, but we'll alter that for larger screens.

The Rows

All our row elements do is contain the columns, making sure that groups of columns clear each other properly. Let's add some general .clearfix rules to our wrapper and row elements.

.wrapper:after,
.row:after {
	  content: "";
	  display: table;
	  clear: both; 
	  }

Note: We don't have to name these elements .wrapper, .row and .column, we can be much more creative than that. These names are just very clear for the purposes of this tutorial.

The Columns

We're going to have a range of column classes, depending on the size of each one (for example .column-1 and .column-6. Let's use an attribute selector to target and style all of these in one go.

[class*='column-'] {
	float: left;
	padding: 1em;
	width: 100%;
	min-height: 1px; 
	}

This selector says; take any element whose class contains the string column- and apply the following styles. Therefore all our column elements will float left, will have a padding of 1em (this forms our gutter and some vertical space) and will fill 100% of the viewport width (again, we're working mobile first here). Lastly, the min-height: 1px makes sure that the column displays properly, even if it has no content.

Believe it or not, we now have ourselves a grid! The following markup is all we need.

<section class="wrapper">

    <div class="row">
	
		<div class="column-3"></div>
		<div class="column-3"></div>
		<div class="column-6"></div>
	
	</div>

</section>

We can use any combination of columns within rows, take a look at the demo to see what you should currently have.

This screenshot shows several more columns than in the snippet above

Going Responsive

That's our mobile view taken care of, now let's add a media query to give us a different layout for larger screens. You'll need to determine breakpoints for your own grid, but we're going to use one arbitrary breakpoint of 30em.

@media only screen and (min-width: 30em) {

}

Any styles we place within this media query will take effect on screens of 30em and wider. We'll use this point to split up our columns into correct widths.

% Widths

How wide should each column be? That depends on how many columns we have. For this demonstration I'm going work with twelve columns, so each one should be exactly one twelfth (1/12) of the wrapper. For a column which spans two widths, it will be two twelfths, and so on. Here's what that gives us:

@media only screen and (min-width: 30em) {

	.wrapper {
		width: 95%;
		max-width: 65em; 
		}
		
	.column-1 {
		width: 8.33333%; 
		}
		
	.column-2 {
		width: 16.66667%; 
		}
		
	.column-3 {
		width: 25%; 
		}
		
	.column-4 {
		width: 33.33333%; 
		}
		
	.column-5 {
		width: 41.66667%; 
		}
		
	.column-6 {
		width: 50%; 
		}
		
	.column-7 {
		width: 58.33333%; 
		}
		
	.column-8 {
		width: 66.66667%; 
		}
		
	.column-9 {
		width: 75%; 
		}
		
	.column-10 {
		width: 83.33333%; 
		}
		
	.column-11 {
		width: 91.66667%; 
		}
		
	.column-12 {
		width: 100%; 
		} 
    
}

You'll also see that we've made the .wrapper element less than the full width of the screen and given it a max-width. Take a look what that's done to our grid.

You'll probably want to choose more appropriate padding in your own grid

Cleaning Things up With Sass

Our CSS grid works, but what would happen if we actually wanted sixteen columns in our grid? Or even more? We'd have to recalculate every column and manually enter it in our CSS file each time. Not to mention that our CSS would get longer and longer and more difficult to manage. Happily, Sass (or any other preprocessor) can help us.

Setting up Sass

This tutorial isn't about setting up Sass, it assumes you already know how to do that. If that's not the case and you need to get things up and running take a look at Mastering Sass: Lesson 1, or SASS and Compass for Web Designers: Introduction.

Once you have a Sass project setup, dive into the next step.

Defining Variables

Sass is going to help us clean up our CSS in all sorts of ways, but the first thing we can do is extract any useful values and store them in variables. Begin by starting off a new partial called "_variables.scss"; a Sass file which won't be compiled into CSS directly, but we'll reference in our other files.

// grid variables
$grid-columns: 12;
$grid-max-width: 65em;


// breakpoints
$breakpoint-small: "only screen and (min-width: 20em)";
$breakpoint-medium: "only screen and (min-width: 30em)";

These variables give us the amount of columns we want to work with; 12 at the moment, but could easily be altered to 16, perhaps 32 (whatever you want really). We've also stored some breakpoints in variables as strings, even though we're only using one at the moment.

We'll use these shortly, but first we'll setup some mixins.

Mixins

Sass mixins are chunks of code which we can define once and then re-use elsewhere in our project. For example, we could take the very first set of rules where we setup border-box and extract most of that into a mixin. We start with:

*,
*:after,
*:before {
	margin: 0;
	padding: 0;
	-webkit-box-sizing: border-box;
	-moz-box-sizing: border-box;
	box-sizing: border-box;
	}

Then we pull out part of it which we can reuse, defining a mixin which I've called "border-box" like so:

@mixin border-box {
	-webkit-box-sizing: border-box;
	-moz-box-sizing: border-box;
	box-sizing: border-box; 
	}

Then, we @import our variables and mixins into the main "grid.scss", using the mixin with an @include statement.

@import "variables";
@import "mixins";

*,
*:after,
*:before {
	margin: 0;
	padding: 0;

	@include border-box;

	}

@extend Clearfix

We can do a similar thing with the clearfix rules, as suggested by Sebastian Ekström. In this case we take the clearfix rules recommended by Nicolas Gallagher and add them to our mixin file with a placeholder selector (%):

%clearfix {
	*zoom: 1;
	&:before, 
	&:after {
		content: " ";
		display: table;
	}
	&:after {
		clear: both;
	}
}

The placeholder selector allows us to define whole chunks of style which only get output when we extend them elsewhere, like this:

.wrapper,
.row {
	@extend %clearfix;
}

Once compiled, that neat few lines of Sass looks like this:

.wrapper,
.row {
	*zoom: 1; 
	}
.wrapper:before,
.row:before,
.wrapper:after,
.row:after {
	content: " ";
	display: table; 
	}
.wrapper:after,
.row:after {
	clear: both; 
	}

Using Our Variables

Let's make use of some of those variables we set up, shall we? To begin with we can swap out the max-width value on the wrapper. This:

.wrapper {
	width: 95%;
	max-width: 65em; 
	}

becomes this:

.wrapper {
	width: 95%;
	max-width: $grid-max-width; 
	}

Now let's do the same with our media query. This:

@media only screen and (min-width: 30em) {

will be improved with our $breakpoint-medium variable:

@media #{$breakpoint-medium} {

Note: you'll see we wrapped our variable in #{}. This is referred to as interpolation. This is normally done if we need to output a variable within another, but it's necessary in this case because the Sass compiler trips over media queries if @media isn't directly followed by braces (). You can read more about this in Hugo Giraudel's All You Ever Need to Know About Sass Interpolation.

To use our final variable, $grid-columns, we need to make use of some more Sass functionality; loops.

Sass Loops

Our column width definitions are all exactly the same, apart from the actual values. It will be far cleaner if we output a column definition for as many columns as we need, changing the values each time. To do this we can use a Sass @for loop, which looks like this:

@for $i from 1 through 12 {
// looped content
}

This will loop over 12 iterations, and each time the value of $i will reflect that loop. We can output $i like this:

@for $i from 1 through 12 {

	.column-#{$i} { 
	
		}

}

Again, you'll notice we're using #{} around $i to output the value as a string which we're appending to the .column- selector. This gives us the following when compiled:

.column-1 {

	}

.column-2 {

	}

.column-3 {

	}

.column-4 {
	
	}

.column-5 {

	}

.column-6 {

	}

.column-7 {

	}

.column-8 {

	}

.column-9 {

	}

.column-10 {

	}

.column-11 {

	}

.column-12 {

	}

Brilliant! Let's now use some calculations to output the correct styles within these selectors.

Sass Operators

We're doing well, but we now need to output something like the following for each selector:

.column-5 {
	width: 41.66667%; 
	}

That column width is calculated as 100% divided by the total number of columns, multiplied by the column number. In this case 100% / 12 * 5 = 41.66667%. That's therefore the calculation we need to apply, switching out the relevant values for variables.

@for $i from 1 through 12 {

	.column-#{$i} { 
		width: 100% / 12 * $i;
	}

}

Sass is now performing the calculations for us, giving us the following:

.column-1 {
	width: 8.33333%;
}

.column-2 {
	width: 16.66667%;
}

.column-3 {
	width: 25%;
}

.column-4 {
	width: 33.33333%;
}

.column-5 {
	width: 41.66667%;
}

.column-6 {
	width: 50%;
}

.column-7 {
	width: 58.33333%;
}

.column-8 {
	width: 66.66667%;
}

.column-9 {
	width: 75%;
}

.column-10 {
	width: 83.33333%;
}

.column-11 {
	width: 91.66667%;
}

.column-12 {
	width: 100%;
}

As a final bit of tidying up, we can use the $grid-columns variable, instead of the value 12:

@for $i from 1 through $grid-columns {

	.column-#{$i} { 
		width: 100% / $grid-columns * $i;
	}

}

Now, if we want to change the number of columns, we simply change the variable and the calculations will all be done for us! For example, changing $grid-columns to 4 give us the following CSS:

@media only screen and (min-width: 30em) {
  .wrapper {
    width: 95%;
    max-width: 65em; }

  .column-1 {
    width: 25%; }

  .column-2 {
    width: 50%; }

  .column-3 {
    width: 75%; }

  .column-4 {
    width: 100%; } 
}

Conclusion

Our final grid.scss is a paltry 42 lines of code; way less than our initial CSS.

@import "variables";
@import "mixins";

*, *:after, *:before {
	margin: 0;
	padding: 0;
	@include border-box;
	}
	
img {
	width: auto;
	max-width: 100%;
	height: auto !important; 
	}	

.wrapper {
	width: 100%;
	margin: 0 auto;
	}

.wrapper, .row {
	@extend %clearfix;
	}
	 
[class*='column-'] {
	float: left;
	padding: 1em;
	width: 100%;
	min-height: 1px; 
	}	

@media #{$breakpoint-medium} {
	.wrapper {		
		width: 95%;
		max-width: $grid-max-width;		
		}
	@for $i from 1 through $grid-columns {	  	
	  	.column-#{$i} { 		
	  		width: 100% / $grid-columns * $i;	
	  		}	  		
	  	}	
	}

Throughout this process we've looked at Sass variables, mixins, placeholders, extend, loops, operators and even interpolation. This is an incredibly powerful set of features and a great start if you're just getting into Sass.

How else could you improve the grid we've built? What else would you add or even remove? Let us know in the comments!

Further Reading

Advertisement