W3C home > Mailing lists > Public > www-style@w3.org > February 2011

Re: CSS Variables Draft Proposal

From: Linss, Peter <peter.linss@hp.com>
Date: Fri, 11 Feb 2011 01:15:50 +0000
To: Tab Atkins Jr. <jackalmage@gmail.com>
CC: www-style list <www-style@w3.org>
Message-ID: <C979C3BF.49DA%peter.linss@hp.com>
On 2/10/11 12:44 PM, "Tab Atkins Jr." <jackalmage@gmail.com> wrote:

>On Wed, Feb 9, 2011 at 6:56 PM, Linss, Peter <peter.linss@hp.com> wrote:
>> On 2/9/11 5:22 PM, "Tab Atkins Jr." <jackalmage@gmail.com> wrote:
>> "A token stream" is really dangerous (or exceptionally powerful,
>>depending
>> on your point of view). You're definitely going to have to tighten this
>> up.
>
>Yeah, "token" isn't the word I want here.  More like "component
>value".  I've edited the language in the draft, but this needs some
>thought to capture the right concept.

Agreed.


>> Consider:
>>
>> @var $foo bar, baz / 100px     ;
>>
>> Does that simply get dropped into place macro style? Including the
>>leading
>> and trailing whitespace?
>
>@var is not a character macro (shudder).  This gets parsed into three
>component values and two separators, and gets substituted in as such.

Yes, but even as tokens, do the tokens get interpreted only in their
original position or in their evaluated position. For example, slashes and
commas have special delimiting behaviors in shorthands, does that mean the
entire shorthand gets re-evaluated when the variable value changes?

@var $foo 10px;  p { background: red $foo 20px; }
And then $foo gets changed to:
@var $foo 10px 20px, blue 20px;

Do you get: p { background: red 10px 20px, blue 20px px; }
And you suddenly have two background layers?

Or if you change to:
@var $foo 10px / ;
Does the 20px in the "raw" declaration now change from a position to a
size?


>
>
>> Do we really want that?
>>
>> How about:
>> @var $foo url(;
>> @var $bar );
>>
>> p { content: $foo http://example.com/yikes.gif $bar }
>
>Oh jeezus no.  I don't know the precise details of url() parsing, but
>either that is parsed into a single $foo variable containing
>"url(;\n@var $bar )", or they're both invalid.  Either way works for
>me.

Yes, url's are particularly nasty, but don't get hung up on that, it could
just as easily have been:
@var $foo rgb(;

For that matter, you do have an interesting problem with url() because the
tokenization rules change within those ()'s. If you have:
@var $foo http://www.example.com/; p { content: url($foo); }

How do you tokenize @var $foo? With normal CSS tokenization rules or URL
tokenization rules.

Donšt forget this case:
@var $foo /*this_is_a_valid_url*/;  p { content: url($foo); }

Is it a comment or not?



>>>Using variables is very easy too - they can be used anywhere you could
>>>use a component value:
>>
>> I can see use cases for having variables in selectors or as properties.
>> But we probably don't want to go there.
>
>I think variable selectors have use-cases, but not directly.  There
>are some interesting patterns that they permit when you combine mixins
>and nested selectors, neither of which I've brought to the group yet.

Let's not. (for now)



>>>~~~~
>>>p {
>>>  color: $main-color;
>>>  background: url(foo) $main-color no-repeat;
>>>  list-style-image: radial-gradient($main-color, $secondary-color);
>>>}
>>>~~~~
>>
>> FWIW, I truly despise this syntax. Yes, it's used in a bunch of
>> programming languages, that doesn't make it good.
>>
>> One technical argument against it is that you can't abut a variable
>> against a trailing identifier or number (unless you add a "eat one
>> trailing whitespace rule like for escapes). Although depending on how
>>the
>> "token stream" gets restricted this may not matter.
>
>Yeah, that's not actually an issue, as it was never the intention that
>a variable be able to do things like hold "px".  Variables should only
>hold whole component values, or streams of them + separators.

This all hinges on how you define legal values of variables. The var()
notation is more flexible because a variable as used is self-delimiting.


>>>Using a variable that hasn't been declared is a syntax error.  (It's
>>>valid to use a variable that hasn't been declared *yet* - the
>>>declaration may appear later in the stylesheet, or in another sheet
>>>entirely.)
>>
>> Don't use the term "syntax error" here. I read this as "invalid and may
>>be
>> thrown away at parse time", which is clearly not your intent.
>>
>> Better to define referencing an undefined variable as having a 'null'
>> value, or more likely a value of 'invalid' which is a special token
>> meaning that it's there, but will always be invalid. You also need a way
>> to "unset" a variable via script and/or set it to a null/invalid value.
>
>You definitely understand my intent here.  I'm open to better ways to
>express this.  I like the idea of an always-invalid special value.
>I'll put that in the draft.
>
>An interesting issue - how does an invalid variable get treated when
>you ask to serialize the property?  I'm going to assume it's
>serialized as itself, since the syntax for variable names is already
>designed to always be invalid.  (Is this true?)

You should serialize it with the variable in place, donšt replace it with
its value at serialization time (presuming you're serializing the CSSOM of
the stylesheet, not the computed style). If you're serializing the
computed style then an invalid variable wouldn't come in to play anyhow.

>
>
>>>Variables and @import, etc.
>>>---------------------------
>>>
>>>Variables exist in the global scope.  @import'ing a stylesheet makes
>>>any variables contained within it available to all other sheets.
>>>Similarly, linking in a stylesheet makes any variables contained
>>>within it available to all other sheets.
>>
>> A mention of alternate stylesheets as well as disabled stylesheets would
>> be good here too. I presume the variables should be out of scope if the
>> stylesheet isn't applied.
>
>Yes, variables come out of active stylesheets only.  I've added a mention.
>
>
>>>The latter two declarations form a dependency cycle, and so are
>>>invalid.  A single variable named `$foo` is created, with the value
>>>`red`.  If you then delete the third rule, the second is no longer
>>>part of a cycle, so the `$bar` variable is valid and contains the
>>>value `red` as well.
>>
>> Invalid at parse time or at run time? If variables are changed via
>>script
>> you should detect and deal with (or detect the absence of) loops after
>> each change...
>
>Runtime, I would think, since you can change the value of a variable
>(and potentially introduce or remove loops) at runtime.

Right, just needs to be clearly specified. (Still glad you took this on?
:-)


>>>Deleting a map entry either deletes the current declaration producing
>>>that value, or deletes all declarations defining that variable.  The
>>>former is more symmetric with the underlying behavior of the "change"
>>>action, but the latter is more symmetric with the *apparent* behavior,
>>>as the complexity of multiple declarations is hidden away.
>>
>> There should also be a way to access all declarations, even if
>>overridden.
>> Insert, remove, reorder, etc. Or (probably better) you can't change this
>> list, only the values of the variables in it. And if you do, you're
>>really
>> changing the @var object in whichever stylesheet it came from.
>
>Yes, changing a var through the css.vars map is always just sugar for
>changing the @var object in the underlying stylesheet.

I _REALLY_ don't like that. There's another issue here, consider:

@var $foo red;
p { color: $foo; }
div { height: $foo; }

Is $foo valid or not? The right answer is, the @var declaration is valid,
the first use is valid, the second isn't. Now I change the value of $foo
to "10px"...

Now consider:
@var $foo red;
p { color: $foo; }
... Some random other style, maybe a different stylesheet...
@var $foo 10px;
div { height: $foo; }


What's the color of the p? What's the height of the div? According to your
current model, the value of $foo is "10px", so the first use suddenly
stops working when a different stylesheet, perhaps linked from a different
site get's applied. Authors are going to have a hell of a time tracking
that bug down...


>
>What's the use-case for messing with shadowed variable declarations?
>You can access them through stylesheet groveling, if it's absolutely
>necessary.

I'm not sure which one you're calling the "shadowed" one.

The authoring tool use case means having access to all the @var
declarations in the stylesheet objects. You need to be able to change both
the values and variable names, as well as insert and remove @var rules. If
you make changes there to a "live" web site in a browser, you potentially
have to re-evaluate all the style data. This can be expensive...

If you now tweak @var rules via document.css.vars.foo, what are you really
changing? The one and only @var rule that happened to be the winner at the
time? Which might be different the next time you change it?

As I think about it more, the document.css.vars list should be a separate
map. It gets populated by the @var rules, but it a distinct entity. If you
change the value in document.css.vars, you're only changing the "used"
value in the map, not touching any stylesheet @var objects. You could add
or remove to/from the css.vars map but you're only adding the values in
the runtime "computed vars" map, not creating @var rules in any sheet. If
you then make a change in the stylesheet objects, the affected @var rules
get re-computed and re-mapped. (This will be an interesting algorithm, and
needs to be spec'ed, if you change the value of an @var that's overridden,
you don't touch the map, if you change a var name, add, remove or move a
@var such that it now applies or no longer applies, you look at all the
@var rules for that variable and recompute the map. Lather, rinse, and
repeat when enabling or disabling stylesheets for all @var rules in the
affected sheet. The alternative is you throw away the whole map and
recompute any time something changes that affects it.)

>
>
>>>To add a new map entry, we first define `css.stylesheet`, which
>>>implements the `StyleSheet` interface.  This stylesheet is treated as
>>>an author-level sheet placed after all other author-level sheets.
>>>Creating a new map entry creates a corresponding @var rule in this
>>>stylesheet.  (Should this exist in `document.stylesheets`?  If so,
>>>where?)
>>
>> As I think about it, adding and removing @var rules should probably only
>> exist in the document.stylesheet interface.
>
>I might agree with you about removing variables.  Adding variables,
>though, is a useful ability that shouldn't require the excessive
>effort of finding a stylesheet and adding a new @-rule.
>
>
>>>To map a variable name to a key in the variables map, follow these
>>>steps:
>>>
>>>1. Remove the $ prefix from the name.
>>>2. Lowercase the remaining letters.
>>>3. Uppercase any letters that immediately follow a hyphen.
>>>4. Remove any hyphens.
>>>
>>>Given this algorithm, a variable like `$main-color` will be accessed
>>>as `css.vars.mainColor`.
>>
>> And if I had a variable named $mainColor ? You're effectively making
>>them
>> equivalent...
>
>My intention was that variable names were case-insensitive, like
>attribute names, but there's nothing about IDENTs that makes that
>necessary.  Instead, I've just dropped the special rules.  If you have
>an appropriately named variable, you can access it with vars.foo.  If
>not, just use vars['foo-bar'].  The only special rule is that vars
>don't have the $ prefix in the var map.

My concern wasn't the case sensitivity, it was:
@var $main-color red;
@var $maincolor blue;

Those should be distinct variables. You change does address that.

>
>
>> And if the value of the variable is a list of values? (like for a
>> shorthand)
>
>I don't understand the context of the question.

Consider:
@var $foo 10px 20px;

And then in my script:
var foo = document.css.vars.foo;

What is 'foo'?
Received on Friday, 11 February 2011 01:17:15 GMT

This archive was generated by hypermail 2.3.1 : Tuesday, 26 March 2013 17:20:37 GMT