Why Your calc() Function in CSS Might Be Broken
A CSS bug is a nightmare for every developer. Not only is CSS hard to debug, but it can also go wrong in so many ways.
You've fiddled with the calc()
function about a dozen times now but no matter what you try, you can't seem to figure out how to make it work.
Only if writing CSS came to you as naturally as writing JavaScript. You would immediately spot the bug and move on to other tasks.
In this article, you'll learn the common mistake developers make when writing mathematical expressions in CSS.
You'll also learn how to debug calc()
and see the computed value change in real-time. You'll finally fix the issue and make sure you'll never have to face it again.
Whitespace and mathematical expressions in CSS
There's a subtle, but important, rule in CSS when it comes to writing mathematical expressions:
The +
and -
operands must be surrounded by whitespace.
This calc()
expression won't work:
.main {
width: calc(10px+100px); /* ❌ no whitespace surrounding `+` */
}
However, adding whitespace around the +
operand fixes the issue:
.main {
width: calc(10px + 100px); /* ✅ `+` surrounded by whitespace */
}
As a backend developer, I really tripped over this one. In Node.js, whitespace around operands is irrelevant.
MDN has to say the following about how the calc()
expression is parsed:
calc(50% -8px)
will be parsed as a percentage followed by a negative length — an invalid expression — whilecalc(50% - 8px)
is a percentage followed by a subtraction operator and a length. Likewise,calc(8px + -50%)
is treated as a length followed by an addition operator and a negative percentage.
Using our example, the expression calc(10px+100px)
is parsed as a length (10px
) followed by another (positive) length (+100px
).
The plus sign (+) is mistaken for a positive integer sign instead of an addition operator which renders the entire expression invalid.
Adding whitespace around the plus sign: calc(10px + 100px)
, fixes the issue. The plus sign is now correctly parsed as an addition operator.
The /
and *
operands don't require whitespace, but adding it is recommend for consistency and to avoid confusion.
How to debug calc()
?
Debugging calc()
is a bit tricky.
With calc()
, the computed value can be different depending on which element it is used. While some expressions such as calc(10px + 10px)
always evaluate to the same result, other expressions such as calc(10px + 50%)
are dynamic and will change depending on context.
The browser evaluates the expression when it's used with an element. In other words, the final value only exists until it's been used within a property.
An, albeit hacky, way to debug calc()
is using Developer Tools in your browser. With DevTools open, select the element you want to apply calc()
on, and add a CSS property width
to it with the calc()
expression. If the expression is invalid it will be negated/struck through.
If it's valid, you'll see the result calculated as the width property of the element. In Chrome, the value is visible in the "Computed" tab. If the expression depends on the width of the browser, resize the window to see the value change in real-time.
Avoiding the issue in the future
If you're linting CSS, you can use a rule that warns you when an operand isn't surrounded by whitespace. In Stylelint, this rule is named function-calc-no-unspaced-operator
. This is an ideal solution in a working environment where you want to formalise the process among your colleagues.
Unfortunately, linting CSS doesn't cover the scenario when you're using calc()
in the sizes attribute of the img tag.
My advice is to always surround mathematical operands in CSS with whitespace. If you make this a strict rule for yourself, you won't face this issue again when writing calc()
expressions in CSS.