21

The CSS Values and Units Module Level 3 states:

...for zero lengths the unit identifier is optional (i.e. can be syntactically represented as the <number>‘0’).

This is why most of us drop the unit identifier when dealing with 0s, as 0px, 0em, 0% and so on all evaluate to the same length.

The same documentation also states that the calc() function:

...can be used wherever <length>, <frequency>, <angle>, <time>, <number>, or <integer> values are allowed. Components of a calc() expression can be literal values, attr() or calc() expressions, or <percentage> values that resolve to one of the preceding types.

The problem here is that the latest versions of Chrome, Firefox, Opera and Internet Explorer treat calc(0) as an invalid expression. The 0 here is a value which resolves to a <number> type (as we're made aware from the first snippet I quoted in this post).

If we try and validate width: calc(0) through W3C's own CSS Validator, we're presented with the following error:

Value Error : width calc(0) is not a width value : calc(0)

However if we try and validate width: 1, we're instead given the following error which appears to contradict this error:

Value Error : width only 0 can be a length. You must put a unit after your number : 1


Example

In this example a 1px red border is applied to each code element. In an attempt to override the border's width, the first has calc(0) specified as the border width, and the latter has calc(0px).

code {
  display: inline-block;
  padding: 2px;
  
  border: 1px solid red;
}

code:first-of-type {
  border-width: calc(0);
}

code:last-of-type {
  border-width: calc(0px);
}
<code>border-width: calc(0);</code>
<code>border-width: calc(0px);</code>

For those using browsers which do not support CSS's calc() function, here is an image of the result I see in Chrome (v39):

Example Image

If we look at this in Chrome's Element Inspector, we'll see that calc(0) is indeed deemed invalid:

Chrome Element Inspector


Is there a reason behind why calc(0) is treated as an invalid expression? Is the number 0 not a value which is considered "literal"?

Community
  • 1
  • 1
James Donnelly
  • 126,410
  • 34
  • 208
  • 218
  • 3
    Interesting to note that this also applies to 0s as part of expressions (e.g. `calc(0 + 1px)`), meaning this isn't simply that implementations need to know the dimension of the calc result for dimensional analysis. But then, I don't see when this would be relevant: the 0 shorthand is for saving typing, but in a calc expression you can always just omit the 0s anyway. And any dynamic code generation can always specify a `px` suffix (or whatever is appropriate). So while it's an interesting edge case, I don't see any practical value to accepting it (and it would likely complicate the code) – Dave Jan 07 '15 at 22:52
  • @Dave oddly `0` can be used in multiplication: `calc(0 * 1px)`. Heck, you can even `calc(0 * 0px)`. – James Donnelly Jan 07 '15 at 22:54
  • 3
    That makes sense; the rules of dimensional analysis say that the dimensions also get multiplied in that case, so you have (1) * (px) giving (px) which is valid. You would NOT be allowed to do `0px * 0px`, because that would give `0 px^2`, which is not a length. In fact, thinking about it that way, this makes sense. The calc expression sees 0 as a numeric constant (e.g. a ratio), and since it doesn't know how it will be used it can't infer a type to append. This means the calc expression returns a numeric constant, which is not a valid width (which can be length or percent) – Dave Jan 07 '15 at 22:57
  • 1
    All these "0s" in the question and comments are messing with me because "0s" in CSS can mean "0 seconds" :) Granted, it's not a length but a time (which makes it [exempt from the unitless zero rule altogether](http://stackoverflow.com/questions/13145352/units-on-0s-transition-in-firefox/13145406#13145406)), but still... – BoltClock Jan 09 '15 at 13:20
  • @BoltClock haha, you had me really confused there for a moment, wondering about the possibility of defining widths in seconds! – James Donnelly Jan 09 '15 at 13:23
  • 4
    I'm not confident enough to post an answer of my own, but I did find this: http://lists.w3.org/Archives/Public/www-style/2010Dec/0479.html which seems to say that if you look really closely, the spec does in fact consider unitless zeros invalid for `calc()` lengths. It doesn't really explain why however, beyond "unitless zero is ambiguous", which is in line with what @Dave said. – BoltClock Jan 09 '15 at 14:54
  • @BoltClock nice find! You'd assume that a simple check for *'if calculated n is equal to 0'* would be all that's required to rule out ambiguity - especially as the computed value for `border-width` in Chrome becomes `0px` even if you specify `calc(0em)`. – James Donnelly Jan 09 '15 at 15:03
  • "the possibility of defining widths in seconds": well once Hz are supported, you could technically do `width:calc(3s * 10Hz * 2px)`! (in fact, that sort of thing would also make 0 short-hand even more complicated: `width:calc(0 * 0 * 2s)` is valid if one of those 0's is Hz and the other is a length!) – Dave Jan 09 '15 at 18:53
  • @Dave: The spec doesn't allow mixing types anyway, so that would not be an issue altogether :) – BoltClock Jan 10 '15 at 04:14

1 Answers1

3

It's basically what @Dave and @BoltClock say:

Section 8.1.2 of that spec says that the resolved type of the math expression calc(0) is determined by the types of the values it contains, which for a NUMBER token like 0 is a <number> or <integer>. Neither of those types is valid as a width value, which takes a <length> or <percentage>, so the expression is invalid.

But if you use 0 as the value, it can be parsed as a <length>, per section 5 of the spec as you quoted.

calc(0) isn't syntactically (or lexically, as we see) equivalent to 0, so section 5 doesn't apply to it.


Or were you asking a philosophical question about why the spec was written so?

Mike
  • 323
  • 3
  • 13
  • Why would `calc(0)` *not* be equivalent to `0`? – James Donnelly Jan 11 '15 at 18:45
  • I said that `calc(0)` is not _syntactically_ equivalent to `0`, and it should be obvious why: they are different strings, `calc(0)` starts with "`c`", etc. – Mike Jan 11 '15 at 20:56
  • `calc()` is a function, not a value, and the part you've summarised to me suggests that `calc(0)` should return the NUMBER `0`, which the user agent would treat as if `0` had been specified in the first place. The NUMBER `0` **is** valid as a `width` value, which I've quoted right at the start of my question (I can't link directly as w3's website seems to have a redirect loop at the moment). I don't feel that this answers the question because it doesn't give a definitive answer of why `calc(0)` is invalid - it only speculates. – James Donnelly Jan 12 '15 at 09:33
  • "which the user agent would treat as if 0 had been specified in the first place" is where you're wrong. Read section 5 of the spec, which you quote: "can be **syntactically** represented as the ‘0’". I think you're hung up not understanding the difference between syntax and semantics (which is common among people who've never written a parser). Yes, `calc(0)` resolves to or `0`; but that's irrelevant to section 5. – Mike Jan 12 '15 at 20:37
  • The problem is that the part you're referring to is an example of what it can be syntactically represented as. It doesn't say that it must be the `` 0 and not the result of the `calc()` function. – James Donnelly Jan 13 '15 at 00:33
  • That's not what I read. It uses "i.e.", not "e.g.". This isn't just an example, it's a clarification of what's necessary. (Now, that may include "`0.0`" or "`+00`" or other syntactic equivalents, so not _just_ a literal "`0`".) – Mike Jan 13 '15 at 03:22
  • To clarify the syntactic distinction, read section 4.2: "A number... corresponds to the NUMBER token", vs. section 8.1.1: 'calc : "calc(" S* sum S* ")"'. So `calc(...)` is a "calc" expression, _not_ a NUMBER token, in the syntax, and is thus not syntactically a . – Mike Jan 13 '15 at 03:41
  • the problem that I find there is that the `calc()` section states that `calc()` can resolve to ``. – James Donnelly Jan 13 '15 at 09:08
  • 1
    That resolution can only happen after a lexical analysis parses the tokens, builds the AST, matching it to the grammar rules, and evaluates the typed expression. That goes beyond the syntactic level. That's why I keep emphasizing [syntax vs. semantics](http://en.wikipedia.org/wiki/Syntax_%28programming_languages%29#Levels_of_syntax). – Mike Jan 13 '15 at 18:31