Rapid Responsive Development with Sass and Flint

Ezekiel Gabrielse
Tweet

For any developer, speed is of the essence. We are constantly looking for more effiecient ways to do our jobs, and being programmers, we often create certain tools that help us do just that. One of the tools my team uses is a new grid system I developed called Flint.

What is Flint?

Flint is a responsive grid system written in Sass, created to allow developers to rapidly produce responsive layouts that are built on a sophisticated foundation. Being a designer myself, a large amount of time was spent on the syntax itself. Flint is very unique in the fact that it uses only a single mixin for all output: _( ). Yes, it really is just simply an underscore. Easy to remember, huh? It shoves the function mumbo-jumbo out of the way and allows us to focus on the aspect that matters most: the layout.

Getting Started

Getting up and running with Flint is simple: you can either configure your own grid, or use the default grid setup that ships with Flint. Being able to quickly configure a grid was at the forefront of my goals when developing Flint; and so it offers an immense configuration map, which is essentially a Sass map of all your project’s grid-settings and breakpoints. You don’t have to mess with endless variables just to get your project up and running. Instead, you just create a single variable called $flint:

$flint: (
    "config": (

        "desktop": (
            "columns": 16,
            "breakpoint": 80em
        ),

        "laptop": (
            "columns": 12,
            "breakpoint": 60em
        ),

        "tablet": (
            "columns": 8,
            "breakpoint": 40em
        ),

        "mobile": (
            "columns": 4,
            "breakpoint": 20em
        ),

        // ...
    )
);

I’ve omitted the settings area of the configuration map for readability purposes, and because frankly that could be an article in itself. To learn more about that section, check out the Flint documentation on Github.

One of the immediate advantages of using Flint is that it allows you to create an unlimited number of breakpoints for your project, with any alias that you want. If you like to call your breakpoints crazy names like ‘high-tide’ or ‘ex-presidents’, that’s completely fine with me.

I went the route of allowing an unlimited amount of breakpoints after realizing that not every project is the same; they differ so drastically that using the same grid every single time limits both the designer and developer, and so the final product.

The Basics

Now that we’ve covered the what and touched base a little bit on the configuration that Flint offers, let’s get down to the good stuff: Making things! Getting started with Flint is super easy. You tell it what to do, and it does it. If you think I’m exaggerating, read on! Here are a few quick examples:

.foo {
    @include _(4);
}

And here’s the CSS output:

.foo {
    display: block;
    float: left;
    width: 93.75%;
    margin-right: 3.125%;
    margin-left: 3.125%;
}

@media only screen and (min-width: 60.0625em) {
    .foo {
        width: 23.4375%;
        margin-right: 0.78125%;
        margin-left: 0.78125%;
    }
}

@media only screen and (min-width: 40.0625em) and (max-width: 60em) {
    .foo {
        width: 31.25%;
        margin-right: 1.04167%;
        margin-left: 1.04167%;
    }
}

@media only screen and (min-width: 20.0625em) and (max-width: 40em) {
    .foo {
        width: 46.875%;
        margin-right: 1.5625%;
        margin-left: 1.5625%;
    }
}

What we did here was tell Flint to create a block that spans 4 columns accross all breakpoints. That’s a nice amount of code for such a small declaration, huh? I call this type of argument a recursive shorthand. You’ll notice, real margins get outputted; none of that faux padding stuff. It’s also thinking mobile first, though that’s completely customizable through the config options I mentioned earlier.

Also notice that Flint automatically outputs @media queries for you. You won’t need a separate plugin to handle all of your breakpoints. Everything is connected within Flint, but we’ll talk more on that later.

While we’re on the topic of breakpoints… What if we need different spans for each breakpoint? Well, Flint’s got you covered there too! And no, you don’t need to create a declaration for each breakpoint. That’s not how things work here. Again, just tell Flint to do what you’re thinking:

.foo {
    @include _(8 6 4 2);
}

And the output:

.foo {
    display: block;
    float: left;
    width: 43.75%;
    margin-right: 3.125%;
    margin-left: 3.125%;
}

@media only screen and (min-width: 60.0625em) {
    .foo {
        width: 48.4375%;
        margin-right: 0.78125%;
        margin-left: 0.78125%;
    }
}

@media only screen and (min-width: 40.0625em) and (max-width: 60em) {
    .foo {
        width: 47.91667%;
        margin-right: 1.04167%;
        margin-left: 1.04167%;
    }
}

@media only screen and (min-width: 20.0625em) and (max-width: 40em) {
    .foo {
        width: 46.875%;
        margin-right: 1.5625%;
        margin-left: 1.5625%;
    }
}

What’s happening here? We told Flint that we want to create a block that spans 8 columns on desktop, 6 columns on laptop, 4 on tablet; and last but not least, 2 columns on mobile. We did this by passing in a simple space separated list of arguments. I call this type of argument a variable shorthand. If you notice, the order of operations matches the order used in our configuration map, which follows a DESC order. Pretty cool, huh?

There’s a lot more to Flint than just simply creating columns. It’s really designed to make the whole process of responsive design easier to manage, and so easier to do.

I was originally going to write up a short tutorial-esque section for this article, but I’ve decided instead to talk about the core concepts that I’ve developed while using and building Flint. Instead of creating something that incorporates all these concepts just for the sake of the article, let’s talk about each of these concepts individually and how Flint helps streamline that particular area of development.

Keeping It All Connected

One of the major things I wanted Flint to do was to keep the various areas of responsive design connected. Let me explain.

With most modern grid systems, your breakpoints and your grid are not connected the way they should be. You have a plugin for your breakpoints, and then you have a plugin for your grid system. This creates a major disconnect in my opinion. Now what do I mean by that?

You have to configure your breakpoints, and then configure your grid within those breakpoints. Configuring the breakpoints in itself can be a headache, especially if it doesn’t handle the math needed for certain breakpoint combinations (think a query between 2 sizes). But! There’s a plugin for that issue too, right?

See what I’m getting at here? We waste way too much time configuring things to work with other things, when all of these things should actually be one thing. Now, I understand that this isn’t always the case and that some workflows call for this type of separation, but mine does not.

Therefore, incorporating Flint into your workflow solves this disconnect. And how does it do that? Well, ladies and gentlemen, that brings us straight into our first concept! (For the sake of brevity, I’m going to use some code-hyperbole — forgive me.)

Speeding Things Up

Alright, finally! Our first concept! I know, I know… it’s been a long road; but hopefully by now you can relate to the various issues that we’ll be quickly going over. The first one is the dreaded container. The setup for this varies on a lot of things, such as if your grid is static, or if it’s fluid; fixed, or stretchy. You get the point. Let’s assume it’s fixed. In order to create real breakpoints that work with our grid, we’d probably have to do something like this:

// Breakpoints
$desktop: 80em;
$laptop: 60em;
$tablet: 40em;
$mobile: 20em;

// Now the math...
$mobile-to-tablet: (min-width: ($mobile + 0.0625em)) and (max-width: $tablet);
$tablet-to-laptop: (min-width: ($tablet + 0.0625em)) and (max-width: $laptop);
$laptop-to-desktop: (min-width: $laptop + 0.0625em));

// Finally, the actual container!
.container {
    @include container(4); 

    @include breakpoint($mobile-to-tablet) {    
        @include container(8);
    }

     @include breakpoint($tablet-to-laptop) {    
        @include container(12); 
    }

    @include breakpoint($laptop-to-desktop) {    
        @include container(16);
    }
}

Cool. That’s a lot of code, and a lot of room for error. I know that this probably isn’t a realistic setup for the average reader, but remember, we’re exaggerating here (even if I have seen this exact setup before). How would we do this with Flint? I’m glad you asked!

.container {
    @include _("container");
}

Flint knows your breakpoints, how many there are, and the column count associated with each one; so it also handles all of the math, all the setup, and all the output. It knows that when you tell it to make a container, you want a container for each breakpoint.

Alright, I said quick, so on to our next concept!

How We Keep Everything Connected

We’ve talked about having a plugin for breakpoints, and then one for your grid. There’s a reason for this, I know. You’ve probably been thinking I’m missing a little something about responsive design. Like the fact that breakpoints are used for things other than just a grid. Things like breakpoint specific styling. I agree. But don’t worry, Flint handles that too!

.foo {
    background: black;

    @include _(tablet) {
        background: white;
    }
}

And the output:

.foo {
    background: black;
}

@media only screen and (min-width: 20.0625em) and (max-width: 40em) {
    .foo {
        background: white;
    }
}

Cool. That’s not a lot of code. There’s a whole slew of arguments you can use for jobs like that, and the best thing of all? No math! You tell Flint what to do in human terms and it does it. Hopefully you’re noticing a trend here. Here’s a small taste of the many arguments you can use when defining media queries.

.bar {
    // ...

    @include _(from tablet to laptop) {
        // Anything between tablet and laptop
    }

    @include _(less than laptop) {
        // Anything less than laptop
    }

    @include _(greater than mobile) {
        // Anything greater than mobile
    }

    @include _(10em greater than tablet) {
        // Anything 10em greater than tablet
    }
}

Would you look at that! Remember when I said human readable? I meant it. This is highly beneficial, especially when working within a team of developers. If it’s easy to understand, everybody is on the same page. And that’s pretty neat. You can even create arbitrary media queries, like @include _(from 12em to 55em).

I hope by now you can see the benefits of having a system that handles all of these things; not only for reasons such as effeciency and speed, but also for your sanity. I kid, I kid.

Before we close, let’s discuss our last concept. This one is a biggy and is the culprit of many long, frustrating nights. The dreaded (even more so than the container)… context.

It’s All Contextual, Dude

Yeah, not really, dude. If you’ve ever used a grid system (and I’m assuming you have), you know that when nesting an element inside of another element, there must be a context argument. This is to keep a consistent gutter regardless of how deep an element is nested. This is how it usually looks:

.foo {
    @include span(12);

    .bar {
        @include span(6, 12);
    }
}

Nothing exciting here. If you’re using a decent grid system, it does this. That’s a good thing. Flint does this too. What Flint can also do is accomplish this repetitive task for you. You see, under the hood Flint is massively complex. When you tell it to create a span, it outputs what you need, but behind the scenes it also creates a log of what you just did and the variables involved in that task.

At best, that was probably confusing, so let me clarify. Flint has a nifty little debug-mode, which will help you visualize what we’re talking about here by outputting all of these logged variables. So, let’s enable the debug mode and take a look. I’m also going to switch to a fixed grid, for readability reasons — I don’t think anybody wants to try to read percentages.

$flint: (
    "config": (
        // ...

        "debug-mode": true
    )
);

.foo {
    @include _(desktop, 12);
}

With debug mode enabled, outputs:

@media only screen and (min-width: 60.0625em) {
    .foo {
        width: 73.4375%;
        margin-right: 0.78125%;
        margin-left: 0.78125%;
        -flint-instance-count: 1;
        -flint-parent-selector: none;
        -flint-key: desktop;
        -flint-breakpoint: 80em;
        -flint-columns: 16;
        -flint-span: 12;
        -flint-output-width: 73.4375%;
        -flint-output-margin-right: 0.78125%;
        -flint-output-margin-left: 0.78125%;
    }
}

This is the log of variables Flint keeps track of throughout your stylesheet. Depending on the element, there might even be a few more. Notice the line -flint-parent-selector: none. It’s value is none because the selector .foo doesn’t have a parent. Let’s go ahead and give .foo a child:

.foo {
    @include _(desktop, 12);

    .bar {
        @include _(desktop, 12, "auto"); // Equivalent to: _(desktop, 12, 12)
    }
}

Output:

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

    .foo {
        width: 940px;
        margin-right: 10px;
        margin-left: 10px;
        -flint-instance-count: 1;
        -flint-parent-selector: none;
        // ... etc ...
    }

    .foo .bar {
        width: 920px;
        margin-right: 10px;
        margin-left: 10px;
        -flint-instance-count: 2;
        -flint-parent-selector: .foo::desktop;
        -flint-key: desktop;
        -flint-breakpoint: 1280px;
        -flint-columns: 16;
        -flint-span: 12;
        -flint-context: 12;
        -flint-output-width: 920px;
        -flint-output-margin-right: 10px;
        -flint-output-margin-left: 10px;
    }
}

Notice, .bar has a parent where .foo had the value none. How did we accomplish that? We passed in the context value of "auto". This tells Flint to find the context of the nested element automatically.

I probably don’t have to stress how helpful a feature like this is. Want to change the column span? Change it in a single place while Flint handles it everywhere else. Quick and effortless. Think this is too good to be true? Take a look at the -flint-context: 12 line. It’s the parent’s span value! Also, the child has adjusted it’s width according to the parent’s width. Pretty sweet, huh?

If you’d like to know more about this, I’ve previously written a short (well, kinda short) blog post on this feature in more detail called Fixing Fixed Grids.

Closing Thoughts

Like I mentioned earlier, this type of system isn’t for all workflows. Some require that you keep these various aspects separate, but others don’t. I hope by now you can see the benefits that Flint can provide when tackling the beast that is responsive design. If you’re interested in learning more, or helping out in the development of Flint, visit Flint’s home page, check out the project on Github or play around with it on Sassmeister.

Have a thought or two? I’d love to hear it in the comments!

Free JavaScript: Novice to Ninja Sample

Get a free 32-page chapter of JavaScript: Novice to Ninja

  • http://viii.in Vinay Raghu

    I love this option @include _(8 6 4 2); It looks so plain and simple. Great work Ezekiel :)

    • Ezekiel Gabrielse

      Thanks a lot man! Let me know if you build anything awesome with Flint. :)

  • sylphdesign

    Hi Ezekiel, have you tried Susy for RWD? and if you have, what would be the big differentiator between Flint and Susy in your opinion?

    • Ezekiel Gabrielse

      I’ve used Susy a few times. Differences I can think of… Susy integrates with an outside plugin called Breakpoint for media query management; while media query management comes baked into Flint. Flint has a debug mode, and can also calculate values based on parent element contexts automatically; both of these are features that, as far as I know, no other Sass grid system has.

      Susy does allow for a broader range of output styles, while Flint is more focused on a single output style (real margins for gutters), and doing that one style well. Apart from that, it really just comes down to syntax and configuration style. The output will likely be similar.

  • Matt Sims

    Having been a Susy user for a while now, this sounds like a great replacement and I’m looking forward to trying it out on my next project. Thanks Ezekiel!

  • http://www.tyssendesign.com.au John Faulds

    One problem I’ve had with SASS grid frameworks (e.g. Suzy) is removing the left margin/padding from the first block in a row and the right from the last. Does Flint have this problem? (Or have I just not been using the other frameworks in the right way?)

    • Ezekiel Gabrielse

      Flint uses margins (not padding), and defaults to uniform left/right margins. It has `$gutter` modifiers where you can do `$gutter: alpha` to remove the first, `$gutter: omega` to remove the last, as well as `$gutter: row` to remove both. Check out the documentation on GitHub for more arguments you can use.

      • http://www.tyssendesign.com.au John Faulds

        I’m not seeing any way to remove left from first and right from last gutters without assigning alpha & omega classes which seems to me is only going to work when you’ve got a single rows, not when you might have a grid of thumbnails. I suppose you could assign classes like first, second, third, fourth etc., but then those lose their meaning when the screen narrows and you only have two blocks per row instead of four.

        • Ezekiel Gabrielse

          You would need to use nth-child and nth-of-type pseudo classes to add those modifiers per breakpoint. Flint doesn’t handle that aspect.

  • Ezekiel Gabrielse

    I was actually saying that in order to handle media queries it can be paired up with Breakpoint, not that it’s a dependency. :)

  • http://kevinrajaram.com/ Kevin Rajaram

    I’m now looking into flint to start using for my projects and it looks very promising and fast! Can’t wait to start testing and using it :D