CSS Properties

CSS Properties are fairly new. Chrome 49, Firefox 42, Safari 9.1, and iOS Safari 9.3 support custom variables. They are incredible for setting up themes at runtime when coupled with a little bit of JavaScript.

Adding Custom Properties

Custom properties must be prefixed with two dashes. To demonstrate, lets add a custom property for our main and background colors:

:root {
  --main-color: red;
  --bg-color: #fff;
}

We’ve set these colors on the document’s root element. To use a custom property, we need to use var(). Let’s say that we wanted to replace the background color for our body. We could do that with the following:

body {
  background-color: var(--bg-color);
}

Changing A Property

With JavaScript, we can update a property to anything we’d like using setProperty. Here’s an example that changes our main color to blue:

document.body.style.setProperty('--main-color', 'blue')

The downside to custom properties is that they allow you to write invalid CSS. If you are using --main-color in places that expect a color but you inadvertently set it to 1.25rem, it wouldn’t alert you that it’s invalid.

To learn more about CSS custom properties, I strongly recommend reading Google’s article on it.

Declaring Themes

Now that we know about CSS custom properties, we can shift to the code that enables theme switching. To declare a theme, I used a basic JavaScript Object to match properties to values.

const LightTheme = {
  '--main-color': '#1b70de',
  '--bg-color': '#FFF',
  '--text-color': '#000',
  '--button-color': 'rgba(0, 0, 0, 0.8)',
  '--header-color': '#424242',
  '--metadata-color': '#828282',
  '--table-border-color': '#cccccc',
  '--table-header-color': '#e8e8e8',
  themeName: 'LightTheme',
}

Once you declare a theme, you should also make another version with alternate values.

Toggle Theme

Updating code at runtime is made easy by JavaScript’s Object.entries, we can traverse the object’s keys and values to save to our stylesheet.

const updateTheme = (theme) => {
  Object.entries(theme).forEach(([key, value]) => document.body.style.setProperty(key, value))
}

When this is ran, anything that is using a var(--custom-property) will immediately update to the new color.

Persistence

An issue with our approach is that the selected theme is lost when a user navigates away from the page. To remedy this, I utilized localStorage to persist the current theme.

Saving to localStorage

Saving to localStorage provides two methods: setItem(key, value) and a subscript method (localStorage['key'] = value). We also want to make sure we’re dealing with the same kind of object. To do this, we can use JSON.stringify to store our theme as a JSON string. For good measure, we also need to save our current theme name.

const saveTheme = (theme) => {
  if (window.localStorage) {
    localStorage['theme'] = JSON.stringify(theme)
    localStorage['currentTheme'] = theme.themeName
  }
}

Loading Saved Theme

To pull the theme back out, we’ll use JSON.parse() to convert our JSON string to a JSON object.

if (window.localStorage) {
  const maybeTheme = localStorage['theme']
  if (maybeTheme) {
    const theme = JSON.parse(maybeTheme)
    updateTheme(theme)
  }
}

Next, we want to check if there’s a currently saved theme on every page load. What I’ve done is created an inline script that occurs at the end of a page load and attempts to load the saved theme that is persisted in localStorage:

<script>
  checkForSavedTheme()
</script>

Wiring

Now that we have a mechanism for using different themes, we need to wire up a toggle for users. I chose to add my toggle to the navigation bar.

We had previously saved the currentTheme’s name to localStorage for exactly this reason.

// First we need to look for the empty nav toggle
// I called mine #theme-switcher
const el = document.getElementById('theme-switcher')
 
// From here we need to check if we have a saved theme
const theme = loadSavedTheme()
// We'll also need the theme's name
const currentTheme = localStorage['currentTheme']
 
// We'll use a rudimentary if clause to check if theme and currentTheme is equal
if (theme && currentTheme === NightTheme.themeName) {
  updateTheme(LightTheme)
  // Here we set the FontAwesome classes for our toggle
  el.className = 'fa fa-moon-o'
} else {
  updateTheme(NightTheme)
  el.className = 'fa fa-sun-o'
}

What’s Next

With custom properties, we were able to make our page react to changes in CSS with a little JavaScript. In this post, I focused mostly on changing colors but you could also change it to any valid CSS 😬.