CSS Variables in Firefox 29

One of the most frequent requests to the CSS Working Group over the years has been to have some kind of support for declaring and using variables in style sheets. After much discussion, the CSS Custom Properties for Cascading Variables specification took the approach of allowing the author to specify custom properties in style rules that cascade and inherit as other inherited properties do. References to variables can be made in property values using the var() functional syntax.

Custom properties that declare variables all must be named beginning with var-. The value of these custom properties is nearly freeform. They can take nearly any stream of tokens, as long as it is balanced.

For example, an author might declare some common values on a style rule that matches the root element, so that they are available on every element in the document:

:root {
  var-theme-colour-1: #009EE0; 
  var-theme-colour-2: #FFED00; 
  var-theme-colour-3: #E2007A; 
  var-spacing: 24px;
}

Variables can be referenced at any position within the value of another property, including other custom properties. The variables in the style sheet above can be used, for example, as follows:

h1, h2 {
  color: var(theme-colour-1);
}
h1, h2, p {
  margin-top: var(spacing);
}
em {
  background-color: var(theme-colour-2);
}
blockquote {
  margin: var(spacing) calc(var(spacing) * 2);
  padding: calc(var(spacing) / 2) 0;
  border-top: 2px solid var(theme-colour-3);
  border-bottom: 1px dotted var(theme-colour-3);
  font-style: italic;
}

When applied to this document:

<!DOCTYPE html>
<h1>The title of the document</h1>
<h2>A witty subtitle</h2>
<p><em>Please</em> consider the following quote:</p>
<blockquote>Text of the quote goes here.</blockquote>

the result would be something that looks like this:

The title of the document
A witty subtitle
Please consider the following quote:
Text of the quote goes here.

Variables are resolved based on the value of the variable on the element that the property with a variable reference is applied to. If the h2 element had a style="var-theme-colour-1: black" attribute on it, then the h2 { color: var(theme-colour-1); } rule would be resolved using that value rather than the one specified in the :root rule.

Variable references can also include fallback to use in case the variable is not defined or is invalid (due to being part of a variable reference cycle). The first rule in the style sheet using the variables could be rewritten as:

h1, h2 {
  color: var(theme-colour-1, rgb(14, 14, 14));
}

which would result in the colour being a dark grey if the theme-colour-1 variable is not defined on one of the heading elements.

Since variable references are expanded using the variable value on the element, this process must be done while determining the computed value of a property. Whenever there is an error during the variable substitution process, it causes the property to be “invalid at computed-value time”. These errors can be due to referencing an undeclared variable with no fallback, or because the substituted value for the property does not parse (e.g. if we assigned a non-colour value to the theme-colour-1 variable and then used that variable in the ‘color’ property). When a property is invalid at computed-value time, the property declaration itself is parsed successfully and will be visible if you inspect the CSSStyleDeclaration object in the DOM. The computed value of the property will take on a default value, however. For inherited properties, like ‘color’, the default value will be ‘inherit’. For non-inherited properties, it is ‘initial’.

Implementation

An initial implementation of CSS Variables has just landed in Firefox Nightly, which is currently at version 29. The feature is not yet enabled for release builds (that is, Firefox Beta and the release version of Firefox), as we are waiting for a few issues to be resolved in the specification and for it to advance a little further in the W3C Process before making it more widely available. It will however continue to be available in Nightly and, after the February 3 merge, in Firefox Aurora.

The only part of the specification that has not yet been implemented is the CSSVariableMap part, which provides an object that behaves like an ECMAScript Map, with get, set and other methods, to get the values of variables on a CSSStyleDeclaration. Note however that you can still get at them in the DOM by using the getPropertyValue and setProperty methods, as long as you use the full property names such as "var-theme-colour-1".

The work for this feature was done in bug 773296, and my thanks to David Baron for doing the reviews there and to Emmanuele Bassi who did some initial work on the implementation. If you encounter any problems using the feature, please file a bug!