Example code using CSS Custom Properties.
:root {
--main-bg-color: brown;
}
element {
--main-bg-color: brown;
}
element {
background-color: var(--main-bg-color);
}
The initial value of a custom property is an empty value; that is, nothing at all.
This is equivilant to setting background-color
to an invalid value.
In case an invalid value is assigned, the browser will:
- first check if there is an
inherited
value assigned to any parent elements it can use. Note: The property needs to be one that’s inherited by default, such as color, it would compute to the inherited value rather than the initial value. - assign it the browsers default
initial
value to use.
p {
background-color: var(--x);
}
Note directly assigning a non-valid value will cause a syntax error and the line will be ignored.
p {
background-color: 16px;
}
Note: If a property contains one or more var()
functions, and those functions are syntactically valid, the entire property’s grammar must be assumed to be valid at parse time. It is only syntax-checked at computed-value time, after var()
functions have been substituted.
.two {
/* Red if --my-var is not defined */
color: var(--my-var, red);
}
.three {
/*
pink if --my-var and --my-background are not defined.
Note this is known to cause performance issues as it takes a longer time to parses through the values.
*/
background-color: var(--my-var, var(--my-background, pink));
}
.three {
/* Invalid: "--my-background, pink" */
background-color: var(--my-var, --my-background, pink);
}
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);
The value may contain any character except some characters with special meaning like newlines, unmatched closing brackets, i.e. )
, ]
, or }
, top-level semicolons, or exclamation marks.
The prohibition on top-level "!" characters does not prevent !important
from being used, as the !important
is removed before syntax checking happens and makes the custom property "important" in the CSS cascade.
Note: While the value must represent at least one token, that one token may be whitespace. This implies that --foo: ;
is valid, and the corresponding var(--foo)
call would have a single space as its substitution value, but --foo:;
is invalid.
:root {
--somekeyword: left;
--somecolor: #0000ff;
--somecomplexvalue: 3px 6px rgb(20, 32, 54);
}
The following is a valid custom property:
--foo: if(x > 5) this.width = 10;
While this value is obviously useless as a variable, as it would be invalid in any normal property, it might be read and acted on by JavaScript.
A real-world example of custom property usage is easily separating out strings from where they’re used, to aid in maintenance of internationalization:
:root,
:root:lang(en) {--external-link: "external link";}
:root:lang(de) {--external-link: "externer Link";}
a[href^="http"]::after {
content: " (" var(--external-link) ")"
}
The variable declarations can even be kept in a separate file, to make maintaining the translations simpler.
This example shows an invalid instance of variables depending on each other:
:root {
--one: calc(var(--two) + 20px);
--two: calc(var(--one) - 20px);
}
Both --one and --two now compute to their initial value, rather than lengths.
Given the following structure, these custom properties are not cyclic, and all define valid variables:
<one><two><three /></two></one>
one { --foo: 10px; }
two { --bar: calc(var(--foo) + 10px); }
three { --foo: calc(var(--bar) + 10px); }
The element defines a value for --foo
. The <two>
element inherits this value, and additionally assigns a value to --bar
using the foo
variable. Finally, the <three>
element inherits the --bar
value after variable substitution (in other words, it sees the value calc(10px + 10px)
), and then redefines --foo
in terms of that value. Since the value it inherited for --bar
no longer contains a reference to the --foo
property defined on <one>
, defining --foo
using the var(--bar)
variable is not cyclic, and actually defines a value that will eventually (when referenced as a variable in a normal property) resolve to 30px
.
.foo {
--gap: 20;
margin-top: calc(var(--gap) * 1px);
}