CSS Variables — How To Use Custom Properties For Cascading Variables

A few years ago one of the more requested features in css was the ability to create variables. With so many repeated values across a stylesheet, the abstraction makes a lot of sense. Then along came preprocessors like Sass and Less and we had variables. The talk for wanting them directly in css quieted down.

The quiet hasn’t stopped variables from coming to css, though. Yes, variables are coming. Not quite yet, but they’re coming. Firefox nightly is currently the only browser to offer support, but variables are on the way. You’d think designers and developers would celebrate, but not everyone is happy about the news.

single digit numbers randomly displayed

Today I thought I’d run through how to use css variables as currently written in the spec. On Thursday I’ll talk about the objections some have to native css variables and why I disagree with those objections.

CSS Custom Properties

First things first. If you want to try any of this you need to grab a copy of Firefox Nightly. As I’m writing, it’s the only browser offering support. Again css variables aren’t anything we’re going to be using in production any time soon.

The css custom properties for cascading variables module is currently in the working draft stage. I believe the next stage is candidate recommendation. Normally I’d wait until we’re closer to being production ready, but I was taking a deeper look anyway and thought why not share.

Just keep in mind it’s early and a lot can still change. While I don’t expect it, for all I know everything below will be completely different by the time we’re using css variables in practice.

In Sass variables are declared with a $ as in $variable. In Less the @ sign is used as in @variable. To reference the variable in either language you use it in place of a value as in color: $color or color: @color. CSS variables are a little bit more complicated, but not too much.

In css they’re more than variables. They’re custom properties for cascading variables. To declare a custom property give it a name that begins with var- followed by anything you want.

  • var-color
  • var-background-color
  • var-font-size
  • and so on

The spec defines a custom property as var-*: <any-value> and that <any-value> can get interesting as we’ll see further down.

When declaring a custom property you’ll typically declare it on the :root selector.

1
2
3
:root {
    var-headline-color: #336;
}

To use the variable is also a bit different than it is for css preprocessors.

1
h1 { color: var(headline-color); }

The code above would set the color of an h1 to #336. Like I said, a bit more complicated than how Sass and Less deal with variables, but not much.

Let me try to clear up the custom property and variable thing. In the code above var-headline-color is the custom property. The variable name is headline-color, which gets referenced through the var(); notation. If that makes sense, great. If not, don’t worry too much about it. Something tells me everyone is going to refer to all of it as variables anyway.

One thing to note is that property names are case sensitive so var-color, var-Color, and var-COLOR are three different properties. The var must be lowercase so VAR-color is invalid.

You might be wondering why not just use $ or @, which are both easier to read. One reason is the notation above is more css-like. If you’re interested there are additional reasons which you can read about below.

Cascading Variables

You might have noticed the title of the spec mentions cascading variables. Above I set the variable on the :root element, which is what we’ll likely do much of the time. It means descendants of the root can access the variable. You can declare variables on more specific selectors instead.

1
2
3
:root { var-color: #000; }
div { var-color: #333; }
.box { var-color: #777; }

The first variable above would be available to every element. The second only to divs and their descendants. The last only to elements with a class of box and their descendants. The usual cascade and specificity rules determine which variable is used on a given selector.

For example if we have a div with a class of box in our html and in our css we set

1
div.box { color: var(color); }

the resulting color would be #777. If you’re unsure why, I’ll refer to you my post on css specificity and cascading rules.

Variables and Javascript

One interesting example shown in the spec involves setting <any-value> to something other than a simple value like a color or a unit of measurement.

1
var-foo: if(x &gt; 5) this.width = 10;

Not the most useful variable as the spec points out. It’s invalid as an actual value on any css property. However, it is something Javascript could read and act on. It also seems to indicate conditional logic might enter into css variables.

Composite Variables

Composite variables can be created by using other variables in the declaration.

1
2
3
4
:root {
 var-main-color: #c06;
 var-accent-background: linear-gradient(to top, var(main-color), white);
}

Here accent-background will always be a gradient from main-color to white. The value of accent-background would change as main-color changes.

Fallbacks

CSS variables can have fallbacks if the variable hasn’t been validly declared or declared at all.

1
2
3
.header { 
  color: var(header-color, blue); 
}

If var-header-color has been validly declared, it’s value will be used as the color for .header. If not, blue will be used.

An Example

Since I have my code editor open as well a Firefox Nightly installed and open, I thought I’d share a not so exciting example. As this only works in Firefox Nightly, I’ll present screenshots for what’s going on. First I set up some simple html

1
2
3
4
5
6
7
8
9
<body>
<p>Body</p>
<div class="container">
 <p>Container</p>
 <div class="box">
   <p>Box</p>
  </div>
</div>
</body>

Just one div inside another inside the body. Each div has a paragraph inside with some text to identify it. Next some default css to present everything

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
html {
 font-family: georgia;
 font-size: 16px;
}

body {
  margin: 0;
  padding: 0;
}

.container,
.box {
  max-width: 75%;
  margin: 0 auto;
  height: 100vh;
}

p {
  float: left;
  margin: 1em;
}

At this point all we have is three words on a white background, which isn’t particularly exciting. Let’s add a custom property and then use the variable.

1
2
3
4
:root {
 var-background-color: #393;
}
body { background: var(background-color); }

Assuming custom properties work we should see the same 3 words only this time on a green background, which is exactly what we see in Firefox Nightly. Still not all that exciting, other than to prove that variables work.

example: 3 containers all with a green background color

Let’s add another custom property, this time on the .container div and then use it to set another background color. I’m placing them together, but more likely you’d set the custom property in one location and use the variable in another.

1
2
3
4
.container {
 var-background-color: #339;
 background: var(background-color);
}

You can see the background behind the body is still green and now the background behind the container is blue. Even though property and variable have the same name for both the body and the container div, each element knows which value to use and it’s based on the rules of the cascade.

example: background behind the body is green, while the background behind the other containers is blue

Let’s call the variable on the .box div without setting up a new custom property for it. What color background do you expect the .box div to have?

1
2
3
.box {
 background: var(background-color);
}

It’s inside .container so it inherits the custom property set on .container and use the same blue background. The screenshot is the same as the one directly above so I won’t show it again.

What if we switch the name of the variable on .container?

1
2
3
4
.container {
 var-container-background: #339;
 background: var(container-background);
}

The backgrounds on the body and .container remain the same, but now .box inherits from the body and gets a green background.

example: body and .box divs with green background, while .container div has a blue background

Let’s change the .container div one more time and set the custom property back to background-color, but call the variable container-background. Let’s also provide a fallback when we call the variable.

1
2
3
4
.container {
 var-background-color: #339;
 background: var(container-background, #933);
}

Now .container is trying to use an invalid variable so it gets a reddish fallback color (#933). The .box div inherits the custom property set on .container though, and shows a blue background instead of the green that’s on the body.

example: containers with background colors of green for the body, red fot the container div, and blue for the .box div

One last example. This time we’ll build up the custom property on the .container div using the var-background-color property set on the :root.

1
2
3
4
.container {
 var-container-background: -moz-linear-gradient(to top, var(background-color), #933);
 background: var(container-background, #933);
}

Because .container is a descendent of :root it can inherit the background-color variable. We use the variable as one of the values inside a linear gradient, which gets set as a the value of a new custom property. The .box div is still set to use background-color, which it inherits from :root and the result is the following.

example: body and .box div showing a green background, while the .container div shows a red to green gradient as a background

Again this is a rather simple example, but hopefully it illustrates some of the things you can do with custom properties and variables in css.

If you’d like to see more information and examples you can check the links below.

Closing Thoughts

Just as we’re getting used to variables in preprocessors, Firefox Nightly has gone and supported css variables. Native css variables are a little more complicated in their syntax, but hardly much and the slight bit of complication makes them more flexible than their preprocessor cousins.

It’ll be awhile before anyone is using css custom properties in production, but you can certainly give them a try today. They’re very easy to work with.

At the start of this post I said not everyone is happy that variables are coming to css. Both Jeremy Keith and Chris Coyier have posted they don’t think css variables are necessary given we have them in preprocessors and both think adding variables will make css a less approachable language for beginners.

I strongly disagree, but we’ll get to that on Thursday.

Download a free sample from my book, Design Fundamentals.

2 comments

  1. We should all stop calling Sass/Less ‘variables’ variables. They’re not variable. They’re constants. That’s why CSS developers aren’t really programmers ;)

    • Well I would think the reason css developers call them variables is because that’s the name given to them in Sass.

      If I’m not mistaken you can assign them different values inside mixins and globally as well. Wouldn’t that make them variables and not constants?

Leave a Reply

Your email address will not be published.

css.php