Build a Style Guide

Building and maintaining a site can be a difficult, seemingly indefinite process. One particular pain-point can be ensuring a consistent look and feel throughout the site. A solution to this can be a style guide.

We've talked about some of the things you can do to improve your Sass, including making a cheatsheet of your variables — but you can take this one step further. Style guides aren't a new idea, but they can still be a useful one. The idea is that you create a dummy page on your site that shows commonly used aspects of your site (such as colors, headings, buttons, etc) and how they're styled. Building a color swatch, for example, can help you visualize your palette of colors to identify what color you should use in your new feature, and if there could be potential clashes.

This article is also a useful way to familiarize yourself with a few features in Sass and CSS that you may not know about. It's also a style guide for a site that uses Sass, and that introduces its own complexities. For example, not only do we want to display the computer value of a declaration, we also need to know if it's provided by a variable or function. Let's look at this now.

Before You Start: Module Preparation

As with a lot of our tutorials, you need to start by creating the basic module structure. In the directory where you keep your customizations, create the following folders:

  • StyleGuide@1.0.0
    • JavaScript
    • Sass
    • Templates

You'll need to create an ns.package.json file to include these three resources into the build, as well as update distro.json to register the module and include it in the JS and CSS for the shopping application.

The first part plan is to create a URL path which we can visit to see our styles in action. For this we need to create the entry point file and a router.

In JavaScript, create StyleGuide.js with the following in it:

define('StyleGuide'
, [
    'StyleGuide.Router'
  ]
, function (
    Router
  )
{
  'use strict';

  return {

    mountToApp: function(application) {
      return new Router(application);
    }

  }
});

For now this just registers the entry point and sets up the router, which we now need to make. Create StyleGuide.Router.js and put the following in it:

define('StyleGuide.Router'
, [
    'Backbone'
  , 'StyleGuide.View'
  ]
, function StyleGuideFormRouter(
    Backbone
  , View
  )
{
  'use strict';

  return Backbone.Router.extend({

    routes:
    {
      'styleguide': 'showStyleGuide'
    }

  , initialize: function initialize(application)
    {
      this.application = application;
    }

  , showStyleGuide: function showStyleGuide()
    {
      var view = new View({
        application: this.application
      });

      view.showContent();
    }

  });
});

Nothing too racy or anything you've seen before. Next, you'll notice we've referenced a view which doesn't exist yet, so let's set up the barebones version of that too.

Create StyleGuide.View.js and in it put:

define('StyleGuide.View'
, [
    'Backbone'
  , 'style_guide_view.tpl'
  ]
, function StyleGuideView
  (
    Backbone
  , styleGuideTpl
  )
{
  'use strict';

  return Backbone.View.extend
  ({

    template: styleGuideTpl

  });
});

And the final piece is the template we're referencing, so create style_guide.tpl in the Templates directory:

<h1>Here's where I'd put my style guide... if I had one.</h1>

And now we're good to go. Run gulp deploy to get the code up to the site. When it's done, visit your site and stick /styleguide on the end of the URL, you should see something like this:

If you have no errors and you're good to move on, let's do that and talk about what we're going to put into our style guide.

Color Palette

A color palette will give you a stark representation of all the colors you use on your site. It'll show you primary colors, secondary colors, button colors, background colors, and so on. The added benefit of this is that you can build it so that it not only shows the hex values for these colors, but also the variable names for them. Remember: one of the key benefits of Sass is that you set variable names for colors you're going to use in a particular role, over and over again, so only have to set them once.

What we need to do first is build up a list of all of our colors so that they can be presented on the template. All of the default colors are stored in BaseSassStyles > Sass > variables > _colors.scss. When working through this bit, you'll also have to take account of any other custom colors you have for your own site design.

What we can see when we look at this file is that there are four bits of useful information:

  1. The grouping of variables, as expressed by line breaks
  2. The name of the variable
  3. The specified value of the variable
  4. The translated value of the variable into hex

These four points together make for a difficult task. After all, we want to expose both the variable's raw value and its computed value. To get the raw value, we would need to pass it is as a string; to get the computed value, we would need to allow Gulp to process it, and then use JavaScript to return it from the frontend.

There's going to be some duplication here, but it'll be fine. What we're going to do is create some arrays, and then have Sass and Handlebars loop through each array, generating the right CSS and DOM elements for us.

Sass

Let's start by creating _style-guide-colors.scss. We need styling for the page, as well as styling for the blocks that will comprise the palette, so put in the following:

.styleguide-container {
  @extend .container;
}
.styleguide-row {
  @extend .row;
  margin:$sc-base-margin * 4 0;
}
.styleguide-block {
  margin: 0.5vw;
  border: 1px solid #ddd;
  height: 12vw;
  width: 12vw;
  float: left;
  p {
    background: rgba(255,255,255,0.5);
    text-align: center;
    font-size: 1.2vw;
  }
}

$themeColors: $sc-color-theme $sc-color-theme-light $sc-color-theme-background $sc-color-theme-background-light $sc-color-theme-background-lightest $sc-color-theme-border $sc-color-background-list;

@for $i from 1 through length($themeColors) {
    .styleguide-block-themecolors-#{$i} {
        @extend .styleguide-block;
        background-color: #{nth($themeColors, $i)};
        :after {
            content: ": #{nth($themeColors, $i)}";
        }
    }
}

So the first three declarations are just pretty standard things to set up the page and the blocks that we're going to use for the palette. (If you haven't noticed, I'm using vw as the units, they're more modern and something I like — change if you don't agree.) The other two are more interesting.

First, we're creating a list containing the first group of colors in _colors.scss. In Sass, we create lists like we would a variable, and separate the items with spaces.

Second, we're taking this list and looping through them with the @for directive. We're telling it to go through each one to the end of the list, and then generate declarations for each one. We use #{$i} to return the index position, and #{nth($themecolors, $i) to return the actual value in the list. This is a very useful part of Sass, which helps cut down on the amount of work we have to do by not manually repeating the same thing over and over.

We're also being clever and taking advantage of CSS pseudo elements to use the content property, to inject the translated hex value of the variable into the page. I have some reservations about using content but this is probably the quickest and easiest way to get this value. As CSS is there only for styling (and not content itself) using it this way is not good practice. In my defence, we're only building a style guide, so I'm going to let it slide for the sake of brevity.

So this generates the CSS, now we need to generate the DOM objects.

View and Template

In our template, we could just manually write out each object ourselves, but that's a waste of time. Instead, why don't we just use a similar loop in the template to generate them for us?

In StyleGuide.View.js add the following:

, getContext: function()
 {
  return {
   themeColors: ["","$sc-color-theme","$sc-color-theme-light","$sc-color-theme-background","$sc-color-theme-background-light","$sc-color-theme-background-lightest","$sc-color-theme-border","$sc-color-background-list"]
  };
 }

This is pretty basic: it's just an array with the same data as the Sass file. However, I've added in an empty value at the start; why? Because, bizarrely, index counts in Sass start at 1, not 0 like JavaScript (and just about every other language). Thus, we add in an empty value to easily align the two arrays.

As you can probably tell, we're going to feed these to the template to loop through. Thus, open style_guide.tpl and replace its contents with:

<div class="styleguide-container">
  <h1>Style Guide</h1>
  <section class="styleguide-row">
    <h2>Theme Colors</h2>
    <ul>
      {{#each themeColors}}
      <li class="styleguide-block-themecolors-{{@index}}">
        <p>{{this}}</p>
      </li>
      {{/each}}
    </ul>
  </section>
</div>

We've got our #each set up, iterating through each of the items in themeColors. It generates each list item, adds the correct number to the class name, and also prints the variable name in the block.

Save and Test

Assuming you still have your local server running, save all the files and refresh. You should see something like this:

Once it's working, you can then go back and repeat the setup for each group of your colors:

  1. Create new arrays for each group of colors you want to add; for example, primary colors, messaging, buttons, etc
  2. Create new arrays in your views to match these
  3. Create new sections in your template for each of the groups

Indeed, as you iterate through all of the colors you want to include, you should start to see something like this:

You'll also notice that we are starting to repeat ourselves in the Sass file. If you're so inclined, you can rewrite it but it adds complexity. One solution would be to nest the arrays in one master array and then change the @for directive so that it iterates through each array and then each value in that array. It'll require some reworking of the templates I expect.

Typography

Let's move on to fonts and text. There isn't much cleverness to be taken advantage of here, instead you just need to go through your site and think about all the sorts of places that you use text.

What we want to do is include an example of that element, as well as its size.

A lot of elements already have default styling: all heading tags will, as well as those for paragraphs and links. Thus, simply putting these elements into your template will be enough to see working examples. From a styling point of view, we really only need to add in classes for the small text we use. However, we are going to add them because we need to send them the values of the font sizes so they can be printed.

In the Sass file, put the following:

$typography: (
    (h1 $sc-h1-font-size)
    (h2 $sc-h2-font-size)
    (h3 $sc-h3-font-size)
    (h4 $sc-h4-font-size)
    (h5 $sc-h5-font-size)
    (base $sc-base-font-size)
    (small $sc-small-font-size)
    (smallest $sc-smallest-font-size)
);

@each $label, $variable in $typography {
    .styleguide-typography-#{$label} {
        font-size: #{$variable};
        +p:after {
            content: ": #{$variable}";
        }
    }
}

So the first part of this is a little familiar: we're creating a list variable with all the things we want to iterate through. However, unlike the others we're using brackets. Brackets are optional in most cases when creating a list, and are mostly there for your benefit. However, they become crucial when you want to include values that contain a space or nest lists. The line breaks are also optional, but they help readability.

Yes, each line is a separate list in itself as each is wrapped in brackets. If we wanted to, we could create a variable for each one of those lists and then put it all together into list using their names. But we don't need to do that.

Next up, instead of using the @for directive, we're using @each. As you'll guess, it's another way to loop through an array (list) doing something for each value. The main reason to use @for in Sass is when it's important to know the index of the current item, which is irrelevant here.

What we're doing is taking each value in each list item, creating a class using the first value as a label, and the second to set the font-size and return it to a paragraph tag using the content attribute.

Superb stuff. Head back to your view and add in some new context properties:

, headingTypography: [
    {el: "h1", text: "Heading Level 1", var: "$sc-h1-font-size"}
  , {el: "h2", text: "Heading Level 2", var: "$sc-h2-font-size"}
  , {el: "h3", text: "Heading Level 3", var: "$sc-h3-font-size"}
  , {el: "h4", text: "Heading Level 4", var: "$sc-h4-font-size"}
  , {el: "h5", text: "Heading Level 5", var: "$sc-h5-font-size"}
  ]
, baseFont: "$sc-base-font-size"
, smallFont: "$sc-small-font-size"
, smallestFont: "$sc-smallest-font-size"
};

Nothing too crazy here, just some objects for the headings. Using this method, we need to have the element listed explicitly so we can use it in our template loop and class. We also want to include a string of text, so that we can demonstrate what we're talking about. As for the non-heading text, each is unique and it's just simpler to do each individually rather than forcing it into a loop.

In the template, add in the following:

{{#each headingTypography}}
    <section class="styleguide-row">
        <{{el}} class="styleguide-typography-{{el}}">{{text}}</{{el}}>
        <p>{{var}}</p>
    </section>
{{/each}}
<section class="styleguide-row">
    <p class="styleguide-typography-base">This is the base font size</p>
    <p>{{baseFont}}</p>
</section>
<section class="styleguide-row">
    <p class="styleguide-typography-small">This is the small font size</p>
    <p>{{smallFont}}</p>
</section>
<section class="styleguide-row">
    <p class="styleguide-typography-smallest">This is the smallest font size</p>
    <p>{{smallestFont}}</p>
</section>

Refresh your page running your local version of the styleguide, and you should see something like this:

So now we have examples of all of our site's typography as well as the variables used to size them, as well as the computed values. Neat!

Buttons

Another key aspect of the site are buttons. Buttons typically come in three different sizes (large, medium and small) and three different flavors (primary, secondary and tertiary).

At this point, I don't think there's much point in me telling you how to add these to your style guide, because you should have a pretty clear idea. Why not use this as an exercise to test what we've just learned and find the best way to present them? Keep in mind:

  1. For each button we will need to know what combination of size and flavor it is
  2. What useful properties, such as color, size and any variables will need to be made available on the frontend
  3. List the classes needed to generate a button that looks that way
  4. Consider grouping similar buttons to save space, keep in mind any complexities this could introduce

With that in mind, you should be able to construct a meaningful, useful and clever way to present your buttons.

Other Additions: Atoms and Molecules

If you have experience building an SCA site already (or you've perused our ace design guide) you'll know that we advocate a design principle of atoms > molecules > organisms > templates. If you so desire, you could start building up examples of atoms and molecules in your design guide to give yourself and your team a better idea of how they all look.

When doing so, think about whether there's benefit showing the individual atom or molecule out of context, or if there's benefit in showing all of its properties. In other words, it's beneficial showing tiles of colors because it'll give you a sense about what colors you should be using in your new elements and pages. It's also helpful to show different font sizes so you understand what's available and how each will look. But what about a form? Is this necessary? Buttons comes in a variety, so it's helpful to understand where each is appropriate, but can you repeat the above Sass functions (and accompanying JS and templating) for facets?

Thus, think about what additions are useful and feasible. It may be that you simply just want to use this page to add in discussion and decisions that you as a team have made about your brand.

Final Thoughts

Style guides provide you and your team with a visualization of key designs elements of your site. This is helpful so that you can keep in mind how things currently look and how they will look when you start your new feature. But they can also be practical by providing you with the code needed to make things appear, for example with the color palette.

One cool thing is along the lines of what Font Awesome do, which is to list all of their icons on one page with the class names next to them. You could implement that too.

There's plenty of resources online regarding style guides online. Indeed, one great source of inspiration for you may be the the Bootstrap style guide. While there's is clearly set up to showcase their entire library, there will certainly be things that you can identify as important for your site.

Another important part of this, I think, is to automate as much of it as possible. The more of the page that is automatically generated, the less time you spend maintaining it. For example, you could hard code a lot of this information into the page, but imagine what would happen if you change a color value, a variable, add a new element etc.

All in all, I think that this could be a useful exercise, not only in the results but also in the experimentation that you can do with the Sass directives.

More Information