Whitespace in CSS Calculations
Published 12 years, 6 months pastI’ve been messing around with native calculated values in CSS, and there’s something a bit surprising buried in the value format. To quote the CSS3 Values and Units specification:
Note that the grammar requires spaces around binary ‘+’ and ‘-’ operators. The ‘*’ and ‘/’ operators do not require spaces.
In other words, two out of four calculation operators require whitespace around them, and for the other two it doesn’t matter. Nothing like consistency, eh?
This is why you see examples like this:
width: calc(100%/3 - 2*1em - 2*1px);
That’s actually the minimum number of characters you need to write that particular expression, so far as I can tell. Given the grammar requirements, you could legitimately rewrite that example like so:
width: calc(100% / 3 - 2 * 1em - 2 * 1px);
…but not like so:
width: calc(100%/3-2*1em-2*1px);
The removal of the spaces around the ‘-’ operators means the whole value is invalid, and will be discarded outright by browsers.
We can of course say that this last example is kind of unreadable and so it’s better to have the spaces in there, but the part that trips me up is the general inconsistency in whitespace requirements. There are apparently very good reasons, or at least very historical reasons, why the spaces around ‘+’ and ‘-’ are necessary. In which case, I personally would have required spaces around all the operators, just to keep things consistent. But maybe that’s just me.
Regardless, this is something to keep in mind as we move forward into an era of wider browser support for calc()
.
Oh, and by the way, the specification also says:
The ‘calc()’ expression represents the result of the mathematical calculation it contains, using standard operator precedence rules.
Unfortunately, the specification doesn’t seem to actually define these “standard operator precedence rules”. This makes for some interesting ambiguities because, as with most standards, there are so many to choose from. For example, 3em / 2 * 3
could be “three em divided by two, with the result multiplied by three” (thus 4.5em) or “three em divided by six” (thus 0.5em), depending on whether you privilege multipliers over dividers or vice versa.
I’ve looked around the Values and Units specification but haven’t found any text defining the actual rules of precedence, so I’m going to assume US rules (which would yield 4.5em) unless proven otherwise. Initial testing seems to bear this out, but not every browser line supports these sorts of expressions yet, so there’s still plenty of time for them to introduce inconsistencies. If you want to be clear about how you want your operators to be resolved, use parentheses: they trump all. If you want to be sure 3em / 2 * 3
resolves to 4.5em, then write it (3em / 2) * 3
, or (3em/2)*3
if you care to use inconsistent whitespacing.
Comments (18)
Good research, Eric! I hadn’t heard that expressions like this were a part of general CSS (since I’ve been focused on SCSS so much).
Eric, nice article but I cannot agree with you on the mathematics. The article on Wikipedia you linked to states this:
The order of operations, or precedence, used throughout mathematics, science, technology and many computer programming languages is expressed here:[2]
* terms inside parentheses or brackets
* exponents and roots
* multiplication and division
* addition and subtraction
And also this:
These mnemonics may be misleading when written this way, especially if the user is not aware that multiplication and division are of equal precedence, as are addition and subtraction. Using any of the above rules in the order “addition first, subtraction afterward” would also give the wrong answer.
So 3em / 2 * 3 equals 4.5em as multiplication and division are of equal precedence; thus should be read from left-to-right. If you multiply first, you are reading from right-to-left and disregard the equality precedence.
This is just my opinion, but I think the whitespace around the + and – operators as seen in your example above makes the order of operations much more readable. Should it be required? Maybe not. Could it be a best practice? Maybe so.
I’m not seeing any browser doing the multiplication before the division in that scenario. As the article you link to explains, division and multiplication have the same precedence (hence the differing mneumonics), as do addition and subtraction. They’re just evaluated left to right.
In Chrome (Canary), Firefox, and IE 9, this test fiddle (http://jsfiddle.net/bdukes/KfL7a/2/) shows the calculation being left to right without inconsistency.
Great to understand the spacing inconsistency, I’m sure that’ll trip up a lot of people (definitely something for CSS minifiers/processors to understand)
I think the first example expression is MUCH easier to read (since you have to adhere to the order of operations). Your last two examples just make my eyes cross and do weird things.
That being said, I fully agree that consistency (especially when writing up specs people have to adhere to) is key, and there’s absolutely no reason why the last example should barf and break everything.
The implication is that infix notation is assumed, but… not documented?
Eric, the standard order of operations rules, both in the US and everywhere that mathematics is taught, are that * and / have the same precedence and you evaluate them left to right. The wikipedia page you link to clearly describes this. The particular section you linked to isn’t about different “standards”. It’s about different _mnemonics_ (some incorrect) that people try to use to remember the order.
So your example should end up with 4.5em. That’s what Gecko does, certainly. Which browser did 0.5em in your testing?
That all said, I agree that using parentheses to disambiguate expressions like this is absolutely the right thing for human-readability, precisely because most people don’t actually remember their order of operations very well.
Sorry, Boris, Brian, and Bouke—that was a typo which I’ve now fixed: I was sure I’d mistakenly switch up 0.5em and 4.5 em, and so I did. Next time I need to build an example that yields wildly different results so there’s less chance of a mistake.
As for the difference between standards and mnemonics, well, if someone learns a mnemonic they regard it as a standard. Learn the wrong mnemonic, learn the wrong standard. I still say the specification should state explicitly which ordering precedence it follows just so there’s less chance of mistake.
I sense a theme…
yeah, what Boris said.
“depending on whether you privilege multipliers over dividers or vice versa.”
no, no (sane) system prioritizes multiplying over dividing (or vice versa). it’s always in the order it’s written (unless it’s polish/reverse-polish/postfix/.. notation).
So, on which day are we celebrating Bidmas this year?
I’m open to any reasonable suggestions, Chris! Regardless of date, though, I think the celebrations have to be held on eBay.
I would imagine that The spaces around adition and division are required because of the grammar defining values. Whereas “2em/2” is unambigously a division operation, “4em-2em” presumably looks like “(4em)(missing operand)(-2em)”. I agree it’s a -little- stupid, but presumably there was a very, very good reason not to complicate the grammar to make this more intuitive for minimalists. For what it’s worth I agree that all operations should consistently require spaces instead.
Oops. That should have read “(missing operator)”, of course…
J. King, you’re right in general, but to a CSS parser your example looks like the number “4” followed by the unknown unit “em-2em”. The relevant parts of the CSS tokenizer spec:
DIMENSION {num}{ident}
ident [-]?{nmstart}{nmchar}*
nmstart [_a-z]|{nonascii}|{escape}
nmchar [_a-z0-9-]|{nonascii}|{escape}
so “em-2em” is a perfectly valid “ident” and the whole thing is a single DIMENSION token as far as the tokenizer is concerned. Then the parser sitting on top of the tokenizer has to drop the whole thing because the actual ident is not a known unit, but making that somehow work would require completely redefining how CSS parsing works…
Pingback ::
Dew Drop – April 11, 2012 (#1,304) | Alvin Ashcraft's Morning Dew
[…] Whitespace in CSS Calculations (Eric Meyer) […]
Pingback ::
Some links for light reading (13/4/12) | Max Design
[…] Whitespace in CSS Calculations […]
I’ve been looking for a standards-based way to do stuff like this since way back in the days of IE-only “behaviors,” but I can’t get FF, Chrome or IE9 to recognize calc. What browsers support this?
Doug, Firefox supports calc() with a “-moz” prefix. IE9 supports it without a prefix, as I recall. Current Chrome may support it with a -webkit prefix, maybe.