CSS Variables, why we drop the $foo notation
By glazou on Friday 17 August 2012, 04:58 - CSS and style - Permalink
CSS Variables... A major request from the Web Authors' community since 1998...
Dave Hyatt and I shaped the first concrete public proposal in 2008 but the CSS Working Group could not reach an agreement about it. Variables, constants, mutable constants, OM or no OM, we had endless "religious" discussions about that and Hyatt finally removed his implementation from WebKit.
Then Tab Atkins made a breakthrough last february proposing a much
better CSS integration, a model that fits nicely with what we
already have. But then we had a problem with the $foo
syntax
everyone was asking for. Let me explain...
First, the proposed spec is NOT about variables and I seriously wonder if we should not change the title of the document. You may call the feature it introduces "variables" but at the deeper level, that's not about variables. That about Inherited User-Defined Properties. Don't take that as some political correctness or a blurb hiding the reality of the feature. The spec is really about letting you define your own properties and reaching their cascaded values. For instance:
div > p {
var-myprop: 1;
}
p {
var-myprop: 2;
}
In the above case, what will be the value of var(myprop)
in
a p contained into a div? Right, it will be 1 because the first rule has a
higher specificity than the second one... Clearly, these beasts are not
variables in the usual acceptance of the term.
Second, how are we going to use the values specified that way? We
considered data(myprop)
, var(myprop)
and $myprop
.
And despite of all the arguments expressed by the community in favor of
the latter, the CSS WG decided during its recent San Diego face-to-face meeting to not
follow the $foo
path: $foo
for the
getter is too similar to a programming language, leading to confusion. The
$ notation is used by preprocessors and script-based toolkits all over the
place, it deeply conveys a notion of "programming language variable" or
"preprocessor macro". And that's not what this spec is about since the
feature introduced by the current spec shows important restrictions a
variable or macro would not show:
- the value must be a set of valid CSS tokens; don't think of a preprocessor, don't dream of a partial string replacement. I repeat: a set of valid CSS tokens.
- you can't use the getter anywhere in CSS Stylesheets. You can use it only on the right-hand side of declarations (in other tems, after the colon in a '<property> : <values>' style declaration).
- we need an optional second argument to the getter giving a fallback value if the user-defined property is not set at all on the element.
Before shouting, don't misunderstand us: we clearly see the simplicity,
readability and maintainability of the $foo
syntax and we
perfectly understand why the community prefers it. Being ourselves coders
and implementors, we're used to manipulate $-based notations. But running
that path implies too much confusion about our feature, will then lead to
errors in live stylesheets, impacts existing CSS preprocessors that will
have to change their own syntax to avoid collisions with ours, and will
block us if we decide in the future to add real $-based macros to CSS.
Again, we do understand why Web Authors prefer a compact and simple
notation like $foo
but we have decided it comes at a too
expensive cost right now. We may reconsider this decision in the future
(don't forget the spec is only a Working Draft at this time) but this will
require solving all the issues detailed just above. Thanks.
Comments
Since a large part of the problem with $foo seems to be about the specific character "$" having baggage, but a large part of the advantage of $foo is its relative brevity, how about using a different character with less baggage? As far as I am aware, for example, the character "=" is not used in CSS at all. How about =foo? That carries the connotation of "equals" of course, which is kind of what you want - you're setting the property equal to a predefined value. It doesn't read like a magical macro that can be used anywhere.
html {
var-my-site-color: #ccc;
}
div {
background-color: =my-site-color;
}
Perhaps I'm missing something, but can't the scenario of fallbacks for when the property is undefined entirely be handled just by native CSS fallback behavior?
div {
background-color: white;
background-color: =my-site-color;
}
If you treat declarations that reference properties undefined on the element in question as ignored for that element rather than having a particular value, that would have the desired effect, wouldn't it?
Excellent to see the co-chair come out and make this point. Thanks for a thoughtful explanation, looking forward to more discussions of some of the finer points on www-style.
Stuart, on #2...
Equals is used in attribute selectors, but that doesn't mean it couldn't have a different meaning as a value. Fallbacks couldn't really just be regular css, it is possible to gang these up and use them in a calc() - you need the ability for explicit fallbacks. There are other things to consider as well, for example, it has already been identified and postponed until this is shipped that there are other features wrt accessing, like "the parent's property, not mine". The options of single character only works in the simplest of cases here really because it isn't simple preprocessor style repleacements. Elsewhere, generally, we use a fn for this sort of thing so it is already a part of the grammar, etc.
Everyone is well aware of the desire for brevity though (is there any css fn whose name is more than 4 characters?) and you can bet it will be small whatever it is.
I ask myself a question. For exemple :
CSS 1 : /path/example/1/test.css
html { var-image: url(example.png); }
body { background: $image; }
______
CSS 2 : /path/example/2/test.css
.comment { background: $image; }
_____
@import url(/path/example/1/test.css);
@import url(/path/example/2/test.css); ← in this sheet, the value of url() is url(example.png) or url(../1/example.png) ?
Anyway, I saw the variables like this:
@vars {
myvar : 1px;
}
boby > div {
border : $myvar; /* 1px */
}
@vars {
myvar : 2px;
}
boby {
border : $myvar; /* 2px */
}
In my opinion the programming language comparisons as an argument against “$” fall flat. I do not come from a programing background but the unrelenting fear of making CSS programatic is holding CSS back. This is why preprocessors have become so popular and this is why LESS is struggling to stay relevant as an antiquate preprocessor. Not to say CSS should just emulate them but I see preprocessors as an experiment with the future functionality of CSS. It’s this kind of stubbornness about programatic stuff makes me want to switch back to the $foo camp.
That being said, the argument Tab made for var() completely won me over: http://www.xanthir.com/blog/b4KT0
The flexibility of custom properties in var() gives us much more power and flexibility. Arguments like this show just how multi-dimensional and powerful the var() syntax can allow CSS to be.
Zéfling re: #4 I assume you meant to include a second set of the value and use the non $ syntax and that the nature of your question is answered like this: its not about what the variable is in the sheet, its about what it is in the cascade and in the dom, just like any other inheritable property in css... that is one reason why this version "fits" css more than most of the preprocessors. If you imagined those were just normal backgorund-image properties with normal url()s you could surely answer that question. What these give you are a way to avoid repeating long urls and cryptic hex colors, mix them with calc()s and other valid CSS mechanisms. Your case is a particularly good example because if 1 and 2 had media queries, you could write your main stylesheet with reference/use functions without caring and CSS will take care of it for you.