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.
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.
--
)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 any valid 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 of brown
:
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
.
@property
at-ruleThe @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 works in the var()
function in place of a standard property value:
details {
background-color: var(--main-bg-color);
}
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:
<div class="container">
<div class="one">
<p>One</p>
</div>
<div class="two">
<p>Two</p>
<div class="three">
<p>Three</p>
</div>
</div>
<input class="four" placeholder="Four" />
<textarea class="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 elements cornflowerblue
or aquamarine
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);
}
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.
A custom property defined using two dashes --
instead of @property
always inherits the value of its parent.
This is demonstrated in the following example:
<div class="one">
<p>One</p>
<div class="two">
<p>Two</p>
<div class="three"><p>Three</p></div>
<div class="four"><p>Four</p></div>
</div>
</div>
div {
background-color: var(--box-color);
}
.two {
--box-color: cornflowerblue;
}
.three {
--box-color: aquamarine;
}
The results of var(--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.
@property
to control inheritanceThe @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 initial value of cornflowerblue
.
The parent element sets --box-color
to a value of green
and uses --box-color
as a value for its background color.
The child element also uses background-color: var(--box-color)
, and we would expect it to have the color green
if inheritance was enabled (or if it was defined using the double dash syntax).
<div class="parent">
<p>Parent element</p>
<div class="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);
}
Because inherits: 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 of cornflowerblue
is used instead of green
that would have been inherited from the parent:
You can define fallback values for custom properties using the var()
function, and the initial-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.
var()
functionUsing the Shadow 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 with var()
.
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 of custom properties, allows commas. For example, var(--foo, red, blue)
defines a fallback of red, blue
— anything between the first comma and the end of the function is considered a fallback value.
@property
initial valueAside from using var()
, the initial-value
defined in the @property
at-rule can be used as a fallback mechanism.
In fact, we've already seen this in the @property
inheritance section.
The following example sets an initial value of --box-color
to cornflowerblue
using the @property
at-rule.
In the ruleset following the at-rule, we want to set --box-color
to aquamarine
, but there's a typo in the value name.
The same is true for the third <div>
where we've used 2rem
for the custom property that's expecting a valid <color>
value.
Both 2rem
and aqumarine
are invalid color values, so the initial value of cornflowerblue
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);
}