40. CSS Variables

May 31, 2019

I wrote this post for my newsletter, sign up here to get emails in your inbox.

I have been playing around a dark theme for my blog.

dark mode toggle

I really like the colors but it’s not as readable as the light mode yet 🤷‍♀️

Let’s keep colors and contrast aside, I want to talk about the implementation details in this post.

Let’s start simple, a blog has a few key style elements: The background, the text, a heading and links (amongst money others).

Here’s the default light version:

light theme

Let’s write the CSS for it:

body {
  background: ghostwhite;
  color: black;
h1 {
  color: darkred;
a {
  color: darkblue;

Did you there are hundreds of named colors in CSS? With cool names like ForestGreen, Firebrick and Chocolate.

Okay, now to create a dark mode for this, we will have to overwrite the styles spread across different selectors. Let’s say we attach the theme class on the html tag.

.dark-theme body {
  background: black;
  color: white;
.dark-theme h1 {
  color: pink;
.dark-theme a {
  color: deepskyblue;

Here’s what that looks like:

dark theme

Not bad right?

Now, practically speaking, you probably have more than 4 elements in your page and the styles for them are spread across different files.

It can be challenging to track the changes down and make sure you didn’t miss any element. And nothing’s worse than the wrong styles in the wrong theme:

oops wrong color

So, instead of spreading theme styles all across, we should keep them in one place. That’s a clean code guideline that I really like:

Things that change together, live together.

Okay enough build up, let’s get to it.


CSS Variables

CSS variables are the way to maintain values at one place without any preprocessing (sass/less).


The syntax is

body {
  --key: value;

  color: var(--key);

Cascading rules apply, so you can set variables in the parent element and use it in the child.

nav {
  --link-color: deepskyblue;
nav a {
  color: var(--link-color);

There’s even a way to set global variables.

:root {
  --background-color: white;

Before we move ahead, let’s talk browser support. As long as you don’t have to support IE11, you’re good to go.

Side note: I’m sorry if you have to support IE11. Have a look at your usage numbers and drop support if you can! You’re missing out on a bunch of new features that reduce flexibility like variables, flexbox, grid, etc.

Let’s apply this to our blog.

What I like to do is declare all the colors along with their purpose as global variables first. This makes sure I don’t miss any combinations.

:root {
  --light-background: ghostwhite;
  --dark-background: black;

  --light-text: white;
  --dark-text: black;

  --dark-heading-color: pink;
  --light-heading-color: darkred;

  --dark-link-color: darkblue;
  --light-link-color: lightblue;

Next up, define your themes.

Notice how we are using variables from the global color palette and exposing them with their purpose.

.light-theme {
  --background: var(--light-background);
  --text-color: var(--dark-text);
  --heading-color: var(--dark-heading-color);

.dark-theme {
  --background: var(--dark-background);
  --text-color: var(--light-text);
  --heading-color: var(--light-heading-color);

Now, my favorite and easiest part: Write element styles using the theme variables.

body {
  background: var(--background-color);
  color: var(--text-color);
h1 {
  color: var(--heading-color);
a {
  color: var(--link-color);

Notice how the elements themselves are unaware that themes even exist. This lets us change styles (and add more themes) without touching the elements.


If you have been using a extended styles language like sass or less, you’re probably thinking

“I’ve had variables since I was 5, what’s the big deal?”

The big deal is:

  1. You don’t need a preprocessing step any more, which is a tiny win.
  2. More importantly, you can change these properties in runtime!

You have access to these variables in the javascript layer of the application and you change them during runtime!

Go ahead, try it out:

You can trigger changes to the variables on the user’s request.

There are multiple use cases for this, the obvious ones are changing the theme and letting the user increase/decrease font-size.

It also opens up a new set of interactions that aren’t possible with just CSS, like reacting to DOM events.

function chaseMouse() {
  let root = document.documentElement

  document.body.addEventListener('mousemove', event => {
    const { clientX, clientY } = event
    root.style.setProperty('--mouse-x', clientX + 'px')
    root.style.setProperty('--mouse-y', clientY + 'px')
Try it! Click this button and then move your mouse. (desktop only)
--mouse-x: null --mouse-y: null.


Bonus: If you’re using an editor like Atom or VSCode, you’ll start seeing them while you write code!


And in the devtools of your browser!


I love it when your tools help you make the right choice!


Hope that was helpful on your journey


Want articles like this in your inbox?
React, design systems and side projects. No spam, I promise!