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:
- 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. - 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.