Using CSS custom properties (variables)
Custom properties(sometimes referred to asCSS variablesorcascading variables) are entities defined by CSS authors that represent specific values to be reused throughout a document. They are set using the@property
at-rule or bycustom property syntax(e.g.,--primary-color: blue;
). Custom properties are accessed using the CSSvar()
function (e.g.,color: var(--primary-color);
).
Complex websites have very large amounts of CSS, and this often results in a lot of repeated CSS values. For example, it's common to see the same color used in hundreds of different places in stylesheets. Changing a color that's been duplicated in many places requires a search and replace across all rules and CSS files. Custom properties allow a value to be defined in one place, then referenced in multiple other places so that it's easier to work with. Another benefit is readability and semantics. For example,--main-text-color
is easier to understand than the hexadecimal color#00ff00
,especially if the color is used in different contexts.
Custom properties definedusing two dashes (--
)are subject to thecascadeand inherit their value from their parent.
The@property
at-rule allows more control over the custom property and lets you specify whether it inherits its value from a parent, what the initial value is, and the type constraints that should apply.
Note:Variables do not work inside media queries and container queries.
You can use thevar()
function in any part of a value in any property on an element.
You cannot usevar()
for property names, selectors, or anything aside from property values, which means you can't use it in a media query or container query.
Declaring custom properties
In CSS, you can declare a custom property using two dashes as a prefix for the property name, or by using the@property
at-rule.
The following sections describe how to use these two methods.
Using a prefix of two dashes (--
)
A custom property prefixed with two dashes begins with--
,followed by the property name (e.g.,--my-property
), and a property value that can be anyvalid CSS value.
Like any other property, this is written inside a ruleset.
The following example shows how to create a custom property--main-bg-color
and uses a<named-color>
value ofbrown
:
section{
--main-bg-color:brown;
}
The selector given to the ruleset (<section>
elements in the example above) defines the scope in which the custom property can be used.
For this reason, a common practice is to define custom properties on the:root
pseudo-class, so that it can be referenced globally:
:root{
--main-bg-color:brown;
}
This doesn't always have to be the case: you maybe have a good reason for limiting the scope of your custom properties.
Note:Custom property names are case sensitive —--my-color
will be treated as a separate custom property to--My-color
.
Using the@property
at-rule
The@property
at-rule allows you to be more expressive with the definition of a custom property with the ability to associate a type with the property, set default values, and control inheritance.
The following example creates a custom property called--logo-color
which expects a<color>
:
@property--logo-color{
syntax:"<color>";
inherits:false;
initial-value:#c0ffee;
}
If you want to define or work with custom properties in JavaScript instead of directly in CSS, there is a corresponding API for this purpose. You can read about how this work in theCSS Properties and Values APIpage.
Referencing custom properties withvar()
Regardless of which method you choose to define a custom property, you use them by referencing the property in avar()
function in place of a standard property value:
details{
background-color:var(--main-bg-color);
}
First steps with custom properties
Let's start with some HTML that we would like to apply some styles to.
There is a<div>
that acts as a container that includes some child elements, some with nested elements:
<divclass="container">
<divclass="one">
<p>One</p>
</div>
<divclass="two">
<p>Two</p>
<divclass="three">
<p>Three</p>
</div>
</div>
<inputclass="four"placeholder="Four"/>
<textareaclass="five">Five</textarea>
</div>
We will use the following CSS to style a few different elements based on their classes (some layout rules are not shown below so we can focus on colors).
Depending on their classes, we're giving elementscornflowerblue
oraquamarine
background colors:
/* For each class, set some colors */
.one{
background-color:cornflowerblue;
}
.two{
color:black;
background-color:aquamarine;
}
.three{
background-color:cornflowerblue;
}
.four{
background-color:cornflowerblue;
}
.five{
background-color:cornflowerblue;
}
This produces the following result:
There's an opportunity to use custom properties to replace repetitive values across these rules.
After defining--main-bg-color
in the.container
scope and referencing its value in multiple places, the updated styles look like this:
/* Define --main-bg-color here */
.container{
--main-bg-color:cornflowerblue;
}
/* For each class, set some colors */
.one{
background-color:var(--main-bg-color);
}
.two{
color:black;
background-color:aquamarine;
}
.three{
background-color:var(--main-bg-color);
}
.four{
background-color:var(--main-bg-color);
}
.five{
background-color:var(--main-bg-color);
}
Using the:root pseudo-class
For some CSS declarations, it is possible to declare this higher in the cascade and let CSS inheritance solve this problem. For non-trivial projects, this is not always possible. By declaring a custom property on the:root
pseudo-class and using it where needed throughout the document, a CSS author can reduce the need for repetition:
/* Define --main-bg-color here */
:root{
--main-bg-color:cornflowerblue;
}
/* For each class, set some colors */
.one{
background-color:var(--main-bg-color);
}
.two{
color:black;
background-color:aquamarine;
}
.three{
background-color:var(--main-bg-color);
}
.four{
background-color:var(--main-bg-color);
}
.five{
background-color:var(--main-bg-color);
}
This leads to the same result as the previous example, yet allows for one canonical declaration of the desired property value (--main-bg-color: cornflowerblue;
), which is very useful if you want to change the value across the entire project later.
Inheritance of custom properties
A custom property defined using two dashes--
instead of@property
always inherits the value of its parent.
This is demonstrated in the following example:
<divclass="one">
<p>One</p>
<divclass="two">
<p>Two</p>
<divclass="three"><p>Three</p></div>
<divclass="four"><p>Four</p></div>
</div>
</div>
div{
background-color:var(--box-color);
}
.two{
--box-color:cornflowerblue;
}
.three{
--box-color:aquamarine;
}
The results ofvar(--box-color)
depending on inheritance are as follows:
class= "one"
:invalid value,which is the default value of a custom property defined in this wayclass= "two"
:cornflowerblue
class= "three"
:aquamarine
class= "four"
:cornflowerblue
(inherited from its parent)
One aspect of custom properties that the examples above demonstrate is that they don't behave exactly like variables in other programming languages. The value is computed where it is needed, not stored and reused in other places of a stylesheet. For instance, you cannot set a property's value and expect to retrieve the value in a sibling's descendant's rule. The property is only set for the matching selector and its descendants.
Using@property
to control inheritance
The@property
at-rule lets you explicitly state whether the property inherits or not.
The following example creates a custom property using the@property
at-rule.
Inheritance is disabled, there's a<color>
data type defined, and an intital value ofcornflowerblue
.
The parent element sets--box-color
to a value ofgreen
and uses--box-color
as a value for its background color.
The child element also usesbackground-color: var(--box-color)
,and we would expect it to have the colorgreen
if inheritance was enabled (or if it was defined using the double dash syntax).
<divclass="parent">
<p>Parent element</p>
<divclass="child">
<p>Child element with inheritance disabled for --box-color.</p>
</div>
</div>
@property--box-color{
syntax:"<color>";
inherits:false;
initial-value:cornflowerblue;
}
.parent{
--box-color:green;
background-color:var(--box-color);
}
.child{
width:80%;
height:40%;
background-color:var(--box-color);
}
Becauseinherits: false;
is set in the at-rule, and a value for the--box-color
property is not declared within the.child
scope, the initial value ofcornflowerblue
is used instead ofgreen
that would have been inherited from the parent:
Custom property fallback values
You can define fallback values for custom properties using thevar()
function, and theinitial-value
of the@property
at-rule.
Note:Fallback values aren't used to fix compatibility issues for when CSS custom properties are not supported, as the fallback value won't help in this case. Fallbacks cover the case where the browser supports CSS custom properties and is able to use a different value if the desired variable isn't defined yet or has an invalid value.
Defining fallbacks in thevar()
function
Using thevar()
function, you can define multiplefallback valueswhen the given variable is not yet defined; this can be useful when working withCustom ElementsandShadow DOM.
The first argument to the function is the name of the custom property. The second argument to the function is an optional fallback value, which is used as the substitution value when the referenced custom property is invalid. The function accepts two parameters, assigning everything following the first comma as the second parameter. If the second parameter is invalid, the fallback will fail. For example:
.one{
/* Red if --my-var is not defined */
color:var(--my-var,red);
}
.two{
/* pink if --my-var and --my-background are not defined */
color:var(--my-var,var(--my-background,pink));
}
.three{
/* Invalid: "--my-background, pink" */
color:var(--my-var,--my-background,pink);
}
Including a custom property as a fallback, as seen in the second example above (var(--my-var, var(--my-background, pink))
), is the correct way to provide more than one fallback withvar()
.
You should be aware of the performance impact of this method, however, as it takes more time to parse through the nested variables.
Note:The syntax of the fallback, like that ofcustom properties,allows commas. For example,var(--foo, red, blue)
defines a fallback ofred, blue
— anything between the first comma and the end of the function is considered a fallback value.
Fallbacks using the@property
initial value
Aside from usingvar()
,theinitial-value
defined in the@property
at-rule can be used as a fallback mechanism.
In fact, we've already seen this in the@property
inheritancesection.
The following example sets an initial value of--box-color
tocornflowerblue
using the@property
at-rule.
In the ruleset following the at-rule, we want to set--box-color
toaquamarine
,but there's a typo in the value name.
The same is true for the third<div>
where we've used2rem
for the custom property that's expecting a valid<color>
value.
Both2rem
andaqumarine
are invalid color values, so the initial value ofcornflowerblue
is applied:
@property--box-color{
syntax:"<color>";
initial-value:cornflowerblue;
inherits:false;
}
.one{
--box-color:aquamarine;
background-color:var(--box-color);
}
.two{
--box-color:aqumarine;
background-color:var(--box-color);
}
.three{
--box-color:2rem;
background-color:var(--box-color);
}
Invalid custom properties
Each CSS property can be assigned a definedset of values. If you try to assign a value to a property that is outside its set of valid values, it's consideredinvalid.
When the browser encounters an invalid value for a regular CSS property (for example, a value of16px
for thecolor
property), it discards the declaration, and elements are assigned the values that they would have had if the declaration did not exist.
In the following example, we see what happens when a regular CSS declaration is invalid;color: 16px;
is discarded and the previouscolor: blue
rule is applied instead:
<p>This paragraph is initially black.</p>
p{
color:blue;
}
p{
/* oops, not a valid color */
color:16px;
}
However, when the values of custom properties are parsed, the browser doesn't yet know where they will be used, so it must consider nearly all values asvalid.
Unfortunately, these valid values can be used, via thevar()
functional notation, in a context where they might not make sense.
Properties and custom variables can lead to invalid CSS statements, leading to the concept ofvalid at computed time.
When the browser encounters an invalidvar()
substitution, then theinitialorinheritedvalue of the property is used.
This example is just like the last one, except we use a custom property.
The browser substitutes the value of--text-color
in place ofvar(--text-color)
,but16px
is not a valid property value forcolor
.
After substitution, the property doesn't make sense., so the browser handles this situation in two steps:
- Check if the property
color
is inheritable. It is, but this<p>
doesn't have any parent with thecolor
property set. So we move on to the next step. - Set the value to itsdefault initial value,which is black.
<p>This paragraph is initially black.</p>
:root{
--text-color:16px;
}
p{
color:blue;
}
p{
color:var(--text-color);
}
For such cases, the@property
at-rule can prevent unexpected results by allowing to define the initial value of the property:
<p>This paragraph is initially black.</p>
@property--text-color{
syntax:"<color>";
inherits:false;
initial-value:cornflowerblue;
}
:root{
--text-color:16px;
}
p{
color:blue;
}
p{
color:var(--text-color);
}
Values in JavaScript
To use the values of custom properties in JavaScript, it is just like standard properties.
// get variable from inline style
element.style.getPropertyValue("--my-var");
// get variable from wherever
getComputedStyle(element).getPropertyValue("--my-var");
// set variable on inline style
element.style.setProperty("--my-var",jsVar+4);