Status: ED TR: http://www.w3.org/TR/css-variables/ ED: http://dev.w3.org/csswg/css-variables/ Editor: Tab Atkins Jr., Google, http://xanthir.com/contact Editor: Luke Macpherson, Google, macpherson@google.com Editor: Daniel Glazman, Disruptive Innovations, daniel.glazman@disruptive-innovations.com Abstract: This module introduces cascading variables as a new primitive value type that is accepted by all CSS properties, and custom properties for defining them.
<ident> [[!CSS3VAL]]
Custom properties are solely for use by authors and users;
CSS will never give them a meaning beyond what is presented here.
:root {
main-color: #06c;
accent-color: #006;
}
/* The rest of the CSS file */
#foo h1 {
color: var(main-color);
}
The naming provides a mnemonic for the colors,
prevents difficult-to-spot typos in the color codes,
and if the theme colors are ever changed,
focuses the change on one simple spot
(the custom property value)
rather than requiring tons of edits across all stylesheets in the project.
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. Further, the value of a custom property must retain its original author-given casing, unlike most CSS values which can be safely lower-cased (because most of CSS is case-insensitive in the ASCII range). (This requirement does not apply when a custom property's value is substituted into another property via a variable.)
var-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.
:root {
var-header-color: #06c;
}
declares a custom property named "var-header-color" on the root element,
and assigns to it the value "#06c".
This property is then inherited to the elements in the rest of the document.
Its value can be referenced via the "header-color" variable:
h1 { background-color: var(header-color); }
The preceding rule is equivalent to writing ''background-color: #06c;'',
except that the variable name makes the origin of the color clearer,
and if ''var(header-color)'' is used on other elements in the document,
all of the uses can be updated at once
by changing the 'var-header-color' property on the root element.
style attribute,
can be read or set using the CSSOM, etc..
:root { var-color: blue; }
div { var-color: green; }
#alert { var-color: red; }
* { color: var(color); }
<p>I inherited blue from the root element!</p>
<div>I got green set directly on me!</div>
<div id='alert'>
While I got red set directly on me!
<p>I'm red too, because of inheritance!</p>
</div>
:root {
var-main-color: #c06;
var-accent-background: linear-gradient(to top, var(main-color), white);
}
The 'var-accent-background' property
(along with any other properties that use ''var(main-color)'')
will automatically update when the 'var-main-color' property is changed.
:root {
var-one: calc(var(two) + 20px);
var-two: calc(var(one) - 20px);
}
Both 'var-one' and 'var-two' now define invalid variables rather than lengths.
<one><two><three /></two></one>
one { var-foo: 10px; }
two { var-bar: calc(var(foo) + 10px); }
three { var-foo: calc(var(bar) + 10px); }
The <one> element defines a value for 'var-foo'.
The <two> element inherits this value,
and additionally assigns a value to 'var-bar' using the ''foo'' variable.
Finally,
the <three> element inherits the 'var-bar' value
after variable substitution
(in other words, it sees the value ''calc(10px + 10px)''),
and then redefines 'var-foo' in terms of that value.
Since the value it inherited for 'var-bar' no longer contains a reference to the 'var-foo' property defined on <one>,
defining 'var-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''.
<variable> = var( variable-name [, <fallback> ]? )A variable can be used in place of any part of a value in any property on an element. Variables can not be used as property names, selectors, or anything else besides property values. (Doing so usually produces invalid syntax, or else a value whose meaning has no connection to the variable.) The <fallback> value is identical to the syntax of a custom property value. If the variable named by the first argument is valid, the variable's value is substituted as normal. If the variable is invalid, and a <fallback> was provided, the <fallback> is substituted instead. Otherwise, the result of evaluating the ''var()'' function will mean that the containing declaration is invalid at computed-value time.
/* 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 {
var-text-color: #080;
/* header-color isn't set,
and so remains blue,
the fallback value */
}
.foo {
var-side: margin-top;
var(side): 20px;
}
This is not equivalent to setting ''margin-top: 20px;''.
Instead, the second declaration is simply thrown away as a syntax error
for having an invalid property name.
Similarly, you can't build up a single token where part of it is provided by a variable:
.foo {
var-gap: 20;
margin-top: var(gap)px;
}
Again, this is not equivalent to setting ''margin-top: 20px;'' (a length).
Instead, it's equivalent to ''margin-top: 20 px;'' (a number followed by an ident),
which is simply an invalid value for the 'margin-top' property.
Note, though, that ''calc()'' can be used to validly achieve the same thing, like so:
.foo {
var-gap: 20;
margin-top: calc(var(gap) * 1px);
}
:root { var-looks-valid: 20px; }
p { background-color: var(looks-valid); }
Since ''20px'' is an invalid value for 'background-color',
this instance of the property computes to 'transparent'
(the initial value for 'background-color')
instead.
If the property was one that's inherited by default,
such as 'color',
it would compute to the inherited value
rather than the initial value.
:root { var-not-a-color: 20px; }
p { background-color: red; }
p { background-color: var(not-a-color); }
the <p> elements will have transparent backgrounds
(the initial value for 'background-color'),
rather than red backgrounds.
The same would happen if the variable itself was invalid.
Note the difference between this
and what happens if the author had just written ''background-color: 20px'' directly in their stylesheet -
that would be a normal syntax error,
which would cause the rule to be discarded,
so the ''background-color: red'' rule would be used instead.
CSSStyleDeclaration InterfaceCSSStyleDeclaration interface is amended as follows:
partial interface CSSStyleDeclaration {
attribute CSSVariablesDeclaration var;
}
While the CSSStyleDeclaration interface normally contains attributes that are camel-cased name variants of all CSS properties
(and sometimes also attributes for their canonical names),
it must not contain any such attributes for custom properties.
The camel-case trick does not work,
as custom property names are case-sensitive,
and there are potentially an infinity of custom properties,
which is incompatible with the normal behavior of exposing every property
whether it was set in the corresponding declaration block or not.
Ordinarily, property names are restricted to the ASCII range and are case-insensitive, so implementations typically serialize the name lowercased.
CSSVariablesDeclaration InterfaceCSSVariablesDeclaration interface
exposes the custom properties declared in the parent declaration block
that have a non-initial value.
interface CSSVariablesDeclaration {
getter DOMString (DOMString varName);
setter creator void (DOMString varName, DOMString varValue);
deleter void (DOMString varName);
}
The supported property names
on a CSSStyleDeclaration object
are the property names of all the custom properties in the CSS declaration block declarations
with a non-initial value,
with the "var-" prefix removed.
Before running any of the algorithms in this section,
prepend "var-" to varName's value.
When asked to get the value of a variable,
if varName is in the CSS declaration block declarations,
invoke getPropertyValue() by passing varName as its argument,
and return the returned value.
Otherwise, return the empty string.
When asked to set or create the value of a variable,
invoke setProperty() by passing varName as the property argument and varValue as the value argument.
Note that using setProperty() to set a property to the empty string
instead deletes the property.
When asked to delete the value of a variable,
if varName matches the grammar of a custom property name,
invoke removeProperty() by passing varName as its argument,
and return the returned value.
Otherwise, do nothing and return the empty string.
div {
var-foo: 16px;
var-Bar: red;
var-foo-bar: 50%;
}
The following lines of script all return something useful:
el = document.querySelector("div");
print(el.style.var.foo); /* Prints the value of "var-foo" */
print(el.style.var.Bar); /* Prints the value of "var-Bar" */
print(el.style.var["foo-bar"]); /* Prints the value of "var-foo-bar" */
On the other hand, the following do not:
print(el.style.varFoo); /* Custom properties don't exist directly on "style" */ print(el.style.varfoo); /* Not even if the casing matches. */ print(el.style.var.foo-bar); /* Retrieves "var-foo" and subtracts a JS variable named "bar", rather than retrieving the value of "var-foo-bar" */
var property:
var customProps = el.style.var;
for(customPropName in customProps) {
var customPropValue = customProps[customPropName];
/* More stuff here. */
}
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.