Status: ED Shortname: css-variables Level: 1 TR: http://www.w3.org/TR/css-variables-1/ ED: http://dev.w3.org/csswg/css-variables/ Previous Version: http://www.w3.org/TR/2013/WD-css-variables-20130312/ Previous Version: http://www.w3.org/TR/2012/WD-css-variables-20120410/ Editor: Tab Atkins Jr., Google, http://xanthir.com/contact 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. Ignored Properties: var-0, var-foo, var-main-color, color, var-Bar, var-two, margin-top, var-header-color, var-accent-background, var-foo-bar, var-bar, background-color, var-one, all Ignored Terms:, , , ,
Name: var-* Value: <> Initial: (nothing, see prose) Applies to: all elements Inherited: yes Computed value: specified value with variables substituted (but see prose for "invalid variables") Media: all Animatable: no
Should variables have special animation behavior, such that they just never animate, not even with the "just flip at 50% progress" like everything else?
Should the global values initial/inherit/default be interpreted? I'm guessing no, because we're not meant to interpret *anything*. You can get switch it back to inheriting by setting it to a guaranteed-invalid value, like ''var-foo: var(foo);'', but you can't get ''default'' behavior.
What's the relationship of custom properties and the 'all' property? Does it reset all the variables? If ''var-foo: initial;'' isn't interpreted by CSS, what effect does ''all: initial;'' have? A custom property is any property whose name starts with "var-", and contains at least one additional character. Custom properties are solely for use by authors and users; CSS will never give them a meaning beyond what is presented here. Note: Authors are recommended to choose custom property names so that the part after the "var-" is an identifier by itself, so that it can be referenced without character escaping. For example, 'var-0' needs to be referenced as ''var(\30)'', because ''0'' isn't a valid identifier. (U+0030 is the hexadecimal code for the ''0'' character.)
:root {
var-main-color: #06c;
var-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 many edits across all stylesheets in the webpage.
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 [, <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 second argument to the function, if provided, is a fallback value, which is used as the substitution value when the referenced variable is invalid. 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. A property value containing a variable must be assumed to be valid at parse time. It is only syntax-checked at computed-value time, after variable references have been resolved. To resolve a variable in a property's value:> ]? )
/* 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 {
readonly attribute CSSVariablesMap var;
}
A CSSStyleDeclaration is the associated style declaration
for the CSSVariablesMap assigned to its var attribute.
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.
CSSVariablesMap InterfaceCSSVariablesMap interface
exposes the custom properties declared in the parent declaration block
that have a non-initial value.
[MapClass(DOMString, DOMString)]
interface CSSVariablesMap {
DOMString get(DOMString varName);
boolean has(DOMString varName);
void set(DOMString varName, DOMString varValue);
boolean delete(DOMString varName);
}
The map entries
on a CSSVariablesMap 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,
paired with their values.
CSSVariablesMap MethodsgetPropertyValue()
on the associated style declaration
by passing varName as its argument,
and return its returned value.
false;
otherwise, return true.
setProperty()
on the associated style declaration
by passing varName and varValue as its two arguments, in that order.
removeProperty()
on the associated style declaration
by passing varName as its argument.
If the returned value is the empty string,
return false;
otherwise, return true.
div {
var-foo: 16px;
var-Bar: red;
var-foo-bar: 50%;
}
Here are the results of several JavaScript expressions,
assuming that el is a JavaScript variable
holding an element that the above style rule applies to:
| Code | Value | Notes |
|---|---|---|
el.style.var.get("foo")
| "16px"
| The value of 'var-foo'. |
el.style.var.get("Bar")
| "red"
| The value of 'var-Bar'. |
el.style.var.get("foo-bar")
| "50%"
| The value of 'var-foo-bar'. |
el.style.varFoo
| n/a | Custom properties don't exist directly on "style" |
el.style.varBar
| n/a | Not even if the casing matches. |
var property:
var customProps = el.style.var;
customProps.forEach(function(propValue, propName) {
if( knownCustomPropName(propName) ) {
/* Processing code 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.