Building grid systems and theming engines is a neverending Sass story. There are probably as many Sass-powered grid systems as JavaScript frameworks nowadays, and pretty much everyone has their own way to deal with color themes in their stylesheets.

In today’s article, I’d like to tackle the latter issue and have a quick round-up of a couple of ways of building color schemes with Sass. But before going any further, what exactly are we talking about?

Let’s say we have a component, such as the classic media object. Now let’s say that we have different color schemes for this component, for instance a palette per category. For a category A, our object would have a red headline and a red-ish border, and for a category B, our object would have a more blue-ish color palette.

Finally, say you don’t have 2 themes but a dozen as well as a lot of color variations within the component. You could concede that managing this by hand would be highly impractical, and this is definitely the kind of thing you want to automate with a tool. In our case, that tool is Sass.

Okay, now we are ready. Although categories are kind of boring so let’s bring in some unicorns and dragons. Any of our approaches will rely on a map of themes. Each theme is a sub-map containing keys mapped to actual colors.

$themes : ( 'unicorn' : ( 'primary' : hotpink , 'secondary' : pink ) , 'dragon' : ( 'primary' : firebrick , 'secondary' : red ) ) !default;

Each of our themes consists of two colors: a primary one and a secondary one. We could have named them differently like alpha and beta, it does not matter. The point is only to be able to get “the main color from a theme” for instance.

How does the theming actually work?

There are different ways of course but usually, a theme is nothing but a class to which is bound some specific styles. This class can either be added to the body element to wrap the whole pages, or to specific components to themify a small module only, as we had considered just before.

All in all, in your stylesheet you will probably expect any of those two variations:

.theme-class .component { } .component.theme-class { }

The individual mixins approach

Let’s start with a simple one, and probably my favorite: the individual mixins approach. To put it simply, you have a couple of mixins named after the property they intend to styles. For instance, theme-color to themify the color or theme-background-color to themify the background.

Using those mixins will probably look like this:

.media { margin : 15px; padding : 15px 0; border-top : 5px solid; float : left; @include border-color('secondary'); /* 1 */ } .media__title { font-size : 1em; margin : 0 0 10px; @include color('primary'); /* 1 */ }

You have to agree this is quite elegant. Moreover, I feel like it is obvious, even without the comments.

Code

Let’s have a look at how to build this. We are not going to repeat the code logic inside each of those individual mixins, so we need a private mixin to gather it all and then be used inside each subsequent mixin.

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

And now, or public API:

@mixin color($arguments...) { @include themify('color', $arguments...); } @mixin border-color($arguments...) { @include themify('border-color', $arguments...); } @mixin background-color($arguments...) { @include themify('background-color', $arguments...); }

That’s it. Coming back to our previous example, here is what the CSS output would look like:

.media { margin : 15 px ; padding : 15 px 0 ; border-top : 5 px solid ; float : left ; } .media.theme-unicorn , .theme-unicorn .media { border-color : pink ; } .media.theme-dragon , .theme-dragon .media { border-color : red ; } .media__title { font-size : 1 em ; margin : 0 0 10 px ; } .media__title.theme-unicorn , .theme-unicorn .media__title { color : hotpink ; } .media__title.theme-dragon , .theme-dragon .media__title { color : firebrick ; }

Pros

Thanks to property-named mixins, the API is both clean and clear, even to a non-experienced developer.

Cons

This approach involves several mixins instead of one which might or might not be considered as extra complexity, even if most of them are just clones.

Because the colors are directly fetched from the color map based on their key, it is not possible to manipulate them with color functions such as lighten or mix . It might be doable with an extended version of the mixin though.

The block mixin approach

The block mixin approach uses a single mixin instead of several one and relies heavily on the usage of the @content directive. Using it would look something like:

.media { margin : 15px; padding : 15px 0; border-top : 5px solid; float : left; @include themify { border-color : $color-secondary ; / * 1 * / } } .media__title { font-size : 1em; margin : 0 0 10px; @include themify { color : $color-primary ; / * 1 * / } }

Code

The idea is simple: exposing the two colors as variables inside the themify mixin. The problem is that we cannot actually do this in a clean way. Variables defined in a mixin are not accessible to the content passed through @content as per the documentation:

The block of content passed to a mixin are evaluated in the scope where the block is defined, not in the scope of the mixin. This means that variables local to the mixin cannot be used within the passed style block and variables will resolve to the global value.

Because of this limitation, we have to hack our way around it. The work-around is actually not that complicated: before outputting @content , we define one global variable per color in the theme, and after outputting @content we unset those variables. This way, they are only accessible in the themify call.

$color-primary : null; $color-secondary : null; @mixin themify($themes: $themes) { @each $theme, $colors in $themes { &.theme-#{$theme} , .theme-#{$theme} & { $color-primary : map-get( $colors , 'primary' ) !global; $color-secondary : map-get( $colors , 'secondary' ) !global; @content ; $color-primary : null !global; $color-secondary : null !global; } } }

Our previous example would look exactly as with the first approach:

.media { margin : 15 px ; padding : 15 px 0 ; border-top : 5 px solid ; float : left ; } .media.theme-unicorn , .theme-unicorn .media { border-color : pink ; } .media.theme-dragon , .theme-dragon .media { border-color : red ; } .media__title { font-size : 1 em ; margin : 0 0 10 px ; } .media__title.theme-unicorn , .theme-unicorn .media__title { color : hotpink ; } .media__title.theme-dragon , .theme-dragon .media__title { color : firebrick ; }

Pros

Contrary to the individual mixins solution, the block mixin gives the ability to manipulate colors with functions since we have direct access to the colors stored in theme variables.

I feel like the API is still quick clear with this approach, especially since we use actual CSS declarations inside the mixin call, which might be easier to understand for some people.

Cons

The use of global variables in such a way is kind of hacky I must say. No big deal, but code quality is not really the plus side here.

The big ol’ theme mixin approach

This approach is actually something I have already written about here at SitePoint, in this article from last year. The idea is that you have a big ol’ mixin you update every time you need something to be themified.

This means that this mixin is the same for all components across the project. If you want to make this new component themified in some way, you need to open the file where the themify mixin lives and add a couple of extra rules in there.

@mixin themify($theme, $colors) { } @include themify; .media { margin : 15px; padding : 15px 0; border-top : 5px solid; float : left; } .media__title { font-size : 1em; margin : 0 0 10px; } @each $theme, $colors in $themes { @include themify($theme, $colors); }

Code

@mixin themify($theme, $colors) { .theme-#{$theme} { .media , &.media { border-color : map-get( $colors , 'primary' ); } .media__title &.media__title { color : map-get( $colors , 'secondary' ); } } }

Pros

Since the mixin content is manually maintained, it gives a high flexibility with selectors. You can pretty much do whatever you want in there.

For the same reason, it also gives the ability to manipulate colors with functions like darken .

Cons

Because everything themed is stored in this mixin’s content, it is not well suited for a modular approach. It is probably okay on small to medium projects with global stylesheets though.

It might be confusing to have component styles divided into several places, i.e. the module stylesheet and the theme mixin.

The class approach

The class approach is actually a DOM-driven one. The idea is that instead of applying specific theme styles from the stylesheet, you instead add theme classes to your markup such as border-color-primary . These classes, generated with Sass, do nothing on their own but apply some styles when used in combination with our eternal theme-$theme classes.

You can read more about this system in this article from Harry Roberts.

< div class = " media theme-unicorn border-color-primary " > < img class = " media__image " src = " https://lorempixel.com/100/100 " /> < h2 class = " media__title color-secondary " > This is the headline </ h2 > < p class = " media__content " > Lorem ipsum dolor sit amet, consectetur adipisicing elit. Provident nulla voluptatibus quisquam tenetur quas quidem, repudiandae vel beatae iure odit odio quae. </ p > </ div >

@mixin themify($themes: $themes) { } @include themify ($themes); .media { margin : 15px; padding : 15px 0; border-top : 5px solid; float : left; } .media__title { font-size : 1em; margin : 0 0 10px; }

Code

@mixin themify($themes: $themes) { $properties : ( 'border-color' , 'background-color' , 'color' ); @each $theme, $colors in $themes { @each $color-name, $color in $colors { @each $property in $properties { .theme-#{$theme} .#{$property}-#{$color-name} , .theme-#{$theme}.#{$property}-#{$color-name} { #{$property} : $color ; } } } } }

The output of this code would be:

.theme-unicorn .border-color-primary , .theme-unicorn.border-color-primary { border-color : hotpink ; } .theme-unicorn .background-color-primary , .theme-unicorn.background-color-primary { background-color : hotpink ; } .theme-unicorn .color-primary , .theme-unicorn.color-primary { color : hotpink ; } .theme-unicorn .border-color-secondary , .theme-unicorn.border-color-secondary { border-color : pink ; } .theme-unicorn .background-color-secondary , .theme-unicorn.background-color-secondary { background-color : pink ; } .theme-unicorn .color-secondary , .theme-unicorn.color-secondary { color : pink ; } .theme-dragon .border-color-primary , .theme-dragon.border-color-primary { border-color : firebrick ; } .theme-dragon .background-color-primary , .theme-dragon.background-color-primary { background-color : firebrick ; } .theme-dragon .color-primary , .theme-dragon.color-primary { color : firebrick ; } .theme-dragon .border-color-secondary , .theme-dragon.border-color-secondary { border-color : red ; } .theme-dragon .background-color-secondary , .theme-dragon.background-color-secondary { background-color : red ; } .theme-dragon .color-secondary , .theme-dragon.color-secondary { color : red ; }

It might looks like an awful lot of generated CSS, but this is something you can re-use several times throughout your whole project, so it actually is not that bad.

Pros

This solution has the benefit of being a DOM-based approach which turns out to be very interesting when having to manipulate themes on the fly with JavaScript. Indeed, it’s only a matter of adding/removing theme classes to/from elements; very handy.

While the output of the themify mixin might look big, it actually is a quite DRY approach since every themed color, border-color, background-color and whatelse is applied through these classes.

Cons

In some cases, a DOM-based approach might not be the correct way to proceed for instance when the markup is not hand-crafted (CMS, user-generated content…).

Final thoughts

I am sure I forgot maybe a dozen of other ways to apply theme styles with Sass, but I feel like these 4 different versions already cover a good area of the topic. If I get my choice we either go for the individual mixins approach, or the DOM-drive way with classes all the way if we want to go very modular (which is always good).

What about you, how do you do it?