Please Support - Ride for the child

I recently came across this fantastic article which describes how to automate with SASS to make multiple themes. As always, I set about carefully following the tutorial and creating my own code to help me understand it properly.
I had never actually done any looping in SASS so the tutorial proved harder than expected and I learnt lots in the process. This blog post breaks the examples down a little more and also covers some of the new SASS syntax I  encountered.

Theming

Whenever I hear the word themes I automatically think WordPress themes, but I think a “theme” in development is really quite a broad statement. Here are two examples of how I think a theme could exist in code.

<body class="theme theme--pink"></body>

.theme {
  background: red;
  width: 200px;
}

.theme--pink {
  background: pink;
}

// Potentially wrong but it's a theme for IE7?

<body class="lt-it8">

<ul><li>a list item</li></ul>

</body>

.li {
  display: inline-block;
}

.lt-it8 li {
  display: inline;
  zoom: 1;
}

The article focuses on the media object. To make things a bit different I’m going to focus on a sidebar which is inside a theme. Imagine a website with a sidebar which changed colour based on which theme it’s using. Normally a theme is activated to adding a class high up in the DOM, maybe on the <body> or <html> tag which in turn will overwrite the default styles because its specificity score is higher. The sidebar is normally white but if a theme is applied higher in the DOM it will change accordingly.

Something like this…

<sidebar class="sidebar"></sidebar>

.sidebar {background: white};
.theme--red .sidebar {background: red};
.theme--blue .sidebar--blue {background: blue};
.theme--black .sidebar--black {background: black};
.theme--green .sidebar--green {background: green};
.theme--yellow .sidebar--yellow {background: yellow};
.theme--brown .sidebar--brown {background: brown};

By simply writing these rules out I can apply them to my HTML and hey presto I have some theme styles. However this method could be cumbersome, especially if you’ve hundred of different themes with dozens of different rules. Instead we could create an SASS object which, when defined, simply writes out our theme CSS.

MAPS

Although they look like a JS object, SASS calles them Maps and they include key & value attributes. Lets have a look at an example of how to write a Map and how SASS can loop over it.

$theme: (
  red: red,
  blue: blue,
  black: black
);

@each $theme, $bgcolor in $themes {
  .theme--#{$theme} {
    background: $bgcolor;
  }
}

In the @each loop, I’m cycling over each key/value pair in $sidebars, assigning the key to $sidebar and the value to $bgcolor. Inside the loop, I generate the selector and add the background colour for each item. This gives me the following.

.theme--red {
background: red;
}

.theme--blue {
background: blue;
}

.theme--black {
background: black;
}

Codepen demo – http://codepen.io/anon/pen/MajVog

Back to the theming

So now that’s your aware of what SASS maps are and how you can loop through them, lets see how I can make some automated themes for my sidebar.

First I need to create my themes map. In the previous example each key only has only value. For my sidebar I need to determine both the background and color CSS attributes. For this I create a sub map containing keys for both the background and color attributes.

$sidebars: (
  red: (
    background: red,
    color: white
  ),
  blue: (
   background: blue,
   color: green
  ),
  black: (
    background: black,
    color: yellow
  )
) !default;

!default

If you know what this does then well done, I didn’t until I started writing this article.

“You can assign to variables if they aren’t already assigned by adding the !default flag to the end of the value. This means that if the variable has already been assigned to, it won’t be re-assigned, but if it doesn’t have a value yet, it will be given one.”

In our scenario it’s defining the $sidebars as the default, but they could be overwritten if something like this came into play in another file. This seem pointless to me as any SASS rule that precedes a previous rule will over write it. However with !default it seems you can overwrite those rules regardless of the order, Check out this demo.

Have a read of this article for more information (this is where the quote comes from).

Back to the theming again

So I have my sidebar themes in a Map so my next job would be to read the SASS map and automate the CSS in an @each loop. First I needed to know how to access the sub maps. I make use of the map-get function. The inner map-get grabs our primary map, then the last value base grabs that primary map’s base value. In the following example I access the color value of the blue sub-map.

h1 {
  color: map-get(map-get($sidebars, blue), color);
}

Codepen demo – http://codepen.io/anon/pen/bVwvYO

So now I can write the following code to output my automated sidebar rules.

@each $theme, $bgcolor in $themes {
    .theme--#{$theme} .sidebar {
        background: map-get(map-get($themes, $theme), background);
        color: map-get(map-get($themes, $theme), color);
    }
}

Demo – http://codepen.io/anon/pen/dYpmJz

Getting technical

That example is great but what I really need is to nest within SASS rules so that we can create themes on the fly when writing our SASS.

The article goes one step further and discusses 4 potential solutions in which rather than outputting all the CSS rules some mixins are created and used alongside a public API to access whatever is needed.

Creating a Mixin

To cut a long story short a mixin is..

“A mixin lets you make groups of CSS declarations that you want to reuse throughout your site”

If you want more information on those check this link.

I’m going to create a mixin which can be used for my sidebars. Mixins are able to take parameters, meaning that I can determine which key I want to access within my map. The example uses a private mixin to gather it all and then be used inside each subsequent mixin.

@mixin themify($property, $key, $sidebar: $sidebars) {
  @each $sidebar, $item in $sidebars {
    &.theme--#{$sidebar} {
      #{$property}: map-get($item, $key);
    }
  }
}

This is the themify mixin as per the example.
– $property – the CSS value we want to apply the rule to. (e.g colour, background)
– $key – the name of the key in our SASS map
– $sidebar: $sidebars – The Map of themes to use

Then inside our @each loop we have
– $sidebar – the key of our sub-maps
– $item in $sidebars – each key/values in our sub maps

We can then allocate the class with

&.theme--#{$sidebar}

Determine the CSS property (this will be passed via another mixin)

#{$property}

and search the $item for the key to return its value

map-get($item, $key);

I must admit the loop was quite complex to me until I broke it down like the above. An approach like this isn’t something I would do naturally, but it’s certainly good to know!

Now we have a private mixin we create “public API’s”. These basically gives the ability to make use of specific parts of the mixin. For example my apply a background color, colour or both.

@mixin color($argument) {
  @include themify('color', $argument);
}

Thinking back to parameters we are passing

– ‘color’ – $property – the CSS value we want to apply the rule to. (e.g colour, background)
– ‘color’ – $key – the name of the key in our SASS map

This is used in SASS like this

.theme {
  @include color('col');
}

Demo – I changed the map names to make things more obvious – http://codepen.io/anon/pen/ZbLoGE

The article then makes additional mixins for the background etc like this

@mixin background($argument) {
  @include themify('background', $argument);
}

.theme {
  @include background('bg');
  @include color('col');
}

Demo – http://codepen.io/anon/pen/meEgjd

 In practice

After that I have figured out how theming works in SASS I moved onto how it could be used in practice.

The most common method would be to simple switch a class on the body which would swap all the styles for the elements below it. Like this.

.box {
  background: red;
}

.theme--green {
  .box {
    background: green;
  }
}

I have seen people add additional stylesheets to overwrite the styles. However this means that you’re splitting your SASS up and re-writing rules twice.

You can make use of the really cool & symbol in SASS like this.

.box {
  background: red;
    .theme--green & {
      background: green;
  }
}

// Outputs
.box {
  background: red;
}
.theme--green .box {
  background: green;
}

The article add a little bit of code in order to allow things to be nested. The inside of the mixin looks like the following

&.theme-#{$theme},
.theme--#{$theme} & {
  #{$property}: map-get($item, $key);
}

An example of what this outputs

.theme {
  &__sidebar {
    height: 200px;
    width: 50%;
    @include background('bg');
  }
}

.theme__sidebar {
  height: 200px;
  width: 50%;
}
.theme__sidebar.theme-red, .theme--red .theme__sidebar {
  background-color: red;
}
.theme__sidebar.theme-blue, .theme--blue .theme__sidebar {
  background-color: blue;
}
.theme__sidebar.theme-black, .theme--black .theme__sidebar {
  background-color: black;
}

This outputs two classes, one can be used directly onto the element and the other can be used as parent class to change the elements below it. This seems a bit lame to me though, as we only really want one or the other?

Demo – http://codepen.io/anon/pen/KdaRZK

Enter Jordan

I made something that required  an additional argument to tell the mixin whether the rule was nested or not, and which class to apply. However, again this isn’t really the best solution.

The main man came up with this beast, have a play with it! It is dependent on BEM but offers the perfect solution to what I wanted.

Writing automated themes with SASS isn’t a easy process and the amount of work ultimately comes down to your exact requirements. I’m not sure if I will ever need to do this in a real project, but at least I know a little bit about it now. :-)