Re: CSS: %% length unit. Proposal.

On Fri, 7 May 2004, Boris Zbarsky wrote:
> More interestingly:
>    foo, bar { display: block; }
>    foo { width: calc(50% - 100px;) }
>    bar { width: inherit; }
>    <foo>
>      <bar />
>    </foo>
> What does bar inherit?

The computed value. The computed value cannot depend on layout.
Expressions containing percentages would depend on layout. Therefore the
computed value can't be a resolved expression. Therefore what is inherited
must be the expression itself.

But of course given:

   foo { border-left: 1em solid; border-right: calc(1em - 0) solid; }
   bar { border: inherit; font-size: 2em; }

...and the markup above, we want <bar> to have equal borders on the left
and right. Thus it actually has to be the calc() expression with all
lengths resolved to absolute lengths or percentages.

> Yes, but _when_ the calculation happens is key.

The expression is normalised to absolute lengths and percentages during
the specified->computed value stage, and evaluated during the computed->
used value stage.

> Also, conversion to px is lossy in many cases, leading to rounding
> issues.

That's a UA-specific implementation detail (and probably bug).

> I'm not sure what I think of the %% proposal yet, but calc() has some
> issues that would need to be ironed out....

The full calc() proposal is:

We could introduce a function calc() that can be used wherever <length>
can be used, with the syntax given below.

The expressions would be normalised to absolute lengths and percentages
during computation (same as for resolving 'em' units), and then evaluated
to final lengths during layout.


  section {
    float: left;
    margin: 1em; border: solid 1px;
    width: calc(100%/3 - 2*1em - 2*1px);

  p {
    margin: calc(1rem - 2px) calc(1rem - 1px);
    border: solid transparent; border-width: 2px 1px;
  p:hover { border-color: yellow; }


This is a simple grammar that just supports five arithmetic operators (+
and - have lowest precedence, *, /, and 'mod' have highest precedence)
and parentheses. At a later date we can maybe introduce new operators such
as min/max, conditionals, etc, and maybe new constants. However, I don't
think we should do this at this stage.

  <length> := calc( <length-expression> ) | <atomic-length>

  <length-expression> := <length-additive-expression>

  <length-additive-expression> :=
     <length-multiplicative-expression> |
     <length-additive-expression> '+' <length-multiplicative-expression> |
     <length-additive-expression> '-' <length-multiplicative-expression>

     <length-term> |
     <length-multiplicative-expression> '*' <number-term> |
     <number-multiplicative-expression> '*' <length-term> |
     <length-multiplicative-expression> '/' <number-term> |
     <length-multiplicative-expression> 'mod' <number-term>

  <length-term> := '(' <length-expression> ')' | <atomic-length>

  <number-additive-expression> :=
     <number-multiplicative-expression> |
     <number-additive-expression> '+' <number-multiplicative-expression> |
     <number-additive-expression> '-' <number-multiplicative-expression>

  <number-multiplicative-expression> :=
     <number-term> |
     <length-multiplicative-expression> '/' <length-term> |
     <length-multiplicative-expression> 'mod' <length-term>

  <number-term> := '(' <number-additive-expression> ')' | <number>

  <atomic-length> := <number><length-unit> | '0'+

(Note: I used 'mod' instead of '%' for modulus since it is very easy to
get confused about whether '%' is acting as a unit or an operator. At
least with 'mod' it always causes a parse error -- invalid unit -- in the
otherwise ambiguous cases.)

Ian Hickson                                      )\._.,--....,'``.    fL
U+1047E                                         /,   _.. \   _\  ;`._ ,.                         `._.-(,_..'--(,_..'`-.;.'

Received on Saturday, 8 May 2004 08:42:28 UTC