Theming with CSS Variables

Nate Baldwin
4 min readJan 14, 2017

If you’ve worked with themes in you web application, you know how difficult it can be. Usually you end up with multiple stylesheets and a script to swap them out.

We can do better than this.

Things get a little more complicated when you take a look at dynamic theming, user-specified theming and even component-specific or layout-specific theming. I’m going to look at this problem from the perspective of component/layout-specific theming, however this solution will work for each use-case.

Let’s say you’ve got a sidebar menu that uses colors similar to your ‘dark’ theme (as well as your header), whereas the rest of your application uses colors from your ‘light’ theme.

Example https://startbootstrap.com/template-overviews/sb-admin/

Instead of building out your sidebar pattern with unique colors, you may want to simply leverage your ‘dark’ theme. This way of thinking would enable you to theme at a component-level, but pulling in two (or more) full theme stylesheets is just ridiculous. Plus, there’s a lot of override nastiness that’s too foul to mention here.

Light themed sidebar looks better already.

Can’t we just have modular themes and atomic theming? That sounds pretty cool to me.

CSS Variables to the Rescue

I’m going to show you a super simple way to create lightweight themes with a single CSS file, as well as the ability to use multiple themes within a single page without any extra bloat.

Start with some shared globals — for example, we know we want the text and background colors to have a particular contrast. We will always define our colors in order of low-to-high contrast. So, base-1 is the lowest contrast (1:1), and base-4 is higher contrast. Pro tip: think in terms of contrast; not in terms of light or dark.

%theme {
background-color: var(--base-1);
color: var(--base-4);
}

We’ll share this declaration across all our themes. The themes allow us to declare the color palettes we’ll use:

.theme--light {
@extend %theme;
--base-1: #f2f2f2;
--base-2: #dadada;
--base-3: #aaa;
--base-4: #000;
}
.theme--dark {
@extend %theme;
--base-1: #000;
--base-2: #333;
--base-3: #999;
--base-4: #fff;
}

Now, we can apply modular theming by adding a class to any component or layout region we want to theme. What’s so cool about CSS Variables is that they are inherited by their children. Also, if children are given a class that declares new variable values, the child will inherit the new declarations, as well as any children of the child element.

Here’s a quick pen demonstrating how to apply themes using CSS Variables: https://codepen.io/NateBaldwinDesign/pen/Xpmqam

What’s good about this approach?

  • Colors are declared once, and do not need to be mapped or declared in multiple locations.
  • Components and regions are mapped to CSS Variables instead of hardcoded values.
  • Multiple themes can be used within a single page application
  • Themes can be used in regions or individual components (you choose how you want to do it)
  • Change theme in the browser by swapping classes
  • This approach can be used for user-specified theming and dynamic theming as well

Final Thoughts

Global theme swapping may need some additional tweaking. For example, if I have an app with the global theme of “light”, and I know that specific regions will need to be themed as either light or dark respectively, I may need to add some constraints to ensure they’re inherited.

So if I want my “dark” theme to be entirely dark (meaning no children are given the “light” theme’s colors), I may need to give a specific override.

.theme--dark {
--base-1: #000;
--base-2: #333;
--base-3: #999;
--base-4: #fff;

.theme--light {
--base-1: #000;
--base-2: #333;
--base-3: #999;
--base-4: #fff;
}
}

By nesting the themes, I’m able to ensure that the light theme (when applied as a child of the dark theme) is also styled as “dark”.

Also, this will only work if you disregard IE, Edge, and Opera Mini. *sigh*

--

--

Nate Baldwin

Design Systems @Adobe Spectrum. Intensity curious about color, visual perception, and the systemization of design.