April 29, 2020

Everything I know about CSS Variables Custom Properties

Brain dump about CSS Custom Properties, AKA CSS Variables

They are not variables

They are actually called CSS custom properties. Huh.
We’ll refer to them as variables for brevity.

Declaring variables

To declare new variables, you just add a new property that begins with --, inside a ruleset:

:root {
  --color-main: #76fadd;
  --color-text: #282828;
}

What is :root?

If you notice, in the previous example, in the previous MDN link and other tutorials, you’ll see that variables are declared inside a :root, instead of html {...}, body{...} or whatever.

According to MDN, :root is a pseudo-class that matches the root of the tree representing the document. In this case, it’s identical to the html selector, except that its specificity is higher.

So why not use html then? Well, it’s perfectly fine.
CSS-Tricks explains this very clearly:

In an HTML document the html element will always be the highest-level parent, so the behaviour of :root is predictable. However, since CSS is a styling language that can be used with other document formats, such as SVG and XML, the :root pseudo-class can refer to different elements in those cases. Regardless of the markup language, :root will always select the document’s top-most element in the document tree.

https://css-tricks.com/almanac/selectors/r/root/

Using variables

.element {
  color: white;
  background: var(--color-main);
}

Inheritance

CSS Variables are inherited exactly like normal CSS properties. They are, after all, CSS properties and not variables. In the following Pen you can see how children-1 can override --color-bg declaration defined in it’s parent (.child), but parent can’t, since it has no access to it. --color-bg declaration is “below” parent it so it can’t cascade down to it.

Inheritance demo CodePen

Fallbacks

The first line of defense against undefined values is CSS’s cascading properties:

:root {
  --color-main: #76fadd;
  --color-text: #282828;
}
.element {
  background-color: #333333;
  background-color: var(--color-main); /* only interpreted by supported browsers*/
}

If the current browser doesn’t support CSS custom properties, it will ignore line 8 and apply a #333333 background. This is a very simple and isolated example, but it can be useful when some of these variables are generated and injected programmatically.

More importantly, fallback values work for browsers that do support custom properties. Fallback values are used when the given variable is not yet defined. The syntax is var(--variableName, defaultValue):

.element {
  background-color: var(--color-main, #333333);
}

If --color-main was not yet initialized or is by some reason invalid (maybe because it’s generated by user input), the background color of the element will be #333333.

Dark mode

One of the perfect use cases for CSS variables is obviously making a dark mode UI. Here’s a quick Pen on how to achieve that very quickly:

See the Pen Dark Mode with CSS custom properties in 5 minutes by Nacho Toledo (@iign) on CodePen.

Congrats, now you have to maintain and test two versions of every element in your site.

Dark Mode CSS queries

In 2019 browsers started introducing the prefers-color-scheme media query, in order to write CSS depending on the users’s OS setting. That means we can show a dark UI or tweak some styles for users that have a dark theme setting enabled on their OS:

:root {
  --color-bg: #fff;
  --color-text: #333;
}
@media (prefers-color-scheme: dark) {
  :root {
    --color-bg: #333;
    --color-text: #fff;
  }
}

Browser support

Support is pretty good overall:

  • Edge 16 (Oct 2017)
  • Firefox 31 (Jul 2014)
  • Chrome 49 (Mar 2016)
  • Safari 9.1 (Mar 2016)
  • Opera 36 (Mar 2016)

Work in progress

I’ll be updating this page soon. If this helped you clear up some thoughts or something seems off, let me know!

Useful links