I wrote a feature for Net magazine’s December issue about creating a living modular framework.

Net Magazine

Here it is.


While there are already many frontend frameworks available for free, it’s often preferable to write the code yourself. In this tutorial, we will explore how to use the Sass CSS preprocessor and the BEM methodology to write a ‘living framework’ that can be adapted to suit any development project.

Sass enables us to write CSS in small, easy-to-navigate modules.

Meaning we can start with base styles and bolt on any components we might need for the system we’re currently building.

We can also make our code easier to understand using well-constructed comments and clear naming conventions for our classes and variables. The BEM methodology gives us this clarity. BEM stands for Block-Element-Modifier and is designed to help modularise frontend development by breaking everything into blocks containing elements, then using modifiers to tweak them.

To apply the BEM methodology, we give each element within a block a class. Consider an unordered list:

<ul class="list">
    <li class="list__item">Item 1</li>
    <li class="list__item">Item 2</li>
    <li class="list__item list__item--end">Item 3</li>
</ul>

The unordered list has the block class list while each list item has the element class list__item. Only the final list item has the additional modifier class list__item—end. Each class follows the BEM syntax:

.BLOCK{__ELEMENT[--MODIFIER]}

Get Started

Set up the project by creating two directories: assets and static. The static directory will contain the compiled CSS and everything else that must be deployed, while the assets folder will contain any resources that don’t need to be deployed (in this instance, our Sass files).

With Sass installed on an environment, it’s necessary to ask it to watch for changes in the SCSS files and update the static CSS files accordingly. This is a pretty simple one-liner in a Terminal window:

sass --watch assets/sass:static/css

Sass should have created a screen.css file in our /static/css/ directory. Although at this point the file is empty, we will link to it from our HTML. (The assets/sass directory must exist for this to work).

Although we want our modules to be independent, there are always variables, functions and mixins that are used by multiple modules: for example, a mixin for outputting rems with a pixel fallback, or site-wide colour variables. With this in mind, we need to create _base.scss in our /assets/sass directory and include them there.

Using BEM as a basis for variable names will reduce the number of variables used and help other developers who might pick up the framework later on. By following this convention, we can add variables for colour and text size to our _base.scss file. For example:

$color__primary--light: #DD0000 !default;
$color__primary: #C10000 !default;
$color__primary--dark: #990000 !default;

$base__font-size: 15 !default;
$base__line: 20 !default;

And a simple function for calculating em sizes:

@function calc-em($target, $context: $base__font-size) {
    @return ($target / $context) * 1em;
}

Next, create _config.scss, which will later be used to override the default module variables, and screen.scss, the master Sass file. This will import the modules to be compiled into CSS for use on your website. Start by simply importing the base and config modules:

@import "base";
@import "config";

Rather than asking Sass to create multiple CSS files and linking each one into our HTML document, use the Sass @import directive to combine all the modules into one master CSS file. This reduces the amount of HTTP requests required, thereby improving performance.

Adding modules

Create a directory in /assets/sass called modules. This will house all the additional modules you add to your site for styling different forms of content. First off, add a CSS reset module (_reset.scss). Paste in the reset code of your choice and import it into the master file in the same way as before.

When you save the changes to the master file, notice that your Terminal process has detected changes and overwritten the screen.css file used by your web page. Opening the file, you can see the contents of the reset have been ported across.

At this point, you can begin to see the workflow in action. Being able to import snippets of code into a master file means we can easily toggle individual modules on or off as needed.

We can create a module called _text.scss and then start adding base styles for headers, paragraphs, and so on:

h1 {
    font-size: calc-em(36);
    line-height: (40/36);
    font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
    margin-bottom: calc-em(10, 40);
    font-weight: bold;
    color: $color__primary;
}

h2 {
    font-size: calc-em(24);
    line-height: (28/24);
    font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
    margin-bottom: calc-em(15, 40);
    color: $color__primary;
}

h3 {
    font-size: calc-em(20);
    line-height: (22/20);
    font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
    margin-bottom: calc-em(15, 40);
}

p {
    font-size: calc-em(15);
    line-height: (20/15);
    font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
    margin-bottom: calc-em(10, 15);
}

As there is quite a lot of repeated CSS here, we can abstract this out into variables and a mixin for text styling, reducing the amount of code used and making any future tweaks quicker.

$text__family-sans:  "Helvetica Neue", Helvetica, Arial, sans-serif !default;

$text__size--alpha: 36 !default;
$text__size--beta: 30 !default;
$text__size--gamma: 24 !default;
$text__size--delta: 15 !default;

$text__line--alpha: 40 !default;
$text__line--beta: 36 !default;
$text__line--gamma: 30 !default;
$text__line--delta: 20 !default;

$text__colour--base: #333333 !default;
$text__colour--alpha: #990000 !default;
$text__colour--beta: #990000 !default;
$text__colour--gamma: #333333 !default;
$text__colour--delta: #333333 !default;

@mixin text($size, $line, $margin: 0, $family: $text__family-sans) {
    font-size: calc-em($size);
    line-height: ($line/$size);
    font-family: $family;
    margin-bottom: calc-em($margin, $size);
}

html {
    font-size: $base__font-size * 1px;
}

body {
    @include text($base__font-size, $base__line);
}

h1 {
    @include text($text__size--delta, $text__line--delta, 15);
    font-weight: bold;
    color: $color__primary;
}

h2 {
    @include text($text__size--beta, $text__line--beta, 15);
    color: $text__colour--beta;
}

h3 {
    @include text($text__size--gamma, $text__line--gamma, 15);
    color: $text__colour--gamma;
}

p {
    @include text($text__size--delta, $text__line--delta, 10);
    color: $text__colour--delta;
}

The approach we’re using for naming colour variables may not be practical when trying to remember which colours are used in context to a design. To counteract this we have used module variables with more contextual names. Each variable is given a !default flag, which allows us to override them in our _config.scss file if we prefer:

$text__family-sans: "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif;
$text__size--beta: 30;
$text__line--beta: 36;

$text__colour--base: $color__neutral--dark;
$text__colour--alpha: $color__primary;
$text__colour--beta: $color__primary;
$text__colour--gamma: $text__colour--base;
$text__colour--delta: $text__colour--base;

You may ask what the point of declaring then overriding these variables is, and, for one-off use, there isn’t one. But what we have now is the beginnings of a modular framework that we can use as a basis for any frontend project, adding and importing more modules as we add more elements to our system.

Redeclaring variables simply reduces the need for dependency between modules.

And gives us a cleaner starting point for each new project.

Making code easier to understand

You could be forgiven for thinking that any developer could now pick up your work and understand italmost instantly, but that’s not always the case. Fortunately, there are ways to make your code even easier to understand. Firstly, Sass comments are not compiled, so will not add any weight to our compiled CSS file, so we can write as much as we like to be extra helpful. For instance, I always like to add comment blocks to mixins and functions.

Secondly, we can use the BEM methodology. For example, we could create a Sass module for lists where inheritance is obvious:

.list {
    padding-left: calc-em(10);
}

.list__item {
    margin-bottom: calc-em(10);
}

.list__item--end {
    margin-bottom: 0;
}

To add more bullets to this list, we could then create a modifier and include that class on the unordered list element:

<ul class="list list--bullet">
    <li class="list__item">Bulleted list item 1</li>
    <li class="list__item">Bulleted list item 2</li>
    <li class="list__item list__item--end">Bulleted list item 3</li>
</ul>
.list--bullet {
    list-style-type: disc;
    padding-left: calc-em(25);
}

Putting it all together

Using the styles we have created, let’s put together a very simple page based on one of my favourite subjects - the quiff:

<article class="layout__container">
<header>
    <h1>Hair styling for beginners</h1>
    <h2>Tips and tricks on creating a masterful quiff.</h2>
</header>
<p>Getting your hair to stand up, and stay up during a busy day in the office can be a bit of a hassle at times. But here&rsquo;s a full-proof method to achieving follicular greatness.</p>
<figure class="media media--full">
    <img class="media__item" src="http://static.fashionbeans.com/wp-content/uploads/2011/03/quiffmain1.jpg" />
    <figcaption class="media__caption">Young Elvis had a magnificent quiff.</figcaption>
</figure>
<h3>Requirements</h3>
<ul class="list list--bullet">
    <li class="list__item">Hair dryer</li>
    <li class="list__item">Styling putty</li>
    <li class="list__item">Finishing spray</li>
</ul>
<h3>Method</h3>
<ol class="list list--number list--method">
    <li class="list__item">Wash and condition your hair as normal.</li>
    <li class="list__item">Towel dry you hair until it is only slightly damp.</li>
    <li class="list__item">Rub a small amount of styling putty between the tips of your fingers and style your quiff.</li>
    <li class="list__item">Blow dry into shape, using a brush to pull your hair up.</li>
    <li class="list__item">Add shine and extra hold by applying some finishing spray.</li>
</ol>
</article>

Our HTML page needs a bit of styling, so we can go ahead and create additional modules for layout and media, while including additional styles for the number and method list modifiers.

The following code example shows how the modifiers in _lists.scss may look, with module-specific variables being included at the top. For example:

$list__number-color: #CC0000 !default;

.list--number {
    counter-reset: items;
    padding-top: calc-em(5);

    .list__item {
        margin-bottom calc-em(5);

        &:before {
            counter-increment: items 1;
            content: counter(items, decimal) ".";
            color: $list__number-color;
            margin-right: calc-em(5);
        }
    }

    .list__item--end {
        margin-bottom: 0;
    }
}

.list--method {
    @extend .list--number;

    .list__item {
        margin-bottom: calc-em(15);

        &:before {
            @include text($text__size--beta, $text__line--beta);
            font-style: italic;
        }
    }

    .list__item--end {
        margin-bottom: 0;
    }
}

There are examples of a layout and a media module in the Github repository that accompanies this tutorial, but I’ll let you be creative here and decide for yourself how to build them.

Module files
Self-contained module files set out base styles for content types. They’re used only when needed

Converting to ems

In this framework we’ve been using em sizing units throughout, which on smaller scale projects isn’t a problem. But issues surrounding compounding font-sizes can crop up when we nest HTML elements. To combat this, we can use rem (root em) units. To do this, we’d set a base font size on our HTML element. We already have a variable for this. For example:

html {
    font-size: $base-font-size;
}

Then we would write a function to calculate rems and use that in place or our original calc-em() function:

@function calc-rem($target) {
    @return ($target / $base_font-size) * 1 rem;
}

Rems are supported in later versions of all major browsers, but for legacy support, we can use a pixel fallback.

Going further

This is a very simple example, but the potential of a Sass and BEM-based framework is enormous.

Using the workflow outlined in this tutorial, you can create an essential building block that you can make use of for all your future projects, no matter how large they are, simply by adding modules as necessary.

It’s important to remember that while you are building one system, you should make the base module as simple, and therefore reusable, as possible. Specificity can then be added using the _config.scss file and BEM modifiers.


This article originally appeared in issue 249 of Net Magazine.