Categories:

Introduction to CSS Variables

Created: Mar 6th, 17'

Browser development these days seems squarely aimed at supplanting features currently afforded to us by popular libraries and extensions. ECMAScript 6 has pretty much sidelined jQuery, and what appears to be next in the crosshairs are CSS preprocessors such as SASS and LESS. Modern versions of Firefox and Chrome support CSS variables (also known as CSS Custom Properties) that let you define variables directly in your CSS, which can then be referenced anywhere in your stylesheet or even manipulated using JavaScript. The result is a CSS preprocessor on steroids, and one that runs natively in the browser to boot. With IE Edge expecting to support this feature soon as well, these are exciting times indeed to be a frontend developer. Lets see what CSS variables are all about in this tutorial.

The Basic Syntax

Using CSS Variables is a simple two step process:

  1. Define your CSS variable inside a selector using the syntax --myvariable. The selector determines the scope of the variable and where it is applicable following normal CSS inheritance and specificity rules. A CSS variable defined inside the :root selector for example is available to all lower level selectors (elements) in the document.
  2. Reference the CSS variable using the syntax var(--myvariable) as a replacement for a static CSS property value.

Lets see a basic example now and put the pedal to the metal!

/* Define CSS variables and scope */
:root{
	--maincolor: black;
	--secondarycolor: crimson;
}

/* Use CSS Variables */
body{
	background: var(--maincolor);
	color: white;
}

body p{
	background: var(--secondarycolor);
}

Here I've defined two CSS variables containing color values inside the :root selector. The selector the variables are defined inside sets their scope, with all descending selectors (elements) able to access these variables. Defining your CSS variables inside the :root selector basically makes them globally available. At this point the variables are not applied anywhere yet, laying dormant and ready to be used. Note that CSS variables are case sensitive unlike other CSS properties, so --maincolor and --Maincolor are considered two different variables.

To use a CSS variable, we access its value using the var() function, by passing the variable name into it. We then select the desired CSS property to utilize this variable's value.

You can even set one CSS variable's value to another CSS variable in whole or in part:

/* Define CSS variables and scope */
:root{
	--darkfont: brown;
	--darkborder: 5px dashed var(--darkfont);
}

/* Use CSS Variables */
div.container{
	color: var(--darkfont);
	border: var(--darkborder);
}

Cascade and Inheritance with CSS Variables

CSS Variables behave very much like other CSS properties in that their values cascade and inherit, unlike properties defined using a CSS preprocessor. The following demonstrates cascading at work with CSS variables:

root{
	--darkborder: 5px solid black;
}

body{
	--darkborder: 1px solid darkred;
}

img{
	border: var(--darkborder); /* img border will be 1px solid red */
}

Here I've defined the same --darkborder CSS variable twice inside two different selectors. Due to cascading rules, the one inside the BODY selector has a higher specificity, and wins out when used in the IMG element.

CSS Variables also inherit by default, so the value of a CSS property defined on a parent element trickles down to the children when used in those elements. We see this in the below example, where a UL border defined using a CSS variable on the UL element automatically gets applied to children ULs as well:

:root{
	--myborder: 5px solid orange;
}

ul{
	list-style: none;
	padding: 10px;
	margin: 0;
	border-left: var(--myborder);
}

ul ul{
	margin-left: 30px;
}

Output screenshot:


Click screenshot to see live example

Disabling inheritance

We can stop a CSS variable from being inherited at a certain level by setting it to the special value "initial" inside the desired selector. Doing so resets that property to essentially "empty" by default at that scope level. To disable inheritance for a CSS variable universally for example, we can do something like the following:

*{
	--somevar: initial; /* disable inheritance for --somevar variable everywhere */
}

Consider the next example, which disables inheritance of the --myborder variable at the UL UL level, so the variable applied at the UL level doesn't trickle down to its descendants:

:root{
	--myborder: 5px solid orange;
}

ul{
	list-style: none;
	padding: 10px;
	margin: 0;
	border-left: var(--myborder);
}

ul ul{
	--myborder: initial; /* reset --myborder variable */
	margin-left: 30px;
}

Output screenshot:


Click screenshot to see live example

Resetting CSS variables' values lets you work with a clean slate where there are multiple CSS authors at work on the page and the possibly of duplicate variable names and unintended inheritance exists.

Building Values using the calc() function

CSS variables also work with the calc() function so number values can be dynamic, bringing CSS variables closer to JavaScript variables:

:root{
	--bottomgap: 30;
}

h1{
	margin-bottom: calc(var(--bottomgap) * 1px)
}

h2{
	margin-bottom: calc(var(--bottomgap) * .5px) /* half of H1 gap */
}

Here I've set a CSS variable to simply a number, then used the calc() function to derive the bottom margins for the H1 and H2 elements so the later is half of that of the former element's. Inside the calc() function, to derive an actual unit (ie: pixels), we perform a multiplication operation on that unit, such as multiplying --bottomgap by 1px. Simply appending the unit to the end of a variable won't work:

h1{
	margin-bottom: calc(var(--bottomgap)px); /* doesn't work */
}

You can also simply set --bottomgap to an actual length to begin with, such as 30px, and calculate half of that for the H2 element by dividing it by 2 (the right hand side of a division operation must always be a number). See the calc() function for more details on the accepted syntax.

CSS Variables and JavaScript

CSS variables can even be accessed and set using JavaScript, simplifying the way CSS styles are manipulated, by merely changing CSS variable values. Here are the two JavaScript  methods to get and set a CSS variable's value, whether the property is directly defined on an element or inherited:

getComputedStyle(element).getPropertyValue('--varname') // get CSS variable value of an element, including any leading or trailing spaces
element.style.setProperty('--varname', 'newvalue') // set CSS variable of an element to new value

To access the :root element/selector in JavaScript, use document.documentElement. When the value of a CSS variable changes, the browser automatically repaints to reflect the change.  You can even set the value of one CSS variable to another, creating an interdependency between CSS values that could lead to interesting effects:

element.style.setProperty('--divheight', 'calc(var(--divwidth)/2)') // set one CSS property to the value of another

The following example creates a CSS bars clock that tells the current time, by updating CSS variables only using JavaScript. Each CSS variable is fed the current hour, minutes, or seconds of the day as a percentage of a full 24 hours, 60 minutes, or 60 seconds, respectively. For example, 6pm would translate into 6/24, or 25% for the hour field. We use those values to determine how much of the background pseudo element inside each bar to move (transform). Here is the result:

The only interaction JavaScript has with our CSS are the CSS variables themselves- CSS variables opens up a new, arguably cleaner way for JavaScript to manipulate CSS.

Setting CSS Variables inline

CSS variables can also be defined or set inline on an element, which lets you tweak an individual element's CSS variable value. Lets say you've set two CSS variables in your stylesheet to define the resting and hover background colors of links inside ULs. For a particular link, however, you wish the two colors to be different from the rest. Overwriting the default values for the two CSS variables inside the link would be one way to do this:

<a href="http://www.javascriptkit.com" style="--basecolor:#D8E6E7; --accentcolor:black">Home</a>

Here is a working demo of this:

Defining fallback values for when a CSS variable is undefined

When making use of a CSS variable with the var(--cssvariable) function, you can populate the function with a 2nd parameter, which become the fallback value in the event the proceeding variable is undefined. For example:

background: var(--primarybg, white); /* Normal value as fallback value */
font-size: var(--defaultsize, var(--fallbacksize, 36px)); /* var() as fallback value */

As you can see, the fallback value can itself be another CSS var() function, which in turn can contain another fall back value in the event that variable isn't defined.

CSS variable fallbacks obviously are only picked up by browsers that support CSS variables in the first place. To provide fallback values for browsers that don't support the feature, you could do something like the following:

background: white; /* background value for browsers that don't support CSS variables */
background: var(--primarybg, white);

Now everyone's happy!

Conclusion

CSS Preprocessors such as SASS has enjoyed widespread adoption amongst frontend developers, though their compiled nature inherently limits their capabilities. Unlike CSS Preprocessors, CSS variables are live, cascade and inherit like regular CSS properties, and can even be manipulated using JavaScript. But perhaps the greatest thing going for CSS variables is that they are supported natively by the browser, removing any technical barrier for even novice web developers to dive into.