Getting to grips with CSS variables

🕑 2 minute read

If you have ever used JavaScript variables then the concept of CSS variables should feel pretty familiar.

  1. You set a variable
  2. You use the variable

CSS variables follows this same pattern, but the language is slightly different.

body {
    --my-variable: red;
}


h1 {
    color: var(--my-variable);
}

What is happening here? Firstly we set the variables - essentially they are custom properties. We use -- before the variable name to make it a variable. Then we use the variable by using var(--variable-name).

<body> <!-- --my-variable: red; -->
    <h1>CSS - Carl's Super Styles!</h1> <!-- colour: red  -->
</body>

Usually most variables are set on the pseudo element ::root so that every HTML element will be encompassed by it.

:root {
    --color: red;
    --size: 20px;
    --background: #222;
    --padding: 1em;
}

Scope of variables

CSS variables are scoped to their ancestors (parents, though grandparents to the root pseudo element).

Variables can be changed by child elements, but only apply further downstream in the DOM. That means that variables set in different branches of the DOM are only applicable to that tree. A bit like genetics, you only share the same genes from shared ancestors.

Changing a variable

:root {
    --my-variable: red;
}

body {
    --my-variable: blue;
}

h1 {
    color: var(--my-variable);
}

Here the root color of red will be overridden by the my-variable on the body, so the h1 ends up with blue text.

<!-- :root --my-variable: red; -->
<body> <!-- --my-variable: blue; -->
    <h1>CSS - Carl's Super Styles!</h1> <!-- colour: blue  -->
</body>

The Same variable in different trees

In the following example the root --color variable is black. We then set the classes of .red and .blue to red and blue respectively. Then we create a h2 to consume the --color variable.

:root {
    --color: black;
}

.red {
    --color: red;
}

.blue {
    --color: blue;
}

h2 {
    color: var(--color);
}

The h2 that is not wrapped by either one of red or blue inherits the value for the root color variable which is black. Both red and blue redefine the value of the variable and the h2 within their trees will be the relevant color.

<h2>Black</h2>

<div class="red">
    <h2>Red</h2>
</div>

<div class="blue">
    <h2>Blue</h2>
</div>

Media queries

In the pre variable days of css we did this:

.red { 
    font-size: 1em;
}

.blue { 
    font-size: 1em;
}

@media screen and (min-width: 32em) {
    .red  {
         font-size: 2em;
     }
    .blue  {
         font-size: 2em;
     }

}

With variables we can replace the hardcoded values with values set at the :root.

:root {
    --mobile-size: 1em;
    --desktop-size: 2em;
}

.red { 
    width: var(--mobile-size);
}

.blue { 
    width: var(--mobile-size);
}

@media screen and (min-width: 32em) {
    .red  {
         width: var(--desktop-size);
     }
    .blue  {
         width: var(--desktop-size);
     }
}

We can do one better and move the media queries to the root, cutting out the need to add extra code for developers to maintain. Media queries will all be managed in 1 place too!

:root {
    --font-size: 1em;
}

@media screen and (min-width: 32em) {
    :root {
        --font-size: 2em;
    }
}

.red { 
    width: var(--font-size);
}

.blue { 
    width: var(--font-size);
}

Final notes and thoughts

One of the important things to remember about CSS variables v1 is that the CSS spec states that var can only be used to set properties, so you cannot use them as part of a media query.

The var() function can be used in place of any part of a value in any property on an element. The var() function can not be used as property names, selectors, or anything else besides property values. (Doing so usually produces invalid syntax, or else a value whose meaning has no connection to the variable.)

That means that the following will currently not work.

@media screen and (min-width: var(--min-width-desktop)) {
  /* ... the content */
}

This is subject to change as later versions of the CSS spec are going to be rolled out in the future.

Even without the current ability to use variables as media queries values, the extra control gained by having reusable values for regular CSS properties is invaluable. With the vast majority of browsers supporting CSS variables then there really is no excuse not to use them in your daily workflow.

Tags