Organizing Multiple Theme Styles with Sass

Published:

Maintaining CSS code on a website with multiple themes can really be quite a handful, especially if there is more than one person working on the project at the same time.

It so happens that one of the projects I’m involved with requires multiple themes, and there is than one person working on the codes. Because of this, I needed make sure that the styles are coded in a way that they’re easy to understand and change.

Here’s an article on my thoughts on organizing theme styles.

Two Approaches To Theming

While researching for the project, I discovered that very little has been discussed about organizing theme information online. The only source I managed to find some ideas on theme management is on SMACSS (Scalable and Modular Architure for CSS) by Jonathan Snook.

In SMACSS, Jonathan recommends that all theme rules should be placed in a separate theme.css file.

I tried Jonathan’s approach and I didn’t really like how it was working out for me. Hence I went on my own exploration and I discovered another possible method.

Here’s my approach: all theme rules should be declared within modules whenever possible.

Which should you choose? Let’s take a look at both approaches and their pros and cons to help you with that decision.

SMACSS Approach: Separate Theme.css File

Just a quick recap, Jonathan recommends all theme rules to be placed in a separate theme.css file. If haven’t had a chance to read up on SMACSS yet, I strongly suggest you check that out. Jonathan has given some solid advice with regards to CSS architecture.

Okay back to topic. Here’s an example he gave on theme rules

Say you have a dialog module that needs to have a border colour of blue, the border itself would be initially defined in the module and then the theme defines the colour:

/* in module-name.css */
.mod {
  border: 1px solid;
}

/* in theme.css */
.mod {
  border-color: blue;
}

Jonathan further goes on to say that you could also take it further and clearly indicate which theme the styles belong to. I believe this is what he’s referring to.

// in theme.css
.blue .mod {
  border-color: blue;
}

Many use cases work with with Jonathan’s approach. One excellent use cases for this approach is for websites that allows users to choose their own themes as a personalization layer.

These themes could either be placed together in the theme.css file, or they could be separated into their respective theme-name.css files.

Another great use case is for a website like BBC, where different categories given slightly different styles to maintain the entire BBC experience. Each page could load a specific category-name.css.

However, this approach of organizing theme styles breaks the context between module styles and theme styles. Which means that it is easy to become confused when you return to the code at a later time. It can also be confusing for someone else to pick up from where you left off.

Media queries had exactly the same issue initially as well. In the past, most developers batch viewport styles together and throw them into a media query at the end of the stylesheet. Boy that was confusing.

A lot of developers that I know (me included) now scatter media queries into layouts and module styles instead of batching everything up at the end. The responsive css became so much easier to understand.

If its possible to put theme codes within module codes that makes it easy for everyone to understand, why not do that?

In summary,

Pros

  1. Potentially requires loading of lesser styles a theme can be specific to a single page.

Cons

  1. Requires more than one http request for CSS files.
  2. Breaks context between theme styles and module styles.

My Approach: Theme Styles are declared modules files

My suggestion is to declare theme styles at the same place where module styles are declared.

That school of thought may be a little unconventional and you might not have any idea how this works yet. Allow me to explain further in detail how I intend it to work.

In the nutshell, here’s what the final CSS might look like with two themes. If you’re writing with sass, the declaration of these styles should found within the same module-name.scss file.

.module {
  /* Other Properties ... */
}

.blue-theme .module {
  color: blue;
}
.red-theme .module {
  color: red;
}

Writing in this manners makes it exceptionally clear that colors of the .module needs to be changed according to the theme. It is easy to refer back and understand whats happening.

But what if we have a lot more themes and properties? That could become a nightmare if we wrote it plainly in CSS. There’s a method to overcome this with Sass.

My Approach With Sass

Say now you have five themes. Each theme has different colors for each property.

First of all, I would use a Sass Map to store all my theme variables so the code is kept as DRY as possible.

$themes: (
  theme1: (color: red),
  theme2: (color: orange),
  theme3: (color: yellow),
  theme4: (color: green),
  theme5: (color: blue)
);

The example is kept to only one variable to simplify the explanation. You can have as many properties as you want for each theme as long as you declare them.

The trick to using this map is with an @each loop.

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

The @each $theme, $map in $themes tell Sass to loop over the $themes map that was defined above.

On each loop, assign these values to $theme and $map respectively.

  • $theme - Theme name
  • $map - Map of all theme variables

You now use the map-get() function to get any theme variable from $map and output the correct property for each theme.

.theme1 { color: red; }
.theme2 { color: orange; }
.theme3 { color: yellow; }
.theme4 { color: green; }
.theme5 { color: blue; }

That’s nice and good, but just outputting the theme name wouldn’t be very beneficial since its still impossible to include them within module-name.scss and expect this all to work correctly.

We have to make a tiny change to the @each loop to make it helpful for a real world usage. The complexity gets a little bit higher so make sure you’re clear what this @each loop does before moving on!

.selector {
  @each $theme, $map in $themes {
    .#{$theme} & {
      // <--- Notice the & here!
      color: map-get($map, color);
    }
  }
}

The & refer to parent selectors and placing it after .#{$theme} tells Sass to output any parent selectors after the theme name.

.module-name {
  @each $theme, $map in $themes {
    .#{$theme} & { // <--- Notice the & here!
      color: map-get($map, color);
    }
  }
}

/* CSS */
.theme-name .module-name { color: red; }

Lets try this with an example that’s more real.

Say if you wanted to change the color for the h1 of the module when the theme changes, we can add the each loop within the h1 context:

.module-name {
  h1 {
    @each $theme, $map in $themes {
      .#{$theme} & {
        color: map-get($map, color);
      }
    }
  }
}

/* CSS */
.theme1 .module-name h1 { color: red; }
.theme2 .module-name h1 { color: orange; }
.theme3 .module-name h1 { color: yellow; }
.theme4 .module-name h1 { color: green; }
.theme5 .module-name h1 { color: blue; }

This output works great, and we can now include .theme-name anywhere before .module-name and ensure that the colors would turn out correctly!

Note that this can only be used since Sass v3.3.0rc3. The minimum stable version I verified this to work with is Sass v3.3.7. I haven’t personally gone lower to test it out yet.

Now that you understand how to use this approach, lets dive into its pros and cons.

When using this method, you keep context between module and theme styles, which makes it easier to understand when you come back to it at a later time. It makes it easier for any fresh pairs of eyes as well.

Another benefit is that the client only has to load one CSS file.

But because theme styles are repeatedly created in many modules, there is a potential of creating a very large CSS file, which can slow down rendering of the page.

In summary,

Pros

  1. Keeps context between module styles and theme styles
  2. Only requires one http request for CSS files

Cons

  1. Potentially a creates very large CSS file, which slows down rendering

Making My Approach Better?

After using it for a few times, I came to realize that constantly writing the full @each loop can be a little cumbersome.

I wanted to make it easier to use and I’ve tried adding converting it into a mixin, like so:

@mixin themify($themes: $themes) {
  @each $theme, $map in $themes {
    .#{$theme} & {
      @content;
    }
  }
}

Unfortunately, that doesn’t work at all. The $map variables are stuck within the local scope of the mixin, but @content forces you to use the global scope. Hence I can’t draw the theme variables out.

At this point, I’m currently at my wit’s end with any improvements for my approach. I’d love to find a way to improve this and would be glad if you have any suggestions!

Conclusion

We’ve gone through two different approaches to organizing theme styles. Which do you prefer? Try both out and let me know in the comments!

Want to become a better Frontend Developer?

Don’t worry about where to start. I’ll send you a library of articles frontend developers have found useful!

  • 60+ CSS articles
  • 90+ JavaScript articles

I’ll also send you one article every week to help you improve your FED skills crazy fast!