W3C home > Mailing lists > Public > www-style@w3.org > May 2012

[css-variables] Variables used without prefix or function

From: Christoph Päper <christoph.paeper@crissov.de>
Date: Thu, 24 May 2012 16:54:28 +0200
Message-Id: <8F657AE5-9FCF-41BA-B88B-E57195F594CE@crissov.de>
To: www-style list <www-style@w3.org>
Christoph Päper:
> I’m still a fan of using variable names verbatim, i.e. no prefix, no function notation, no nothing:

Proposed Syntax (Example)

>  color: foo-bar;
>  var_foo-bar: blue;

  color: foo-bar !important;
  color: foo-bar !override;

> if variables were just user-defined keywords we needed to come up with a system that establishes the order of preference so authors either could or could not overload existing and future keywords.

Usage Scenarios

Usually authors will use variable names that are not equivalent with any keyword. Even if they do, said keyword might not be valid where the variable is used, so there’s no ambiguity either. When authors use a keyword for a variable’s name they usually do so by intention. Hardly ever there is a new keyword added to a property later on and very rarely this could result in a clash in existing stylesheets with new browsers that was not foreseen by the author. Even when that happens, in many cases the new official keyword will do something similar to what the author originally intended.

Proposed Precedence Policy

 – Global keywords (‘inherit’, ‘initial’, ‘default’ 
   are illegal to use as variable names. 
   (‘normal’, ‘none’ and ‘auto’ probably should be, too.)

 – By default an existing keyword (supported by the UA) 
   takes precedence over a declared variable.

 – A unit, pseudo-function name or priority marker
   can never be overridden by a variable.

 – A user-defined keyword from another source,
   e.g. a custom font family from ‘@font-face’, a named page,
   a counter or its style from ‘@counter-style’,
   can never be overridden by a variable.

 – If the ‘!important’ priority marker is specified 
   for the declaration, any variable takes precedence, 
   but if that results in an invalid declaration 
   it falls back to the pre-defined keyword,
   which also may be invalid there, though.

 – If the (new) ‘!override’ priority marker is specified 
   for the declaration, any variable takes precedence 
   and possibly invalidates an otherwise valid declaration.

 – Both markers apply to all variables in a declaration, 
   because per the grammar, only one of the priority markers 
   (‘!’ followed by either ‘important’ or ‘override’) 
   can be used at a time and only once per declaration.

 – For matters of the cascade ‘!override’ equals ‘!important’
   (Maybe there needed to be two additional equivalent markers 
   that had no impact on the cascade whatsoever).

Frequent Demands

>> I need a default value if the variable is invalid where used.

This is usually not needed, because one would just use an appropriate fallback keyword as the variable’s name. A better solution would be stronger typed and automatically cast variables, e.g.

  length_foo:    20px;
  color_foo:     blue;
  keyword_foo:   solid;
  complex_foo:   20px length(foo);
  border-width:  foo; /* = length(foo) */
  border-color:  foo; /* = color(foo) */
  border-style:  foo; /* = keyword(foo) */
  border:        foo;
  border-radius: complex(foo);

>> I want to use the value the variable had in this element’s parent.


  parent {var_foo: red;    color: foo;}          /* red */
  this   {var_foo: orange; color: foo !inherit;} /* red */
  child  {var_foo: green;  color: foo;}          /* green */


  parent {var_foo: red;    color: foo;}          /* red */
  this   {var_foo: orange !defer; color: foo;}   /* red */
  child  {var_foo: green;  color: foo;}          /* green */

>> Variables must be able to contain more and less than one “atomic” value, 
>> e.g. a comma-separated list of values or a pseudo-function argument.”

  :root {var_foo: 100;}
  bar   {color: rgb(foo, foo, foo);}    /* valid */
  baz   {color: rgb(foo%, foo%, foo%);} /* invalid */
  quz   {color: rgb(foo);}              /* invalid */

  :root {var_foo: 100, 50, 25;}
  bar   {color: rgb(foo);}

  :root {var_rgb: rgba;}
  bar   {color: rgb(100, 50, 25)}       /* valid, no var use */
  bar   {color: rgba(100, 50, 25, 0)}   /* valid, no var use */
  bar   {color: rgb(100, 50, 25, 0)}    /* invalid, no var use */

>> My variable should cycle through two or more values, advancing one step each time it is used.

Each variable aligns with a counter of the same name (remember that counter names always take precedence over variable names). The ‘cycle()’ pseudo-function takes as a second parameter the counter it should use and increment by one afterwards.

  :root {var_foo: cycle(red green, foo);}
  bar   {color: foo; background-color: foo;} /* alternating red-red and green-green */
  baz   {counter-increment: foo;}

  :root {var_foo: red; var_bar: green;}
  baz   {color: cycle(foo bar);}      /* anonymous counter for this selector */
  baz   {color: cycle(foo bar, foo);} /* explicit existing counter */
  baz   {color: cycle(foo bar, quz);} /* explicit new counter */

>> What happens if I use variables or priorities inside variable declarations?

  var_foo: 0 !important;    /* higher specificity of assignment */
  var_foo: 0 !override;     /* same as with ‘!important’ */
  var_foo: bar;             /* loses against declarations above */

  var_bar: baz;
  var_foo: bar;             /* treated as keyword ‘bar’ */
  var_foo: bar !important;  /* almost the same as “var_foo: baz;”,
                               doesn’t fall back to keyword ‘bar’ */
  var_foo: bar !override;   /* almost the same as “var_foo: baz;”,
                               except when ‘baz’ is not specified */
Received on Thursday, 24 May 2012 14:56:57 UTC

This archive was generated by hypermail 2.4.0 : Friday, 25 March 2022 10:08:16 UTC