Tab Completion

I'm Tab Atkins Jr, and I wear many hats. I work for Google on the Chrome browser as a Web Standards Hacker. I'm also a member of the CSS Working Group, and am either a member or contributor to several other working groups in the W3C. You can contact me here.
Listing of All Posts

Let's Talk about CSS Variables

Last updated:

If you didn't know, CSS is adding Variables to the language. Here's the spec.

We needed to nail down some arguments about the syntax used, so the CSSWG discussed this yesterday at our face-to-face meeting.

First, let me state this plainly. THE DECISIONS MADE ARE NOT NECESSARILY FINAL. We needed an answer to the questions, so that if nothing else came up, they'd be settled and we'd have a completed spec. However, we can change it if necessary in the future.

The Actual Decisions

The CSSWG decided to use properties that look like "var-foo" to define variables, and refer to variables with a function like "var(foo)".

We also agreed to include the fallback argument to the var() function (a value to be used when the referenced var is invalid or undefined), and to defer the parent-var() function to level 2, which we'll start working on concurrently, as we think we'll need more time to suss out features in this space.

What's the Relevance?

Previously (in fact, currently, since I haven't updated the spec yet), there was discussion about using properties that look like "$foo" to define variables, and using "$foo" to refer to vars as well.

(There were also some additional suggestions to use the same structure we decided on, but different prefixes or function names. I'll omit those from this discussion, because they're less interesting/controversial. Most people who don't like the decision we ended on want $foo, not just a different prefix in "var-foo".)

Why Did I Drop $foo?

I'll start by saying - I totally like the $foo syntax. I am definitely not opposed to it in general. It's in the current spec (until I make edits) because I liked it and wanted it.

However, I recommended we drop it. Why?

Mainly, I think there's more space to explore here. If we use $foo for variables, we'll be unable to use it for future "variable-like" things.

For example, if we do define an alternate form that are more SASS-like (can be used anywhere, but are global; more "macros" than "variables") we'd have to use some other glyph for them. That's suboptimal.

More specifically, if we ever do some sort of "variables" in selectors, we must use a compact form like $foo or something. Anything else is unusable, I believe.

For example, David Baron recently proposed a powerful form of selector variables for discussion. After some thought, I realized that this is exactly identical to SASS's @extend directive, just with a somewhat different, more explicit, syntax.

As another example, fantasai and I have been discussing a proper macro system for CSS; in other words, exactly what SASS's variables currently do. I assert that we'd definitely want a nice short "$foo" or similar syntax for this as well.

However, CSS Variables as written don't have these constraints. I think it's acceptable for them to have a slightly longer form, given the way they're used. (They're not being pushed into a terse microsyntax like Selectors.) As well, they're not "combining" with surrounding context like macros might do, so the functional form is okay.

So, for now, I recommend that we drop the $foo syntax from CSS Variables, and look into the other types of things we can use this syntax for. If we don't find anything that needs this kind of syntax, then we can go back to letting Variables consume this kind of syntax for its own use.

I hope this clears up some of the confusion around my reasoning for this decision. Questions? Let's take them to the comments.

(a limited set of Markdown is supported)

The fallback's not really a must-have, since a valid var value is always preditable. There are no unknowns in a stylesheet, right?

I do like var(name) better than $name though. It's more CSS-like. More classic. Bring it on.

Reply?

(a limited set of Markdown is supported)

"Must-have", no. Convenient, yes. It means you can write simpler selectors in some cases. This becomes extra-true when we start using parent-var() - doing it right without fallback is kinda tricky.

Reply?

(a limited set of Markdown is supported)

+1 on a lot of this stuff. I personally didn't like $ as used (liked it with $()) especially on both sides because I spent a lot of time (even today) explaining to people that they weren't both variables and they worked nothing like (fill in the blank: less,sass,stylus,php,jquery,freemarker,shell,etc), and..more confusingly, the best way to understand them is actually more like "custom properties" and "a function to access the value of these custom properties". Almost to a person, as soon as you give them that much they suddenly "got it" sometimes after hours of discussion. I still think the name is an important change, especially if we plan to do things in the future which also could use the word "variables"...it could get muddy fast.

Reply?

(a limited set of Markdown is supported)

Yeah, I'm still thinking about using the "custom properties" phrasing. I think it's probably a good idea.

Reply?

(a limited set of Markdown is supported)

The motives for the change seem entirely reasonable to me. I find the way variables are declared (var-foo) a little weird, but the way they are called (var(foo)) seems more in keeping with the rest of the CSS syntax, so I'm completely satisfied with that.

Reply?

(a limited set of Markdown is supported)

The above is just one recent project (I'm still working on it) and it uses php variables to load database stored values into the values of some of the stops within compound CSS gradients. So I was confusing the idea of variables in CSS as discussed here initially.

What seems to be getting done here is to allow for custom property definitions which may be substituted for fall back property definitions. Accordingly, my preference is definitely for the syntax shown to me by Brian Kardell. Compared to the proposed W3CWG spec it's just more intuitive to me as the one writing the code. It more clearly shows the intent and functionality of the "variables" or rather custom properties, their syntax and use cases.

That clarity of definition and purpose seems to me to allow that more will adopt and correctly apply this proposed expansion of CSS capabilities. The link below references the syntax and use cases I refer to above.

http://briankardell.wordpress.com/2012/06/28/properties-the-new-variables/

just a thought

Reply?

(a limited set of Markdown is supported)

Glad to hear we're finally getting variables. I'm not sure I quite understand the syntax you describe, though. Why do we have two different formats, "var-foo" and "var(foo)"? Is the intention to allow variables for both properties and values?

Could you then have a valid line of CSS such as "var-box-shadow: var(large-blue-shadow)" where "var-box-shadow" would translate to something like "-moz-box-shadow, -webkit-box-shadow, box-shadow", allowing you to set multiple properties in one go, and "var(large-blue-shadow)" would translate to a particular shadow value like "10px 10px 50px #00f"?

Am I following this correctly?

Reply?

(a limited set of Markdown is supported)

Glad to hear we're finally getting variables. I'm not sure I quite understand the syntax you describe, though. Why do we have two different formats, "var-foo" and "var(foo)"? Is the intention to allow variables for both properties and values?

You may want to read the draft - it's linked in the first sentence of this post.

You declare a variable with a custom property. Custom properties can have any name you want, but must start with a "var-" prefix. You can then use the value of that custom property inside of other properties with the var() function. The spec has several examples - check it out!

You cannot use variables as property names, only property values. As I say in the post, I'm looking at other possibilities for dealing with this kind of thing, and I probably want to use the "$foo" or similar syntax for it, which is why I dropped that syntax from variables.

Reply?

(a limited set of Markdown is supported)

Corey...if you have trouble with the draft, read the blog post linked to above about custom properties and let us know if that helps. Fun experiment :)

Reply?

(a limited set of Markdown is supported)

#11 - Jon Rimmer:

I'm still a little confused by the actual arguments presented here. We can't use $foo for CSS variables, because we might need them later for CSS variables?

I think it's pretty unfortunate that the working group has managed to go from a straightforward case of paving the cowpaths with something that a lot of developers are using, right now, via preprocessors, to producing something that is still called CSS variables, but apparently works so differently that we have to change the syntax to stop people confusing it with the currently understood notion of CSS variables, and so we can actually implement CSS variables as understood at some unknown point in the future.

Reply?

(a limited set of Markdown is supported)

#13 - Jon Rimmer:

In fact, let me see if I've got this straight in my head:

CSS preprocessors added a feature called variables, but which would more accurately be described as macros.

Browser vendors and the working group worked sporadically on an in-browser implementation and standardisation of this macro feature, and they also called it CSS variables.

The WG's focus shifted to a feature more akin to custom properties, but which retained the name CSS variables.

So, right now we have CSS variables in preprocessors, which are macros, CSS variables in browsers, which are custom properties, and maybe at some point in the future, macros in the browser, and actual variables in the browser.

Reply?

(a limited set of Markdown is supported)

Hi - after reading the current spec using var-xx and var(xx) makes sense. Good work!

Reply?

(a limited set of Markdown is supported)

I agree with #1 var(name) seems more semantically fitting with properties like background: url();

$var seems like jQuery from a front-end perspective.

Reply?

(a limited set of Markdown is supported)

Using var-xx and var() sounds good.

Will we be able to use var(cstProp, +20px) or have to use calc(var(cstProp) +20px)?

Reply?

(a limited set of Markdown is supported)

Re #26:

You have to use calc(). The second argument in the var() function is the fallback value, which is used instead of the variable when the variable is undefined or invalid.

Reply?

(a limited set of Markdown is supported)

Well, I'm biased, as I mainly use SASS, so it's understandable why I might prefer $foo over var-foo/var(foo).

However, what I dislike about the var-foo/var(foo) is the cognitive dissonance created by using different sytax's for declaring / calling the variable. In both SASS and LESS (two of the main CSS preprocessors today), the syntax used to declare and call the variable are the same. Though, admittedly, the number of people who are currently using preprocessors is a small sub-set of developers, it seems strange to make them relearn this concept.

Also, I'd avoid the "custom properties" phrasing. The term "property" already has a definition in CSS. Things like "border," "display," and "color," are properties. Saying something is a "custom property" makes it sound like I can do #selector { jelly: grape; }. "Jelly" would be a "custom property," var-foo is a variable.

Though, any movement, no matter how imperfect, is better than no movement at all.

Reply?

(a limited set of Markdown is supported)

Quite relieved about this actually :)

I think this is definitely the best solution for reasons already stated in the post and the comments

Reply?

(a limited set of Markdown is supported)

I don't understanding having var('name') instead of a quick representation like $name. If we can make our CSS files smaller, why not? It contributes to a faster web experience for everyone.

I also don't like the idea of adding another prefix "var-" when we could just use $ or another symbol. We're already battling too many prefixes.

I prefer a minimalist approach. I don't look at this as "CSS needs to maintain character".

Why do we even have to specify url()? Shouldn't the rendering engine easily be able to determine if it's a URL or not.

Reply?

(a limited set of Markdown is supported)

Thanks for being open to questions and taking the time to really think this through. All of us who have no say in the matter really appreciate it :)

Reply?

(a limited set of Markdown is supported)

Russell re:#28

They really are properties in virtually every sense - including the one where you can't have a selector based on them :) Did you read my blog post linked above? Personally curious whether you feel that way after having read it or not...

Reply?

(a limited set of Markdown is supported)

var(name, value) feels similar to calc(expression) and url(path). I think it helps keep it's purpose clear and doesn't muddy the water with more prefix soup.

I've read the spec but 'parent-var' notation for it clouds the purpose, flow, and is difficult to read. Also it seems to unnecessarily restrict the var usage by limiting to only the direct Parent element; given that we have selectors for so many other combinations.

Would not adding a third parameter (selector) to the var help to clarify. Where given the selector rules can be our inspiration here. ex: given the selector rule 'element1~element2' as inspiration when the third parameter is '~' it references the Parent Element; likewise '~~' would be the parent{2}. By use of selectors as the third parameter it allows for deep reaching into the variable stack and could allow for some nice evaluations.

Reply?

(a limited set of Markdown is supported)

What about uting the established @ sign? @favoriteColor #123456 h1{color:favoriteColor}

Reply?

(a limited set of Markdown is supported)

Re #28:

The difference between var-* and var(*) has been cited before. I don't think it's a huge problem, but aligning them would be nice. The main problem with doing so is that it would require changing CSS's Core Grammar, which we try to keep stable - right now, the "property name" part of the grammar is defined to be a bare IDENT token.

Regarding the "custom properties" phrasing, it is a custom property. You can definitely write "var-jelly: grape;" if you want. This is useful if you're, say, using CSS to pass information around for JS (similar to using data-* attributes to put information into your markup for JS).


Re #30:

Saving a few characters per use does not contribute in any meaningful way to page speed. I greatly value terseness in web languages (the DOM is a monument to how stupid it is to assume that everyone can just use autocompletion, so the length of a name doesn't matter), but below a certain point it's not that significant. (Additionally, below a certain point terseness stops being "easy to type" and starts being "hard to read".)

More importantly, though, I gave reasoning for why I didn't want to use a $ prefix - I think we'll want to use it later, for a different mechanism. I'd like to keep the amount of grawlix added to the language to the minimum necessary.

Note that going with $ instead of var- isn't avoiding a prefix, it's just using a shorter prefix. The problem with prefixes in CSS is specific to the concept and execution of "vendor prefixes" - it's not a generic problem with all concepts of "prefix".

Finally, the url() function is definitely necessary, because urls (particularly relative ones) can be nearly anything. Look at background: red;. If we didn't require url(), this would be impossible to tell whether it meant background-color: red; or background-image: url(red);.


Re #34:

We're pushing parent-var() to level 2 anyway, so there's time to argue about its syntax. Maybe something like var(inherit foo) instead?

I don't want to add arbitrary-element referencing yet. It increases complexity of the feature a lot, and makes it much harder to do efficiently. Using the inherited value (parent-var()) has strong use-cases, but I'm not aware of any good use-cases for reaching arbitrarily across the document.


Re #35:

That's unworkable for several reasons:

  1. It's SASS-style global variables, rather than CSS Variables, which are tree-scoped by virtue of being properties.
  2. It collides with current (and more importantly, future) at-rules in the language. We could never add another at-rule again without a compatibility check against the web.
  3. In your proposal, variables are referenced as simple keywords. This collides with current (and more importantly, future) keywords in properties. Again, we could never add a new keyword to any property without a compatibility check against the web.
Reply?

(a limited set of Markdown is supported)

Wrt "Why do we even have to specify url()? Shouldn't the rendering engine easily be able to determine if it's a URL or not."

Is "red" a keyword or a URL? It could be either.

Reply?

(a limited set of Markdown is supported)

Re #36: Unrelated to the article, but I think your comment parser needs a tweak: it's greedily consuming asterisks, so "one and two" looks wrong. :o)

Reply?

(a limited set of Markdown is supported)

Re #4: Having read through a couple of articles and skimmed the spec, I must say I think "variable" is something of a misnomer. The article linked in comment #6 has a fork of the spec where they remove all references to "variables" and refer to "custom properties" and "property references", which makes a lot more sense to me.

Given this, could property references refer to regular, non-custom properties? In the var() notation this wouldn't be easy, but in Kardell and Remy's spec they suggest use(my-color) as the syntax to reference the custom property my-property. In that case, why couldn't I do, say

p.one {
    color: blue;
    border-style: solid;
    border-color: use(color);
}

to get a consistent style? Why should var (or use, or ref, or whatever) be restricted only to custom properties?

Reply?

(a limited set of Markdown is supported)

This is awesome! It's also the first I've heard about it.

At first glance, I was curious why variable defining was different from variable usage. I noticed the fallback was one of the reasons, which you cannot do if 'var-name' was also how you used the variable. It also allows you to do this, like in the spec: 'var(gap)px'. I now understand why and I'm starting to like it.

I wonder though if the syntax can be unified the other way, using 'var(name): 12px' to define variables. Is it just annoying to type? It does remind me of the 'data-name' and $().data('name') distinction. Looking forward to seeing the final say.

Very cool stuff!

Reply?

(a limited set of Markdown is supported)

I assume you've read Bert Bos' essay about why variables are a bad idea? Have you actually bothered to address his points in public or is it just a case of you being hard-headed and desperate to make your mark on history?

Please stop pushing through frivolous and utterly useless features.

Reply?

(a limited set of Markdown is supported)

Re #40: Calling them variables is somewhat misleading. Calling them "custom properties" is just completely wrong.

Reply?

(a limited set of Markdown is supported)

Re #42: No, it's not "cool stuff". It's stuff that will have to be supported forever and adds indirection to CSS, which is a slippery slope.

Variables or symbolic constants are for authors not for browsers. Just use a pre-processor.

Reply?

(a limited set of Markdown is supported)

Re #43: Sigh, obvious troll is obvious.

Do you really think we haven't argued about this exact thing on the CSSWG mailing list? Go look up the history; I've argued strongly against Bert's points. Mostly, the argument is just that Bert hasn't ever written a really non-trivial site, where the bonus to code organization from Variables really shines (though it's useful for small sites, too).

Additionally, Bert quite simply doesn't believe that HTML+CSS+JS should be used for "apps" (instead, he thinks we should be shipping Java or something similar). His opinions on what sort of complications we should allow in CSS, thus, are pretty tainted, and should be taken with a grain of salt. The WG has had to hard-override him several times due to this.

Re #44: lolwut

Re #45: "Just use a preprocessor" is the same as "just use a library". It's a fully general counterargument to adding anything except for fundamentally new primitives. However, libraries are not free in JS (or any other language, really, but JS-on-the-client has extra-special cost structures to pay attention to).

Fully general counterarguments are worthless, because they add no meaning to the argument. One must evaluate whether the addition provides functionality that is sufficiently useful to justify the cost of adding it to the platform. Given their conceptual simplicity for authors, and demonstrably high value (known due to preprocessors), Variables jumps over the "useful" line.

Reply?

(a limited set of Markdown is supported)

Re #46:

This is an aside, but I'm just curious why SASS is usually used in examples by Google folks when referring to pre-processors when LESS has a larger user base than all other pre-processors combined. This is corroborated by GitHub data and google statistics. Even Chris Coyer, a SASS advocate, showed the results from a survey with thousands of respondents that concluded that LESS was by far the most popular (http://css-tricks.com/poll-results-popularity-of-css-preprocessors/).


"So, for now, I recommend that we drop the $foo syntax from CSS Variables, and look into the other types of things we can use this syntax for. If we don't find anything that needs this kind of syntax, then we can go back to letting Variables consume this kind of syntax for its own use."

What?! "If we don't find anything that needs this kind of syntax...". The greater-than-50%-of-front-end-developers-who-already-use-variables have found something for that kind of syntax lol. Seriously, this sounds like self-indulgence, like you're "syntax squatting" so you can "maybe use the $ for another idea of your own someday". How is that even an acceptable argument to others in the CSSWG?

Beyond that, in practice IMHO the var-customproperty` syntax will be strange for a couple of reasons. First, you are prepending a "custom property" name with "var-" which indicates that the property's actual name includes "var-", but then to use the "custom property", you drop the "var-" and use "var()"?

That's not CSS-like at all, that's more like JavaScript... kind of, but not really. It's clunky and seems like a hack. In any case you seem to be defending your proposal from the point of view of a JavaScript programmer, rather than through the lens of a CSS developer.

But I also think your baseline argument about using "var-" instead of "$var" doesn't hold any water. It's reasonable to propose a syntax with the intention of making it as "future proof" as possible. But what you're suggesting is that 1) there "might" be "variable selectors", "at some point", and 2) if there are, it might be better to use the "$var" syntax on the "selector variety" of variables, but not on the "property variety" of variables, which will be used orders of magnitude more often than the selector variety? So you are suggesting a hack-ish solution for the common usage, and a more proper syntax for the edge case? I'm not sure that's good logic.

Also, when a new flavor of variables is introduced for selectors, do you see any compatibility problems that might occur from having an entirely different syntax than "property variables"? So, how would one use "custom property (variables)" in conjunction with "selector variables". Or will this even be possible if your proposal is accepted? You do know that people use these things together in practice already in preprocessors, right? Do you know of any use cases for the two being used together (selector variables and property variables)? What about interpolation or variables on import statements?

Not only am I struggling with how your syntaxes will remain compatible technically - but you actually think that your proposal won't confuse people? Barring any technical challenges between the syntaxes, don't you think CSS users will be confused as heck by what you're proposing? Chris Coyer survey indicated that - as of June 2012 - greater than 50% of CSS developers had used or were using a preprocessor already. That percentage is undoubtedly higher today. So even though LESS uses "@var" and SASS uses "$var", both communities have the experience to know that this implementation works in practice. But you don't see any value in leveraging the learning curve and experience of either community? To that end, I personally don't believe that experience with CSS alone makes you even qualified to propose, much less debate, "preprocessor-like" functionality. If you haven't been there in the trenches, how can you act like you know what you're talking about? So I'm curious what experience you have with preprocessors that that gives you a better perspective than the communities who have paved the cow-paths?

To be direct, it sounds like you are trying to create your own syntax just to say it was "yours", rather than building off of the work done by preprocessors like LESS and SASS, which are already used by tens of millions of people. Have you even contributed to any pre-processors at all? Have you worked on the specs for SASS or LESS, or even spent time debating these things with people who work on those projects? LESS works in the browser, so the core developers are completely sensitive to the issues you're raising. But have you even listened to debates about these things on GitHub Issues for SASS or LESS?

It's like you "came up with this idea" and proposed it without properly vetting it through proper channels and by discussing them with the real thought leaders of CSS (the core developers of SASS and LESS), now the rest of us need to live with your idea. It's disheartening that the spec for CSS is being influenced by someone who seems inexperienced with using the features you're describing in the real world.

Reply?

(a limited set of Markdown is supported)

Re #47: I have no idea why other Googlers prefer to mention SASS, but for myself, I just consider SASS a much better technology. LESS has several bad decisions in it, and it regularly chooses what seems like the worst possible syntax for things. ^_^


Regarding the syntax of $ vs var(), I actually already know what I'll likely use it for.

The basic difference between SASS and CSS variables is that SASS variables have lexical scope, defined for all the rules sharing a grouping rule (or all, if it's not nested), while CSS variables have dynamic scope, defined for all rules applying to the same element or a descendant.

This difference allows for several useful new things that SASS can't do, but it also blocks out a few useful things that SASS can do but CSS can't.

If we ever add lexical-scope variables generally, they should use the $foo syntax, like SASS. I'm not sure we'll ever add lexical-scope variables generally, but I do think we'll add mixins with arguments to the language. The arguments passed into a mixin act as lexical variables for the mixin's body, and so I'd like to use $foo as the syntax for them. (Same thing applies to custom functions, if we add them to the language as well, which is something I'd like to do.)

Basically, while CSS variables and SASS-style variables look very similar, and can accomplish mostly the same things, there are important differences that mean simply reusing the same syntax may be confusing.

But hey, the syntax of this has turned out to be remarkably contentious. If I don't convince you, you're not alone. ^_^

Reply?

(a limited set of Markdown is supported)

I recently tested CSS variables in a nightly firefox build. I think variables is the wrong term but also is custom-properties. I suggest calling them cascading-values.

Please see my whole criticism and ideas on my blog.

gossi

Reply?

(a limited set of Markdown is supported)