HTML & CSS
Article

Creating a Responsive Grid System with Susy and Breakpoint

By James Steinbach

The big appeal behind CSS frameworks (like Bootstrap and Foundation) is that they come with a responsive grid system all set and ready to go. The downside is that you have to be content with their column count, the width of their gutters, and all their classes in your markup. What if your project requirements go beyond the defaults of a prefab CSS grid framework? This article will show how you can build and maintain your own unique responsive grids with two Sass libraries: Susy and Breakpoint.

Susy Basics

For a more detailed introduction to using Susy to manage your grids, check out this SitePoint article or this slidedeck. We’ll just hit the high points here so that you have some context for what follows.

All your grid config settings live in a map called $susy. You can use the key-value pairs in this map to set your max-width (container), column count (columns), and gutter width (gutters).

With Susy, you specify a container element for your grid elements by using the container() mixin. The selector that includes that mixin will then be assigned the max-width from the container value in $susy, centered with left and right margins set to auto, and given a clearfix.

Each column inside that container is controlled with the span() mixin. This mixin typically takes three arguments: span (number of parent columns it will fill), layout (number of parent columns if different from default), and location (first/alpha, last/omega, or a column number).

$susy: (
  container: 70em,
  columns: 12,
  gutters: 1/6
);
.main {
  @include container;
}
.content {
  @include span(8 of 12 first);
}
.sidebar {
  @include span(4 of 12 last);
}

If you want to look under the hood of a more detailed Susy grid, here’s a Sassmeister gist for you to play with.

Breakpoint Basics

If you want a detailed look at how to use Breakpoint, a Sass plugin for managing media queries, this SitePoint article should answer most of your questions. Again, we’ll cover some basics for the sake of introduction.

Breakpoint provides a single powerful mixin for handling almost any kind of media query: breakpoint(). If you pass the mixin a single measurement (like 960px), it will use that number as a min-width media query. If you provide two measurements, it will use them as min-width and max-width values. If you pass a keyword / value pair, you can create any media query you need: height, orientation, resolution, etc. You can also pass several keyword / value pairs to create complex queries: just enclose each pair in parentheses:

.element {
  font-size: 1.3em;
  @include breakpoint((min-width 30em) (max-width 60em) (orientation portrait)) {
    font-size: 1.5em;
  }
}

Mixin: susy-breakpoint()

Now that we’ve got the basics of Susy & Breakpoint, let’s look at how they work together to help us quickly build responsive grid layouts. If you have both imported into your project, you can use the susy-breakpoint() mixin to combine their functions.

The susy-breakpoint() mixin wraps Breakpoint’s media queries around Susy’s grid output. It requires two parameters (and takes an optional third parameter).

The first parameter you’d pass to susy-breakpoint() is the media query data you plan to pass to Breakpoint. This data is passed straight to the Breakpoint mixin and used to generate the media query for this part of your code.

The second parameter is the Susy grid layout: at its simplest, it can be a number: a column count. You can also pass it a map that contains the same keys as $susy. This column count sets the “context” for everything that Susy calculates inside this mixin. For example, if you pass “16” as the layout, all the Susy grid mixins and functions inside susy-breakpoint() will use 16 as the column count value, regardless of what columns value is in the $susy config map.

If you’re familiar with the concept of scoped variables, this is simply a way to scope a new column count to the current media query. In fact, if you pass a map that follows the $susy pattern, any values in that map will be scoped to the generated media query context.

The third (optional) parameter for susy-breakpoint() is Breakpoint’s no-query value. This parameter allows you to handle fallbacks for browsers that don’t support media queries. If you enable $breakpoint-no-query-fallbacks and pass a class as this third parameter, it will be used as a wrapper class around the current selector (instead of a media query).

$breakpoint-no-query-fallbacks: true;
.element {
  color: blue;
  @include susy-breakpoint(480px, $susy, '.no-mq') {
    color: red;
  }
}

// output
.element {
  color: blue;
}
@media screen and (min-width: 480px) {
  .element {
    color: red;
  }
}
.no-mq .element {
  color: red;
}

Note: the example above doesn’t use Susy grid mixins inside the query: it’s just demonstrating how to use Breakpoint’s no-query.

One Container Grid with Changing Column Elements

There are two ways to manage responsive grids. You’re probably most familiar with the first one: all column measurements are based on the same column count and elements change span count at various breakpoints. This is the way nearly every CSS grid framework operates. Let’s look at how we can create that with Susy and Breakpoint.

The first thing we need is our $susy map and a couple of breakpoint variables. We’ll also turn on Breakpoint’s no-query feature.

$susy: (
  container: 64em,
  columns: 12,
  gutters: 1/6
);
$bp-medium: 32em;
$bp-large: 60em;
$breakpoint-no-query-fallbacks: true;

After our map is set up, we’ll declare our container element:

.container {
  @include container;
}

Inside that container, we’ll set up our two child elements as full-width spans. This will be our default for small screens: the “columns” are stacked vertically.

.content {
  @include span(12 last);
}
.sidebar {
  @include span(12 last);
}

Let’s move up to the medium and large breakpoints. We’ll do that with the susy-breakpoint() mixin. We’re passing $susy as the layout parameter. We could also use 12 (the columns value in $susy). I know, this may feel a bit redundant now, but later we’ll see how useful passing a new map as the layout can be.

.content {
  @include span(12 last);
  @include susy-breakpoint($bp-medium, $susy) {
    @include span(6 first);
  }
  @include susy-breakpoint($bp-large, $susy) {
    @include span(8 first);
  }
}
.sidebar {
  @include span(12 last);
  @include susy-breakpoint($bp-medium, $susy) {
    @include span(6 last);
  }
  @include susy-breakpoint($bp-large, $susy) {
    @include span(4 last);
  }
}

Now we’ve got a basic responsive grid. On any screen smaller than 32em, the content and the sidebar will be full-width and stacked vertically. At 32em, those elements will appear side-by-side at half the total width. At 60em, the ratio between their widths shifts from 1/1 to 2/1. At 64em, the container will stop expanding and will be centered inside the viewport.

Changing Container Grid with Stable Column Spans

The other (and admittedly, more complicated) way to set up responsive grids is to set fixed span values for the columns and change the container column properties. To do this, you’ll need to change the column ratios at each breakpoint by using a column list in the columns value of a $susy-style maps for each breakpoint:

$susy: (
  container: 70em,
  columns: 1,
  gutters: 1/16
);
$medium: (
  columns: 1 1
);
$large: (
  columns: 2 1
);

$bp-medium: 32em;
$bp-large: 60em;

Putting a list of numbers in the columns value tells Susy to create columns widths that match the proportions of those numbers. The span() mixin will size then elements to match those proportions based on their location. So columns: 1 1 creates two equal width columns (just like columns: 2 would do), while columns: 2 1 creates two columns and makes the first twice as wide as the second.

To use these ratios, we’d call susy-breakpoint() and span() in the following way:

.container {
  @include container;
}
.primary {
  @include span(1 at 1);
  @include susy-breakpoint($bp-medium, $medium) {
    @include span(1 at 1);
  }
  @include susy-breakpoint($bp-large, $large) {
    @include span(1 at 1);
  }
}
.secondary {
  @include span(1 at 1);
  @include susy-breakpoint($bp-medium, $medium) {
    @include span(1 at 2);
  }
  @include susy-breakpoint($bp-large, $large) {
    @include span(1 at 2);
  }
}

The at location provides the magic here: in the large breakpoint, the first column is wider than the second, so specifying the location tells Susy to make .primary the width of the first number in the columns list. Specifying that the .secondary element is at location 2 tells Susy that it should match the ratio of the second number in the columns list (and that it’s the last element and should have its margin-right removed). We have to repeat the span() mixin inside each susy-breakpoint() because Susy needs the new columns measurements to recalculate the width percentages at each breakpoint. Try this out in real life with these Sassmeister gists: one, two.

Conclusion

Susy and Breakpoint make a pretty powerful team here. Not only can you create grids that are precisely as simple or as complex as you need them to be, you can also change those grids as much as your project requires at any breakpoint. Please ask any follow-up questions you have in the comments, or leave a comment to share a clever grid you’ve created with Susy and Breakpoint!

Free Guide:

7 Habits of Successful CTOs

"What makes a great CTO?" Engineering skills? Business savvy? An innate tendency to channel a mythical creature (ahem, unicorn)? All of the above? Discover the top traits of the most successful CTOs in this free guide.

Comments
sfvdmerwe

HI James,

Thanks for this post. I like the idea to use stable columns. Zell Liew posted a complex structure. http://www.zell-weekeat.com/wp-content/uploads/2014/04/susy2-1.png

To use stable columns on this layout should be straight forward.
Not sure how to implement the breakpoint.

Will it be possible to use a more complex layout as an example in your tutorial.

jdsteinbach

Thanks for your question! Zell spends 2 posts explaining how he created the layout he showed in that image: Post 1 & Post 2. If you'd like to see code behind his example, I'd start with those two posts & contact him with further questions. I'm not able to rewrite this tutorial to cover his example, however.

sfvdmerwe

Hi, you explained in your post how to use "stable columns" in a basic layout. Will it be possible to nest other structures inside the stable columns, for example similar to the layout Zell is using in his examples.

Maybe if you use your layout and nest another "stable column"

jdsteinbach

Sure, you can nest columns inside of columns: if your wider column is @include span(8 of 12);, for example, you could put two columns inside of that using @include span(4 or 8); on each of those.

Zell_Liew

Hey there,

Its possible to nest columns within each other. This article on Susy contexts might be useful for ya.

http://www.zell-weekeat.com/context-with-susy/

smile

sfvdmerwe

I uploaded my code onto codepen.. http://codepen.io/anon/pen/gbQvod

The breakpoints is not working inside codepen, maybe something I
didn’t configure. Working on my local host with some floating problems.

Can you please check and give recommendations.
I have screen dumps of what i tried to accomplish, but can upload images here.

Thanks

jdsteinbach

Yeah, looks like CodePen doesn't do the @imports like that: You can put your code on sassmeister.com and get all those libraries working right away though

sfvdmerwe

Hi,

Moved it to http://sassmeister.com/gist/d9ce0c19884ee3cc1295
Now it shows the same as my localhost.

im trying to do this...https://www.totalindex.co.za/images/4.jpg

Can you please help.... is must be something small

sfvdmerwe

anyone please help....

jdsteinbach

A couple things:
1) for .blue1 and .blue2, wrap your @include span(3 wider of 6); in susy-breakpoint($bp-medium, $medium) { } - that will get those two divs to go full width at the smallest width
2) the clear: left on .Green is causing it to end up below the bottom of .Blue - that's how CSS clear work: it forces the div down to a new line with nothing to its left. You can remove that at the next breakpoint to put the green div back at the top, but on medium, that's it. I'd work around this by putting yellow & green in a parent div together so they stack correctly at medium, then making them 1/2 their parent's width at the wider size.

Recommended
Sponsors
Because We Like You
Free Ebooks!

Grab SitePoint's top 10 web dev and design ebooks, completely free!

Get the latest in Front-end, once a week, for free.