57

Recently I've started using the calc(...) method within CSS. I quickly learned that code such as: width: calc(100%-2) will not work, though adding white-space before and after the - operator will fix the problem and the calc method will function properly.

After doing a bit of research I found multiple blog posts reference that the white-space is required and many have even pointed to the specification (CSS3 8.1.1) which states:

In addition, whitespace is required on both sides of the + and - operators. (The * and / operaters can be used without whitespace around them.)

Now, clearly, the spec tells us that these operators must be wrapped in white-space, but why? I've read further within the spec (through sections 8.1.2-4) and if it's explained in these additional portions, I'm not understanding the reasoning.

In simple terms, could someone explain why it was specified that calc(100% - 1) or even calc(100%/2) is acceptable syntax but not calc(100%-1)?

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
RLH
  • 15,230
  • 22
  • 98
  • 182
  • 3
    I would assume it is so that you can use negative numbers in calculations. – Turnip Dec 22 '15 at 16:08
  • 2
    @Festive I don't get why you need negative numbers there? – nicael Dec 22 '15 at 16:10
  • 6
    @FestiveTurnip That's not a good assumption. Spaces are hardly required to tell a binary `-` from a unary `-`. You'll not that no other language requires you to write `3 - 2` instead of `3-2`. – user229044 Dec 22 '15 at 16:15
  • 1
    In fact, rather than requiring whitespace around binary operators, CSS simply disallows whitespace between a sign and a number. This is true everywhere, including `calc()`, `:nth-child(An+B)` and simple length values like `-2em`. – BoltClock Dec 22 '15 at 16:41
  • Fun fact: Right now in Chrome, `100-3*20` is just parsed as `100` and treated as if you didn't do a subtraction or multiplication at all. In Safari, it just breaks the calc value entirely. – Phoenix Sep 09 '22 at 19:43

3 Answers3

47

The - character is one of the allowed characters in CSS idents. Judging by the resolution given here, it seems they wanted to prevent syntactic ambiguities that could arise from using - without whitespace, especially with keyword values such as min-content (although AFAIK keyword values aren't yet allowed within calc() — correct me if I'm wrong).

Not everyone agrees with this resolution, though.

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
  • Very interesting. I guess this could make sense, but a good parser should be able to resolve these issues. (But maybe not, since CSS is different than traditional computational code (e.g. C, C#, Java, etc.)) I guess they just through the plus-sign in there because `+` and `-` often go together? – RLH Dec 22 '15 at 16:14
  • @RLH: How would you resolve something like `min-content-2em`? Should that be interpreted as a single ident (which would almost certainly result in an invalid declaration), or should it reluctantly match `min-content` as a keyword value before consuming the rest of the tokens? I reckon even a human reader would have trouble parsing that. – BoltClock Dec 22 '15 at 16:18
  • I see your point. Hmm, me thinks allowing for `-` in idents could be bad syntax design. What's wrong with throwing `_` all over the place, instead, like every other sane language! :P But, I digress, my comments are outside the scope of this question. – RLH Dec 22 '15 at 16:21
  • 8
    @RLH: The *real* bad design is that we have a single ASCII ["hyphen-minus"](http://www.fileformat.info/info/unicode/char/2d/index.htm) character on our keyboards :P – BoltClock Dec 22 '15 at 16:22
38

The Mozilla Developer Network explains it quite well:

Note: The + and - operators must always be surrounded by whitespace. The operand of calc(50% -8px) for instance will be parsed as a percentage followed by a negative length, an invalid expression, while the operand of calc(50% - 8px) is a percentage followed by a minus sign and a length. Even further, calc(8px + -50%) is treated as a length followed by a plus sign and a negative percentage.

The * and / operators do not require whitespace, but adding it for consistency is allowed, and recommended.

Community
  • 1
  • 1
sp00m
  • 47,968
  • 31
  • 142
  • 252
  • I think this is also where I was getting confused. Example: Consider `100% +2px`, which could resolve to `100% 2px`. This doesn't make any sense, in context. Ergo, any case where a `+` or `-` exists (within a calc function`) should be considered an associative operation (ignoring the whole usage of the `-` symbol within idents.) Again, I maybe missing something here, but I think a good interpreter could properly resolve at least the plus sign? – RLH Dec 22 '15 at 16:29
  • It'd be awesome if my Sass/CSS Linter was able to catch this – Lounge9 Apr 20 '18 at 19:09
12

I think you should first consider how do CSSs identify a length. A length is defined as an optional sign followed by a module and an optional unit of measure (although many properties actually require it):

<CSSlength> := [<sign>]<module>[<unit>]

So, for example, valid CSS lengths are:

-3px
100em
+10pc
0
91
5%

Defining a length like this, the CSS engine parses the following:

calc(100% -1px);

as a length followed by another length. In this case it would be 100% followed by -1px, which doesn't make sense to calc() at all. This is also explained in the relative MDN documentation page.

In order to put two lengths in relation you need to use a distinct operator, therefore, following the above logic, you'll need to use whitespaces:

calc(100% - 1px);
Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128