[Implementersâ doc]
Note: CSS variables are not and wonât be supported in IE11. You can either create a static stylesheet for all UA browsers or decide to leverage them in most UA browsers + use a JS fallback for IE11 if you want to support this browser â you can test for CSS variables support in JS.
CSS variables allow an author to assign arbitrary values to a property with an author-chosen name. This custom property must start with --
. The var()
function then allows the author to use those values elsewhere in the document.
:root {
--primaryColor: red;
}
a {
color: var(--primaryColor);
}
button {
color: white;
background-color: var(--primaryColor);
}
If youâre familiar with CSS pre-processors (LESS, SASS, Stylus, etc.), youâre probably using variables already. The major difference is that CSS variables are supported in the browser and, consequently, you can access and edit them in real-time using JavaScript.
Global references:
Custom properties follow standard cascading rules. In other words, you can define the same property at different levels of specificity.
:root {
--fontSize: 16px;
}
aside {
--fontSize: 12px;
}
body, aside {
font-size: var(--fontSize);
/* Will be 16px for body and 12px for aside */
}
You can also use them in media queries.
:root {
--defaultFontSize: 16px;
}
@media screen and (max-width: 560px) {
:root {
--defaultFontSize: 14px;
}
}
html {
font-size: var(--defaultFontSize);
/* Will be 14px if the viewport max-width < 560px and 16px otherwise */
}
To sum things up, variables bring a lot of flexibility and customization to CSS. It can also simplify your stylesheets and make them much more maintainable.
In case you want to use them, youâll have to hardcode values for IE11 (and Edge 14).
:root {
--primaryColor: red;
}
a {
color: red; /* Fallback */
color: var(--primaryColor);
}
button {
color: white;
background-color: red; /* Fallback */
background-color: var(--primaryColor);
}
That could be managed via JavaScript though, so that you donât need to maintain fallbacks in pure CSS.
It is important to note the var()
function provides a fallback mechanism itself, in case your custom property is not set.
The function looks like this:
var(<custom-property-name> [, <declaration-value> ]? )
:root {
/* No --primaryColor in there */
}
a {
color: var(--primaryColor, red); /* In which red is the fallback */
}
button {
color: white;
background-color: var(--primaryColor, red); /* In which red is the fallback */
}
That could be useful for theming for instance, by setting default values as fallbacks then declaring custom properties using JavaScript.
Although browsers have been optimized to deal with CSS variables, please note performance might suffer in some browsers if you have a lot of variables with a fallback.
As seen in this article by Michael Scharnagl, if you want to test for support, you can do the following:
if (window.CSS && window.CSS.supports && window.CSS.supports('--a', 0)) {
// CSS custom properties supported.
} else {
// CSS custom properties not supported
}
Since CSS feature queries are not supported by IE11, the previous snippet will return false.
It will return true for browsers which support feature queries since @supports
only checks if the syntax is correct (and donât bother checking if the custom property exists).
You can retrieve values by using getPropertyValue("--name_of_the_variable")
(it is a computed style).
var myColor = window.getComputedStyle(document.documentElement).getPropertyValue("--textColor");
You can set values by using setProperty("--name_of_the_variable", "value")
.
document.documentElement.style.setProperty("--textColor", "#000");
If youâre appending a style element in the head
of the document, you can also change the value in there, especially if you have a lot of elements to manage. For instance, if a --maxImageHeight
variable is defined in the CSS for img
, you could do the following:
var newStyles = document.createElement('style');
newStyles.id = "overrides";
var docHeight = window.getComputedStyle(document.body).getPropertyValue("height") + "px";
var newMaxImageHeight = document.createTextNode("img {--maxImageHeight: " + docHeight + ";}");
newStyles.appendChild(newMaxImageHeight);
document.head.appendChild(newStyles);
This will override the value in the original CSS since the img
selector is more specific. It is important to notice you could hardcode styles here for browsers which donât support variables (adding max-height: docHeight
to the textNode youâre appending in the stylesheet).
CSS variables offer a lot of flexibility.
As seen in this article by Zack Bloom, if you want to avoid managing concatenation (value + unit) in JavaScript, you could use the calc()
function in CSS.
.element {
height: calc(var(--element-height) * 1px);
}
As seen in this article by Eric Ponto, you could create a --breakpoint
variable to âemulateâ something like container/element queries.
body {
--breakpoint: medium;
}
@media screen and (max-width: 30em) {
body {
--breakpoint: small;
}
}
@media screen and (min-width: 50em) {
body {
--breakpoint: large;
}
}
Then create a function to return the breakpoint and append styles accordingly.
You could probably leverage such a hack to implement some kind of column-width
media query in paged view, but it would require CSS authors to provide specific stylesheets with this non-standard media as a link
attribute â otherwise, performance will be really really bad as you would have to hijack the entire stylesheet to retrieve media queries first.
As seen in the developers.google.com article, you can set the value of the variable to refer to another variable at runtime by using the var()
function in your call to setProperty()
.
document.documentElement.style.setProperty("--primary-color", "var(--secondary-color)");
As seen in the spec itself (example 3), you could create values which sole purpose is to be read and acted on by JavaScript.
While the following variable would obviously be useless as a variable (invalid value):
--foo: if(x > 5) this.width = 10;
You could use it in JavaScript since itâs still a valid custom property.
You could even go the extra mile and provide an âimport themeâ feature or implement custom styles inputs (color pickers, font family, etc.) in the UI so that users can create their own theme on the fly, then manage it more easily (storing the user theme as a style element with an id in localStorage
for instance).