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@propertyat-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-coloris 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@propertyat-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@propertyat-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-colorand uses a<named-color>value ofbrown:

css
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:rootpseudo-class, so that it can be referenced globally:

css
: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-colorwill be treated as a separate custom property to--My-color.

Using the@propertyat-rule

The@propertyat-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-colorwhich expects a<color>:

css
@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:

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

html
<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 elementscornflowerblueoraquamarinebackground colors:

css
/* 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-colorin the.containerscope and referencing its value in multiple places, the updated styles look like this:

css
/* 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:rootpseudo-class and using it where needed throughout the document, a CSS author can reduce the need for repetition:

css
/* 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@propertyalways inherits the value of its parent. This is demonstrated in the following example:

html
<divclass="one">
<p>One</p>
<divclass="two">
<p>Two</p>
<divclass="three"><p>Three</p></div>
<divclass="four"><p>Four</p></div>
</div>
</div>
css
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 way
  • class= "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@propertyto control inheritance

The@propertyat-rule lets you explicitly state whether the property inherits or not. The following example creates a custom property using the@propertyat-rule. Inheritance is disabled, there's a<color>data type defined, and an intital value ofcornflowerblue.

The parent element sets--box-colorto a value ofgreenand uses--box-coloras a value for its background color. The child element also usesbackground-color: var(--box-color),and we would expect it to have the colorgreenif inheritance was enabled (or if it was defined using the double dash syntax).

html
<divclass="parent">
<p>Parent element</p>
<divclass="child">
<p>Child element with inheritance disabled for --box-color.</p>
</div>
</div>
css
@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-colorproperty is not declared within the.childscope, the initial value ofcornflowerblueis used instead ofgreenthat would have been inherited from the parent:

Custom property fallback values

You can define fallback values for custom properties using thevar()function, and theinitial-valueof the@propertyat-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:

css
.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@propertyinitial value

Aside from usingvar(),theinitial-valuedefined in the@propertyat-rule can be used as a fallback mechanism. In fact, we've already seen this in the@propertyinheritancesection.

The following example sets an initial value of--box-colortocornflowerblueusing the@propertyat-rule. In the ruleset following the at-rule, we want to set--box-colortoaquamarine,but there's a typo in the value name. The same is true for the third<div>where we've used2remfor the custom property that's expecting a valid<color>value. Both2remandaqumarineare invalid color values, so the initial value ofcornflowerblueis applied:

css
@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 of16pxfor thecolorproperty), 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: bluerule is applied instead:

html
<p>This paragraph is initially black.</p>
css
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-colorin place ofvar(--text-color),but16pxis not a valid property value forcolor. After substitution, the property doesn't make sense., so the browser handles this situation in two steps:

  1. Check if the propertycoloris inheritable. It is, but this<p>doesn't have any parent with thecolorproperty set. So we move on to the next step.
  2. Set the value to itsdefault initial value,which is black.
html
<p>This paragraph is initially black.</p>
css
:root{
--text-color:16px;
}

p{
color:blue;
}

p{
color:var(--text-color);
}

For such cases, the@propertyat-rule can prevent unexpected results by allowing to define the initial value of the property:

html
<p>This paragraph is initially black.</p>
css
@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.

js
// 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);

See also