Re: CSS Variables Draft Proposal

On Mon, Feb 14, 2011 at 1:28 PM, Boris Zbarsky <bzbarsky@mit.edu> wrote:
> On 2/14/11 3:46 PM, Tab Atkins Jr. wrote:
>>
>> This term is underdefined for my usage,
>> and perhaps not exactly what I want, though.
>
> OK.
>
>> You can't put a unit in and expect to use it as a unit, for example, the
>> "@var $foo
>> px;" is perfectly fine if used as a keyword.  This shouldn't be hard -
>> the intent is just that you can't store a "partial value" in a
>> variable and then compose it with something else to get a whole value
>> (so you can't do something like "@var $foo px; p { width: 200$foo;
>> }").
>
>>
>> I heard conflicting statements about whether "token" was correct here,
>> so I just avoided the issue and used a different word.  What is the
>> correct term?
>
> I think the problem you're having is that this concept of "value" is not
> really exactly how the CSS spec is defined at the moment, and different UAs
> have different internal concepts of "value".  At least as far as I can tell.
>
> Offhand, I wouldn't be willing to claim that the same string is always
> treated as the same kind of "value" in Gecko, even.  It might well be
> context-dependent.  I'm not saying that's the case; just that nothing
> ensures that it's not.
>
> I agree that a raw token stream may not be the right thing due to things
> like:
>
>  @var foo 255, 255);
>
> which could add pretty oddly if $foo is used like so:
>
>  color: rgb(0, $foo, 0);
>
> (though in this case I think it'll just cause the whole property to be
> discarded).  But if we require that any close parens/curlies/brackets be
> matched by open parens/curlies/brackets in the variable definition, then it
> seems like a token stream with that restriction might be ok.  It would
> certainly make it much simpler to specify how variable substitution should
> work: you just tokenize the template, replace the $foo with the
> corresponding token stream, and then parse the resulting token stream.
>
> If you want to do this in terms of values, then you have to define somewhere
> what the value sets for various properties are, which sounds like a pretty
> major undertaking.

I think that "token stream with matched parens/braces/brackets" might
be what I need.  I'll run with that until someone tells me otherwise.


>> "component value" is defined in CSS2.1, at
>> <http://www.w3.org/TR/CSS21/about.html#value-defs>.  It's not exactly
>> what I want, but it appears to be closer in intent than "token".
>
> Hmm.  So the problem is that nothing guarantees that different value types
> as defined here will be syntactically distinct (and in fact they're not).
>  Put another way, you can't tell what sort of value it is until you see how
> it's being used.  That seems unfortunate.

Indeed.


>>> Currently in a situation like that (same property specified multiple
>>> times
>>> in a declaration) only the last specified value needs to be kept by the
>>> UA.
>>>  It sounds like your proposal is that this is no longer the case with
>>> variables, right?
>>
>> Yes, though your gloss isn't completely correct, right?  If you make a
>> declaration block contain the same property twice, and use the CSSOM
>> to twiddle whether the second one's value is valid or not, you have to
>> pay attention to the first one.
>
> Gecko certainly doesn't.  Invalid stuff is dropped at _parse_ time and not
> exposed to the CSSOM at all.
>
> In particular, up until now invalid stuff has always been dropped at parse
> time, since the whole point is that if you don't know what it is you can't
> parse it apart from just skipping over it.
>
>> Do you just let this case fall down a
>> slow path, where you effectively reparse the block?
>
> No; this case simply doesn't arise right now.  You're introducing it, by
> requiring some sort of non-parse-time discarding behavior.

What about a case like this:

<style>
p {
  color: red;
  color: blue;
}
</style>
<script>
/* use the CSSOM to change the value of the
   second 'color' property to '12px'
*/
</script>

Do you drop the first color declaration at parse time, so that the
above would leave the block with *no* valid 'color' declarations?  Or
do you keep both of them around?


>>>> Scoped stylesheets (those created with a `<style scoped>` element in
>>>> HTML) have their own nested global scope.  Variables created or
>>>> imported within a scoped stylesheet are only available within the
>>>> scoped stylesheet; variables created in the outer global scope are
>>>> still available in a scoped stylesheet.
>>>
>>> I'm not sure I follow this.  Say I have this markup:
>>>
>>>  <div>
>>>    <p>
>>>    </p>
>>>  </div>
>>>
>>> with stylesheets scoped to the<div>  and<p>.  If I have an @var in the
>>> div-scoped sheet, can the p-scoped sheet use it?  Note that rules in the
>>> div-scoped sheet apply to the<p>  and all, in general.
>>
>> No.  This is defined by HTML - I'm just restating the restrictions
>> that<style scoped>  applies, for clarity.
>
> What you're stating is different from what the HTML5 draft says about <style
> scoped> as far as I can tell.  Again, the div-scoped sheet's rules apply to
> the <p> if I read the <style scoped> draft correctly, but you're saying its
> @vars do not?

I'm not sure how to answer your question, because I'm not entirely
sure what you mean by "its @vars do not".

I'll give an example that should hopefully be clearer:

<style>
  @var $foo blue;
  p { text-shadow: 2px 2px $foo; }
</style>
<div>
  <style scoped>
    @var $foo red;
    p { color: $foo; }
  </style>
  <p>text text text
    <style scoped>
      p { background-color: $foo; }
    </style>
  </p>
</div>

In this example there are 3 uses of the $foo variable, and two
definitions of $foo.

The first use, in the global scope, draws from the global definition,
so the text-shadow color is blue.

The second use, in the div's child scoped stylesheet, draws from the
definition in the scoped stylesheet instead, so the text color is red.
 If this scoped stylesheet @import'd another sheet, it would also get
$foo=red.

The third use, in the p's child scoped stylesheet, doesn't have it's
own $foo definition, and can't use the div's child scoped stylesheet's
definition, since it's in a private scope, so it uses the global
definition.  Thus, the background-color is blue.

I checked with Hixie before I sent my last email to verify that I was
saying this correctly.


>> It'll be overrideable, so I doubt it'll cause any problems.
>
> You mean replaceable?

Yes, thank you.


> It can still cause problems even so (esp. if multiple scripts interact, one
> of which writes it and one of which wants to mess with your new APIs).

Oh, of course.  But those aren't legacy constraints, they're problems
going forward and can be fixed by the author as they come up.


>> I'd also like there to be a window.css which forwards to
>> window.document.css, for ease of use.
>
> That seems to have even more scope for problems.

The scope is larger, but the problems are the same.


>>>> 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.
>>>
>>> What about adding other rules to the sheet?  Would they be applied to the
>>> document?
>>
>> It acts like a stylesheet in the document, so yes.
>
> So a question.... apart from the handling of !important, how is this
> different from the override sheet stuff CSSOM specifies already?

Where is the override sheet defined?


>>>> Variables appear as themselves in specified values. If the variable is
>>>> defined and valid, its computed value is the value of the variable. If
>>>> not,
>>>> its computed value is the variable name.
>>>
>>> I don't understand this at all, if invalid values are supposed to be
>>> treated
>>> like parse errors....  What is this trying to say?
>>
>> Invalid values are no longer parse errors, since some time before you
>> quoted this out of the draft.
>
> That doesn't answer my question.  Consider this style:
>
>  div { color: red; color: $foo; }
>  p { color: $foo; }
>
> What is the specified value of "color" for <div>s?  What is the computed
> value of "color" for <p>s?  How do I reconcile those answers with the text
> quoted above?

Specified value for each is "$foo".  Computed value for each is
whatever $foo resolves to, if it's defined and valid.  If not, it's
"$foo".


>>> 2)  Can the type be changed via the CSSOM?  I assume yes, to make Daniel
>>> happy.  ;)
>>
>> Yeah.
>
> A followup: what happens if you try to change to an unknown type?

The same thing we'd do for setting a value that doesn't agree with the
type.  Either ignore it or throw an exception or something similar
that we decide on.


>>>  @var color $foo 12px;
>>>  * { font-size: $foo; }
>>>
>>> then do I get 12px font-size?  Or is the variable considered invalid if
>>> its
>>> value in the @var can't be parsed as its type?
>>
>> The validity of the variable can be verified at parse time in this
>> proposal, so the $foo declaration would be invalid, and no $foo
>> variable would be created.  The font-size declaration is then invalid,
>> as it references an undefined variable.
>
> OK (though this is not clear from the spec).

Right, that part is just a bare-bones idea, alongside the other two
ideas, so it's less fleshed out.  Sorry.


> Let's try a more interesting
> testcase:
>
>  @var color $foo red;
>  * { font-family $foo; }
>
> If I have a font with a family name of "red" on my system, will I get it?

I think no.  If a variable is given a type, it must be used as the
type commands.

I'm not wedded to this answer, if it turns out bad.  It's possible
that all we do with the type is expose CSSOM interfaces, but otherwise
typed variables are the same as untyped, and you can use them in
anything that would accept that value.


>>>> The previous suggestion seems to put the typing in the wrong place.
>>>> Typing doesn't help the CSS developer in any way, as CSS can figure
>>>> out types as necessary all by itself.
>>>
>>> Maybe... and maybe not.  It sort of depends on what variable values
>>> "are".
>>>  See beginning of this mail.
>>
>> I mean that you can figure out types at the time of use.  You can't
>> possibly infer types at definition time, as there is too much
>> ambiguity.
>
> OK, but my point is that sometimes you can't really figure out types at time
> of use either, without falling back on the actual tokens involved.

Yes.


>>>> This would only work if the OM interfaces were carefully designed in
>>>> such a way that there is never ambiguity
>>>
>>> Seems fragile....
>>
>> I agree.  We want to try this and see if it works, though, before
>> throwing it out.
>
> The problem is that by the time we decide it doesn't work the damage will
> have been done: we'll have interfaces we can't drop for compat reasons but
> that will sort of suck in actual use.  See getComputedStyle as it is
> currently practiced.

We should be able to figure out whether or not this sucks during the
spec/experimental implementation stage, before it reaches the point
where compat becomes an issue.

~TJ

Received on Monday, 14 February 2011 21:58:39 UTC