Problematic History
Variables in CSS are among the most often and oldest requested features in CSS. For well over a decade, numerous W3C proposals for them have come and gone. To answer a number of the most common use cases, several preprocessors have sprung up over the years, more recently and most notably LESS and SASS. Once in place, there were a lot of great ideas experimented with and, on a few occasions it even looked like we might just be building to something which might become a standard. But the results in the end have always been the same: An eventual agreement by a lot of members that the things we keep specing out just don’t “fit” within CSS. Generally, the consensus view has been that these things are, frankly, better left to a preprocessor which can be “compiled” into CSS: it is more efficient (potentially quite a bit), requires no changes to CSS and allows competition of ideas.
New Hope
That is, until recently, when a fortunate confluence of new ideas (like HTML data-* attributes) opened the door to a brand new way of looking at it all and thus was born the new draft of CSS Variables. The principles laid out in this new draft really do “fit” CSS quite nicely, and it addresses most of the common cases as well as several that preprocessors cannot. Further, it should be reasonably easy to implement and won’t require drastic changes to complex existing implementation and ultimately should be pretty performant. In short , it really answers all of the previous concerns that have historically held it up.
Confusion
But… it seems to be causing no end of confusion and debate by people among people familliar with variables in existing pre-processor based systems like LESS or SASS. It has all been very dramatic and full of heated debates about why things don’t “seem like” variables and how to make them seem more so. All of this discussion, however misses the real point. There is a clear reason for the confusion: What the draft describes as “variables” (largely because of its history it would seem) are actually entirely unlike any existing concept of preprocessor variables (for reasons already explained). Instead, it describes something else entirely: Custom properties.
Enter: Custom Properties
When described in verbiage regarding “properties” and “values”, rather than “variables”, it is actually quite simple to not only understand the new draft without the confusion, but also to see how the new draft fits the CSS model so much better than all of the previous attempts and not only provides means to solve a large number of known use cases, but also provides fertile ground for new innovative ideas.
To this end, at the suggestion of a few folks involved in the ongoing W3C discussions, Francois Remy and I have forked the draft and proposed a rewrite presenting the idea in more appropriate terms of “custom properties” instead of continuing to attempt to shoe-horn an explanation of the now overloaded idea of “variables”.
You can view the proposal and even fork it yourself on github and suggest changes. As with any draft, it’s full of necessary technical mumbo jumbo that won’t interest a lot of people, but the gist can be explained very simply:
1. Any property in a CSS rule beginning with the prefix “my-” defines a custom (author defined) property which can hold any valid CSS value production. It has no impact on rendering, and no meaning at the point of declaration it is simply holding a named value (tokens).
2. Custom properties work (from the author’s perspective) pretty much like any other CSS property. They follow the same cascade, calculation and DOM structure inheritance models, however, their values are only resolved when they are applied by reference.
3. Custom properties may be referenced via a function in order to provide a value to another property (or function which provides a value). All referencing functions begin with the $ character. Reference functions, like the attr() function provide an optional second default/fallback to use in the case where the named value is not present.
A Fun Example…
Putting it all together, you can see an extremely simple example which illustrates some of the features:
/* Set some custom properties specific to media which hold a value representing rgb triplets */@media all{
.content{ my-primary-rgb: 30, 60, 120; my-secondary-rgb: 120, 80, 20; } }@media print{
.content{ my-primary-rgb: 10, 10, 120; my-secondary-rgb: 120, 10, 10; } } /* Reference the values via $() The background of nav will be based on primary rgb color with 20% alpha. Note that the 3 values in the triplet are resolved appropriately as if the tokens were there in the original, not as a single value. The actual values follow the cascade rules of CSS. */ nav{ background-color: rgba($(my-primary-rgb), 0.20); } /* The background of .foo will be based on primary rgb color with 60% alpha */ .foo{ background-color: rgba($(my-primary-rgb), 0.60); }/* The foreground color of h1s will be based on the secondary rgb color or red if the h1 isn't inside .content - note an amazing thing here that the optional default can also be any valid value - in this case it is an rgb triplet! */ h1{ color: rgb($(my-secondary-rgb, 200, 0, 0)); }