> tokens with a value of "!".
long-variable-reference-crash.html
test_variable_legal_values.html
variable-declaration-15.html
variable-declaration-24.html
variable-declaration-25.html
variable-declaration-26.html
variable-declaration-59.html
In addition, if the value of a custom property contains a ''var()'' reference,
the ''var()'' reference must be valid according to the specified ''var()'' grammar.
If not, the custom property is invalid and must be ignored.
Note: This definition,
along with the general CSS syntax rules,
implies that a custom property value never includes an unmatched quote or bracket,
and so cannot have any effect on larger syntax constructs,
like the enclosing style rule,
when reserialized.
Note: Custom properties can contain a trailing ''!important'',
but this is automatically removed from the property's value by the CSS parser,
and makes the custom property "important" in the CSS cascade.
In other words, the prohibition on top-level "!" characters
does not prevent ''!important'' from being used,
as the ''!important'' is removed before syntax checking happens.
variable-declaration-20.html
variable-declaration-23.html
For example, 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.
The values of custom properties,
and the values of ''var()'' functions substituted into custom properties,
are case-sensitive,
and must be preserved in their original author-given casing.
(Many CSS values are ASCII case-insensitive,
which user agents can take advantage of by "canonicalizing" them into a single casing,
but that isn't allowed for custom properties.)
variable-declaration-38.html
variable-declaration-39.html
Because custom properties can contain anything,
there is no general way to know how to interpret what's inside of them
(until they're substituted into a known property with ''var()'').
Rather than have them partially resolve in some cases but not others,
they're left completely unresolved;
they're a bare stream of [[css-syntax#tokenization|CSS tokens]] interspersed with ''var()'' functions.
This has some knock-on implications.
For example, relative URLs in CSS
are resolved against the base URL of the stylesheet the value appears in.
However, if a custom property like ''--my-image: url(foo.jpg);'' shows up in an "/a/style.css"
stylesheet,
it will not resolve into an absolute URL immediately;
if that variable is later used in a different "/b/style.css"
stylesheet
like ''background: var(--my-image);'',
it will resolve at that point to "/b/foo.jpg"
.
Guaranteed-Invalid Values
The initial value of a [=custom property=] is a guaranteed-invalid value.
The [=guaranteed-invalid value=] is, well,
guaranteed to be invalid.
If it ever appears in a property value,
then at [=computed value=] time
that property becomes [=invalid at computed-value time=].
Non-property contexts will define their own behavior for the [=guaranteed-invalid value=],
but it will always be "invalid" in some sense.
The [=guaranteed-invalid value=] serializes as the empty string,
but actually writing an empty value into a custom property,
like ''--foo:;'',
is a valid (empty) value,
not the [=guaranteed-invalid value=].
If, for whatever reason,
one wants to manually reset a [=custom property=] to the [=guaranteed-invalid value=],
using the keyword ''initial'' will do this.
Note: Other than invoking the [=initial value=]
of a non-registered [=custom property=],
the only way to create the [=guaranteed-invalid value=]
is by having an invalid [=arbitrary substitution function=].
Resolving Dependency Cycles
Custom properties are left almost entirely unevaluated,
except that they allow and evaluate the ''var()'' function in their value.
This can create cyclic dependencies
where a custom property uses a ''var()'' referring to itself,
or two or more custom properties each attempt to refer to each other.
For each element,
create a directed dependency graph,
containing nodes for each custom property.
If the value of a custom property prop
contains a ''var()'' function referring to the property var
(including in the fallback argument of ''var()''),
add an edge between prop and the var.
Edges are possible from a custom property to itself.
If there is a cycle in the dependency graph,
all the custom properties in the cycle
are [=invalid at computed-value time=].
variable-cycles.html
variable-declaration-30.html
variable-declaration-48.html
variable-declaration-49.html
variable-declaration-50.html
variable-reference-39.html
Note: Defined properties that participate in a dependency cycle
either end up with invalid variables in their value
(becoming [=invalid at computed-value time=]),
or define their own cyclic handling
(like 'font-size' using ''em'' values).
They do not compute to the [=guaranteed-invalid value=]
like custom properties do.
This example shows a custom property safely using a variable:
:root {
--main-color: #c06;
--accent-background: linear-gradient(to top, var(--main-color), white);
}
The '--accent-background' property
(along with any other properties that use ''var(--main-color)'')
will automatically update when the '--main-color' property is changed.
On the other hand,
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' are now [=invalid at computed-value time=],
and compute to the [=guaranteed-invalid value=]
rather than lengths.
It is important to note that
custom properties resolve any ''var()'' functions in their values at computed-value time,
which occurs before the value is inherited.
In general,
cyclic dependencies occur only when multiple custom properties on the same element refer to each other;
custom properties defined on elements higher in the element tree can never cause a cyclic reference with properties defined on elements lower in the element tree.
variable-declaration-51.html
variable-declaration-52.html
For example,
given the following structure,
these custom properties are not cyclic,
and all define valid variables:
The <one> 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''.
Using Cascading Variables: the ''var()'' notation
The value of a custom property can be substituted into the value of another property
with the ''var()'' function.
The syntax of ''var()'' is:
var() = var( <> , <>? )
The ''var()'' function is an [=arbitrary substitution function=].
variable-reference-07.html
variable-reference-08.html
variable-reference-09.html
variable-reference-10.html
variable-reference-17.html
variable-reference-20.html
variable-reference-21.html
variable-reference-22.html
variable-reference-23.html
variable-reference-24.html
variable-reference-25.html
variable-reference-28.html
variable-reference-29.html
variable-reference-31.html
variable-reference-32.html
variable-reference-33.html
variable-reference-34.html
variable-reference-35.html
variable-reference.html
variable-supports-01.html
variable-supports-02.html
variable-supports-03.html
variable-supports-04.html
variable-supports-05.html
variable-supports-06.html
variable-supports-07.html
variable-supports-08.html
variable-supports-09.html
variable-supports-10.html
variable-supports-11.html
variable-supports-12.html
variable-supports-13.html
variable-supports-14.html
variable-supports-15.html
variable-supports-16.html
variable-supports-17.html
variable-supports-18.html
variable-supports-19.html
variable-supports-20.html
variable-supports-21.html
variable-supports-22.html
variable-supports-23.html
variable-supports-24.html
variable-supports-25.html
variable-supports-26.html
variable-supports-27.html
variable-supports-28.html
variable-supports-29.html
variable-supports-30.html
variable-supports-31.html
variable-supports-32.html
variable-supports-33.html
variable-supports-34.html
variable-supports-35.html
variable-supports-36.html
variable-supports-37.html
variable-supports-38.html
variable-supports-39.html
variable-supports-40.html
variable-supports-41.html
variable-supports-42.html
variable-supports-43.html
variable-supports-44.html
variable-supports-45.html
variable-supports-46.html
variable-supports-47.html
variable-supports-48.html
variable-supports-49.html
variable-supports-50.html
variable-supports-51.html
variable-supports-52.html
variable-supports-53.html
variable-supports-54.html
variable-supports-55.html
variable-supports-56.html
variable-supports-57.html
variable-supports-58.html
variable-supports-59.html
variable-supports-60.html
variable-supports-61.html
variable-supports-62.html
variable-supports-63.html
variable-supports-64.html
variable-supports-65.html
variable-supports-66.html
variable-supports-67.html
The first argument to the function is the name of the custom property to be substituted.
The second argument to the function, if provided,
is a fallback value,
which is used as the substitution value when the value of the referenced custom property
is the [=guaranteed-invalid value=].
variable-declaration-08.html
variable-declaration-09.html
variable-declaration-10.html
variable-declaration-11.html
variable-declaration-12.html
variable-declaration-13.html
variable-declaration-22.html
In an exception to the usual comma elision rules,
which require commas to be omitted when they're not separating values,
a bare comma, with nothing following it,
must be treated as valid in ''var()'',
indicating an empty fallback value.
variable-declaration-07.html
variable-declaration-37.html
variable-reference-06.html
variable-reference-11.html
variable-reference-26.html
variable-reference-27.html
Note: That is, ''var(--a,)'' is a valid function,
specifying that if the ''--a'' custom property is invalid or missing,
the ''var()'' should be replaced with nothing.
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'';
that is, anything between the first comma and the end of the function is considered a fallback value.
The fallback value allows for some types of defensive coding.
For example,
an author may create a component
intended to be included in a larger application,
and use variables to style it
so that it's easy for the author of the larger application
to theme the component to match the rest of the app.
Without fallback,
the app author must supply a value for every variable that your component uses.
With fallback, the component author can supply defaults,
so the app author only needs to supply values for the variables they wish to override.
/* In the component's style: */
.component .header {
color: var(--header-color, blue);
}
.component .text {
color: var(--text-color, black);
}
/* In the larger application's style: */
.component {
--text-color: #080;
/* header-color isn't set,
and so remains blue,
the fallback value */
}
To [=resolve an arbitrary substitution function|resolve a var() function=]:
1. Let |result| be the value of the [=custom property=]
named by the function's first argument,
on the element the function's property is being applied to.
2. Let |fallback| be the value of the function's second argument,
defaulting to the [=guaranteed-invalid value=]
if it doesn't have a second argument.
3. If the [=custom property=]
named by the ''var()''’s first argument
is [=animation-tainted=],
and the ''var()'' is being used in a property that is [=not animatable=],
set |result| to the [=guaranteed-invalid value=].
4. Return |result| and |fallback|.
Variable Units
In addition to being referenced directly with the ''var()'' function,
custom properties can be referenced as custom units,
making it easy to use multiples of significant "base sizes" in a document,
perhaps established by a design system.
A [=dimension=] whose unit is a <> is a variable unit reference.
It has identical effects and restrictions to using ''var()'';
the unit name is the [=custom property=] being referenced.
The only difference is during substitution--
rather than just substituting the [=custom property=] value directly,
it substitutes as ''calc(X * (var(Y)))'',
where X is numeric component of the dimension,
and Y is the unit component of the dimension.
For example,
"fluid typography" sizes text according to the viewport size.
A very simple version of this can be created as:
@property --fem { /* "fluid em" */
syntax: "";
initial: 2vw;
inherits: true;
}
.fluid-type {
font-size: 1.2--fem;
/* equivalent to */
font-size: calc(1.2 * (var(--fem)));
}
More complex expressions can be used as well.
For example, fluid typography often wants to impose limits
on how much the size responds to the viewport,
to avoid degenerate situations
on very large or very small screens:
@property --fem { /* "fluid em" */
syntax: "";
initial: clamp(10px, 1vw + 1vh, 1rem);
inherits: true;
}
.fluid-type {
font-size: 1.2--fem;
/* Won't get smaller than 12px,
or larger than 1.2rem. */
}
As the [=variable unit reference=] is a [=custom property=] reference,
it can be overridden by setting the [=custom property=] normally.
This can be useful to specialize a component for a particular position on the page,
while still styling it generically:
@property --bs { /* block size */
syntax: "";
initial: 8px;
inherits: true;
}
.module {
margin-block: 1.5--bs;
border-block: .5--bs;
/* gives a vertical margin of 12px,
and vertical border of 4px */
}
.sidebar .module {
--bs: 6px;
/* Makes the components slightly more compact
in the sidebar, with a vertical margin of 9px
and a vertical border of 3px. */
}
Note: [=Variable unit references=] can't have fallback values,
so if the referenced [=custom property=] doesn't exist or is invalid,
the unit reference will be invalid as well.
Use ''@property'' to create a [=registered custom property=],
as the ''@property/initial''' value will instead be used
as the default.
Note: While [=variable unit references=]
clearly expect their referenced [=custom property=] to have a numeric value
(so that it's valid to substitute into a ''calc()''),
nothing enforces this.
Supplying a non-numeric value,
such as by using ''--fem: red;''
to override the initial value in the above examples,
will simply result in an invalid property after substitution,
like ''font-size: calc(1.2 * (red));''.
APIs
All custom property declarations have the case-sensitive flag set.
variable-definition.html
variable-invalidation.html
Note: Custom properties do not appear on a CSSStyleDeclaration object in camel-cased form,
because their names may have both upper and lowercase letters
which indicate distinct custom properties.
The sort of text transformation that automatic camel-casing performs is incompatible with this.
They can still be accessed by their proper name via getPropertyValue()/etc.
Serializing Custom Properties
Custom property names must be serialized
as the exact code point sequence provided by the author,
including not altering the case.
Note: For non-custom properties,
property names are restricted to the ASCII range and are ASCII case-insensitive,
so implementations typically serialize the name lowercased.
Specified values of [=custom properties=] must be serialized
exactly as specified by the author.
Simplifications that might occur in other properties,
such as dropping comments,
normalizing whitespace,
reserializing numeric tokens from their value,
etc.,
must not occur.
Computed values of [=custom properties=] must similarly be serialized
exactly as specified by the author,
save for the replacement of any ''var()'' functions.
variable-reference-shorthands-cssom.html
variable-reference-variable.html
For example, given the following properties:
--y: /* baz */;
--x: /* foo */ var(--y) /* bar */;
the serialization of the specified value of ''--x''
must be
"/* foo */ var(--y) /* bar */"
,
while the serialization of the computed value of ''--x''
must be
"/* foo */ /* baz */ /* bar */"
.
(Note that the leading whitespace on the value is automatically trimmed by the CSS parser;
it's not preserved here.)
This requirement exists because authors sometimes store non-CSS information in custom properties,
and "normalizing" this information can change it in ways that break author code.
For example, storing a UUID in a custom property,
like ''--uuid: 12345678-12e3-8d9b-a456-426614174000'',
requires the UUID to be echoed back out as written
when it's accessed by script.
This value is technically parsed by CSS as a series of adjacent numbers and dimensions.
In particular, the segment "-12e3" parses as a number, equal to -12000.
Reserializing it in that form,
as required by CSSOM in other contexts,
would fatally break the author's use of the value.
Changes
Changes Since the 16 June 2022 CR Snapshot
* Clarified that the comment-insertion can happen with 0+ comments between the original tokens, not just exactly 1.
* Clarified the transition behavior of custom properties, in a note
Changes Since the 11 November 2021 CR Draft
* Clarified that custom properties apply all pseudo-elements (including those with restricted property lists)
* Added example to illustrate issues with combining characters, ligatures, etc
* Strengthened wording around similar-appearing variable names that use distinct codepoint sequences
* Clarified an example by using more visually distinct languages as examples (English and Greek)
* Split Security and Privacy into separate sections
Changes Since the 03 December 2015 CR
* Now that [[css-syntax-3]] auto-trims whitespace from declaration values,
made <> optional in the custom property grammar,
so that empty variables are still allowed.
(Issue 774)
* Similarly, made empty fallbacks valid in ''var()''.
* The -- property is reserved for future use by CSS.
* Added concept of "animation-tainted",
to prevent non-animatable properties
from using a variable to smuggle in some animatability.
* Defined the [=guaranteed-invalid value=]
to make the initial value of custom properties
and the result of cycles or substitution failure
more straightforward,
and allow failure to propagate thru substitutions
until finally intercepted by a fallback.
* Defined that cycles trigger [=invalid at computed-value time=] behavior.
* Allowed variables to resolve to a CSS-wide keyword
(only possible by providing it as a fallback).
* Clarified that [=registered custom properties=] act like non-custom properties
when they're [=invalid at computed-value time=].
* Made longhands with ''var()''s also trigger their shorthands to be unserializable,
like longhands with pending-substitution values already did.
* Required UAs to defend against exponential substitution attacks.
* Defined how to serialize the values of custom properties
(previously, only the property name's serialization was specified).
Changes since the May 6 2014 Last Call Working Draft
* Serialization of longhands when shorthand uses a variable was defined.
* Link to DOM's definition of "case-sensitive".
* Added example of using variables with '':lang()'' to do simple i18n.
* Clarified that usage of ''var()'' in a custom property must be valid per the ''var()'' grammar.
Acknowledgments
Many thanks to several people in the CSS Working Group for keeping the dream of variables alive over the years,
particularly Daniel Glazman and David Hyatt.
Thanks to multiple people on the mailing list for helping contribute to the development of this incarnation of variables,
particularly
Brian Kardell,
David Baron,
François Remy,
Roland Steiner,
and Shane Stephens.
Privacy Considerations {#privacy}
===============================================
This specification defines a purely author-level mechanism for passing styling information around within a page they control.
As such, there are no new privacy considerations.
Security Considerations {#security}
===============================================
[[css-values-5#long-substitution]] calls out a long-standing Denial-of-Service attack
that can be mounted against "macro-expansion"-like mechanisms,
such as the ''var()'' function,
and mandates a defense against that attack.
revert-in-fallback.html
revert-layer-in-fallback.html
variables-animation-math-functions.html
variable-recalc-with-initial.html