CSS calc() is a math function that computes a value from addition, subtraction, multiplication and division, so you can mix units such as percent and pixels in one property. It is defined in the W3C CSS Values and Units Module Level 4. The rule people trip on most is that + and - need spaces around them, so calc(10px + 5px) works and calc(10px+5px) does not.
calc() holds one calculation made of values and the four operators, with parentheses for grouping. It follows normal precedence: and / bind tighter than + and -, and equal operators go left to right. The spec's example is calc(2 + 3 4), which equals 14, while calc((2 + 3) * 4) equals 20.
The unit rules are what make errors:
100% - 40px.10px * 5px is invalid.10px / 5px is invalid in the syntax tested below.var() references that resolve to a valid type.The browser resolves percentages at layout time against the property's reference box, which is why 100% - 40px works even though the percentage is unknown when the CSS is parsed.
#a { width: calc(100% - 40px); }
#c { width: calc((2 + 3) * 4 * 1px); }
#d { width: calc(10px+5px); }
#e { width: calc(10px + 5px); }
Computed widths in headless Chromium 141 inside a 600px wide parent:
#a: 560px
#c: 20px
#d: 600px
#e: 15px
#d is invalid because of the missing spaces, so the declaration was dropped and the block div fell back to its auto width of 600px. CSS.supports also returned false for calc(10px+5px), and false for 10px * 5px and 10px / 5px.
Yes, calc() accepts var() references, which makes it the standard way to derive values from a token. In headless Chromium 141 with --gap: 8px and --n: 3, calc(var(--gap) var(--n)) computed to 24px. A unitless variable needs a unit multiplier: calc(var(--n) 1px - 2px) computed to 1px.
calc(10px+5px) is invalid and the whole declaration is ignored. Always put a space on both sides. Spaces around * and / are optional.calc(10px * 5px) is invalid. One operand must be a unitless number.calc(10px / 0) passed CSS.supports in Chromium 141, so a parse check does not protect you. The result is decided later, so treat zero divisors as a bug to avoid.calc(2 + 3 4) is 14 but not a length. Add 1px to get a length.