CSS calc() Not Working — Every Cause and Fix With Code Examples
Quick answer
💡The most common reason CSS calc() silently fails is missing spaces around the + and - operators. calc(100%-20px) is invalid; calc(100% - 20px) is required. Division must use a unitless divisor: calc(100px / 2) is valid but calc(100px / 2px) is not. Spaces around * and / are optional but required around + and -.
Error symptoms
- ✕
calc() expression is written correctly but the property has no effect - ✕
Layout breaks at certain viewport widths where calc() result should constrain the value - ✕
Custom property used inside calc() resolves to zero or the browser ignores the rule - ✕
calc() works on desktop but the layout collapses on mobile - ✕
Nested calc() compiles without errors in SCSS but the browser does not apply the result - ✕
calc() inside a SVG attribute does not apply any sizing
Common causes
- •Missing whitespace around + or - operators: calc(100%-20px) is a parser error
- •Dividing by a value with units: calc(200px / 2px) is invalid; the divisor must be unitless
- •Multiplying two values that both have units: calc(2rem * 1px) is invalid
- •Using var() inside calc() where the custom property has not been defined or is empty string
- •Using calc() in a media query condition: @media (max-width: calc(var(--bp))) does not work
- •Division or subtraction inside an SVG attribute where calc() is not supported
When it happens
- •When centering elements with calc(50% - 120px) and forgetting the spaces
- •When building a responsive grid with calc((100% - var(--gap) * 3) / 4)
- •After copying a calc() expression from a design tool that omits spaces
- •When using a SCSS function that generates calc() and the output collapses the spaces
- •When trying to create a fluid type scale with calc() inside a clamp()
Examples and fixes
Spaces around + and - are required by the CSS spec; missing them causes the parser to reject the entire declaration.
Missing spaces around operators
❌ Wrong
.sidebar {
width: calc(100%-260px);
padding: calc(1rem+8px);
}
.content {
margin-left: calc(260px+2rem);
}✅ Fixed
.sidebar {
width: calc(100% - 260px);
padding: calc(1rem + 8px);
}
.content {
margin-left: calc(260px + 2rem);
}The CSS parser treats calc(100%-260px) as a single token, not as subtraction, and rejects the declaration. The parser requires spaces on both sides of + and - operators so that it can distinguish subtraction from a sign prefix on the second operand. The * and / operators do not require surrounding spaces but adding them is considered best practice for readability. After adding spaces, the mixed-unit expressions become valid: the browser can subtract pixels from a percentage because the result is computed at paint time when the container's pixel width is known.
Dividing by a px value is a type error in the CSS spec — the divisor must be dimensionless.
Division by a value with units
❌ Wrong
.grid-item {
/* Trying to divide 960px by 4px to get a ratio */
width: calc(960px / 4px);
gap: calc(100% / 3px);
}✅ Fixed
.grid-item {
/* Divide by a unitless number */
width: calc(960px / 4);
gap: calc(100% / 3);
}
/* Modern alternative for fluid columns */
.grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
}In CSS, division inside calc() requires the right-hand operand to be a number without any unit, because dividing a length by a length would produce a dimensionless ratio — not a valid CSS length value. calc(960px / 4px) would mathematically produce 240 with no unit, which cannot be used where a length is expected. The browser rejects the declaration. The fix divides by the plain number 4. For responsive grid layouts, CSS Grid's 1fr unit is a cleaner solution than calc() arithmetic because the browser handles the column width and gap distribution automatically.
A calc() expression that references an undefined custom property resolves to an invalid value at computed time.
calc() with var() where the custom property is undefined
❌ Wrong
:root {
/* --spacing is never declared */
}
.card {
padding: calc(var(--spacing) * 2);
width: calc(100% - var(--sidebar-width));
}✅ Fixed
:root {
--spacing: 1rem;
--sidebar-width: 260px;
}
.card {
padding: calc(var(--spacing) * 2);
width: calc(100% - var(--sidebar-width));
}
/* Or use var() fallback for resilience */
.card-safe {
padding: calc(var(--spacing, 1rem) * 2);
width: calc(100% - var(--sidebar-width, 260px));
}When a custom property referenced inside calc() is not defined on any ancestor, var() returns the guaranteed-invalid value, which invalidates the entire declaration at computed value time. The element receives no padding or width from that rule. The primary fix is to declare the custom properties on :root or a parent element before using them. As a defensive fallback, var() accepts a second argument: var(--spacing, 1rem) uses 1rem if --spacing is undefined. This makes the calc() expression resilient to missing variable declarations in environments where CSS may be partially loaded or overridden.
Why calc() expressions silently fail
The CSS parser processes calc() expressions with stricter rules than most developers expect. Unlike JavaScript arithmetic, CSS calc() has type constraints built into the spec. A length divided by another length would produce a dimensionless number, not a length, which means it cannot be used as a width or height. The browser enforces this at parse time by rejecting the entire declaration, not by displaying an error.
The spaces-around-operators rule is the most frequent trap. The spec requires whitespace before and after + and - operators specifically because these characters can appear at the start of a number as a sign. Without spaces, calc(100%-20px) is parsed as a single custom-ident token, and the parser cannot interpret it as subtraction. The result is that the entire property declaration is treated as invalid and silently dropped.
Mixing units in calc() is intentional and powerful: calc(100vw - 2rem) is valid because the browser defers the pixel resolution of both the vw and rem values to computed time, when it knows the viewport width and the root font size. However, multiplying two dimensional values is not allowed: calc(2rem * 1px) is invalid because the type would be length-squared, not length.
Custom properties add another layer of complexity. A var() reference inside calc() is not resolved at parse time — it is resolved at computed value time. If the custom property is undefined, the entire expression becomes the guaranteed-invalid value and the browser acts as if the declaration does not exist. Unlike a syntax error (which the browser skips and continues), an invalid computed value can cascade: other properties that inherit from this value may also fail.
Diagnosing a broken calc() in DevTools
Open DevTools and select the element. In the Styles panel, look for the property where you used calc(). If the property name has a yellow warning icon or a strikethrough, the declaration is invalid. Hover over the warning to see a tooltip with the parse failure reason. In Chrome 119 and later, the tooltip may identify the specific token that failed.
In the Computed tab, look for the property you expected calc() to set. If it shows the initial value — for example, width showing auto when you expected a pixel value — the declaration was rejected. If it shows a value but not the one you expected, the expression parsed correctly but evaluated to a different result, often because a custom property resolved to an unexpected value.
To debug a calc() that references var(), add a temporary rule that declares each custom property directly on the element and check whether the layout changes. For example, add --sidebar-width: 260px inline on the element via DevTools and see if the width updates. If it does, the original custom property was undefined or scoped incorrectly. Search the Computed tab for the custom property name to find its resolved value at the element.
For complex nested expressions, simplify step by step. Replace calc((100% - var(--gap) * 3) / 4) with calc(100% / 4) first to confirm the basic structure works, then add back the subtraction. This isolates whether the division, the custom property reference, or the parenthesization is the cause of the failure.
Fixes for every common calc() error
For missing spaces, add whitespace around all + and - operators. A quick way to catch this in your editor is to search the project for the pattern calc(.*[+\-]) and review every match for missing spaces. CSS linting tools including stylelint can enforce calc() spacing automatically on save, which catches the error before it ever reaches the browser.
For division errors, always check whether your divisor has a unit. If you are calculating a column width for a grid, you almost always want to divide by a plain number: calc((100% - 48px) / 3) for three columns with a total gap of 48px. If you find yourself wanting to divide by a px value, reconsider the math — you almost certainly want a unitless ratio, and CSS Grid's fr unit may eliminate the need for calc() entirely.
For custom property issues, add fallback values to every var() that appears inside a calc() expression. The syntax is var(--my-property, fallback-value). The fallback documents the intended type and scale of the variable, which helps other developers understand what value is expected. If the fallback is another var() call, wrap it in parentheses: var(--primary, var(--secondary, 1rem)).
For calc() inside media queries, you cannot use custom properties as breakpoint values. The spec prohibits var() inside media query conditions because custom properties are resolved per-element in the computed value cascade, but media queries are evaluated before any element exists in the tree. If you need responsive breakpoints driven by design tokens, use a CSS preprocessor variable that resolves at build time, or use Container Queries which operate on an element's available space rather than the viewport.
Edge cases: nested calc, clamp, and SVG
Nested calc() expressions are valid: calc(calc(100% - 20px) / 3) is allowed and equivalent to calc((100% - 20px) / 3). Modern browsers resolve the inner calc() first and then apply the outer operation. The nested form is redundant but not harmful. If you are using a CSS preprocessor that generates nested calc() from mixins, the output is valid and you do not need to collapse it manually.
The min(), max(), and clamp() functions accept calc() expressions as their arguments and are often better choices for responsive values. clamp(1rem, 2.5vw, 1.5rem) sets a font size that is fluid between viewport widths and bounded by fixed minimum and maximum values without any calc() arithmetic. For cases where you need to add a fixed offset to a fluid value, clamp() and calc() compose naturally: clamp(200px, calc(50% + 20px), 600px) is valid and well-supported.
Inside SVG, calc() is not supported in presentation attributes such as width, height, x, and y. These attributes accept a CSS length string in modern SVG 2, but the browser's SVG attribute parser does not run the full CSS calc() engine. If you are using inline SVG and need responsive dimensions, set the width and height via CSS classes on the SVG element rather than through presentation attributes, or use the SVG viewBox attribute with percentage-based layout instead.
In older versions of Safari (before Safari 15), calc() inside a CSS custom property declaration had limited support. A pattern like :root { --total-width: calc(100vw - 2rem); } might resolve to zero on Safari 14. The practical fix is to store static values in custom properties and apply calc() at the usage site: width: calc(var(--content-width) - var(--sidebar-width)) rather than storing the calc() result in the custom property.
Frequent calc() mistakes in production CSS
Copying calc() expressions from Figma or design tool exports is a reliable source of space-missing bugs. Design tools often format values without spaces — 100%-20px looks clean in a properties panel but fails in CSS. Always add spaces when pasting from any source, and run the output through a CSS formatter to validate the syntax before committing.
Applying calc() to properties that CSS layout algorithms already handle natively adds unnecessary complexity. width: calc(100% / 3) for a three-column layout is correct but verbose compared to display: grid; grid-template-columns: repeat(3, 1fr). Reserve calc() for values where the layout system cannot do the arithmetic for you, such as subtracting a fixed sidebar width from a fluid container width.
Writing calc(0px + var(--offset)) to force a unit on a unitless custom property is a known workaround that works but is fragile. When a custom property holds a plain number (--multiplier: 2), you cannot use it directly in a length context. The workaround calc(1px * var(--multiplier)) produces a valid length. However, the cleaner solution is to store the value with its unit in the custom property from the start: --multiplier: 2px avoids the workaround entirely.
Failing to test calc() in Firefox and Safari after testing only in Chrome is a common oversight. While the spec is clear on operator spaces and unit rules, edge cases with nested expressions and custom property interactions have historically differed between rendering engines. Add a Firefox and Safari check to your browser testing matrix before shipping any complex calc() expression, especially if it uses nested var() references or clamp().
Best practices for robust calc() usage
Always write calc() with explicit spaces around + and -. Even if your linter does not enforce it yet, the habit eliminates an entire class of silent parse errors. Many teams add a stylelint rule for this as part of their CSS linting configuration, which catches the error at commit time rather than after deployment to a staging environment.
Prefer named custom properties over magic numbers inside calc(). Instead of calc(100% - 260px), write calc(100% - var(--sidebar-width)) and declare --sidebar-width: 260px in a central token file. This makes the intent explicit, allows the value to be overridden at any scope level, and makes responsive adjustments (changing --sidebar-width in a breakpoint) straightforward and contained.
Use clamp() for responsive values that have natural minimum and maximum bounds. font-size: clamp(1rem, 2vw + 0.5rem, 1.5rem) is a fluid type scale with clamped extremes, and it is cleaner than a calc() expression nested inside min() inside max(). The clamp(min, preferred, max) pattern is supported in all modern browsers without prefixes since 2020 and should be the default choice for fluid sizing.
Document complex calc() expressions with a short comment explaining the math. A comment like /* (full width minus 3 gaps of 16px) divided by 4 columns */ above calc((100% - 48px) / 4) makes the expression immediately readable to anyone who encounters it. The comment adds no runtime cost and significantly reduces the time needed to debug or adjust the expression when requirements change.
When migrating older calc() patterns to modern CSS, consider adopting clamp() as the primary tool for responsive sizing. The clamp(min, preferred, max) syntax expresses the intent of a value more clearly than nested calc() with min() and max() wrappers, and it composes naturally with viewport units and rem values in the preferred expression, making fluid typography and spacing systems easier to read and maintain.
calc() not working — fix checklist
- ✓Add spaces around every + and - operator: calc(100% - 20px) not calc(100%-20px)
- ✓Check that the divisor is a unitless number: calc(100% / 3) not calc(100% / 3px)
- ✓Verify every custom property referenced in var() is declared on an ancestor or :root
- ✓Add fallback values to every var() inside calc(): var(--size, 1rem)
- ✓Remove any calc() or var() used inside a media query breakpoint condition
- ✓Test in Firefox and Safari in addition to Chrome for cross-engine edge cases
- ✓Use clamp() instead of complex nested calc() for fluid min/max value clamping
- ✓Check the DevTools Styles panel for yellow warning icons on the affected declaration
Related guides
Frequently asked questions
Why are spaces required around + and - in calc() but not * and /?
The CSS parser needs spaces to distinguish subtraction and addition from a sign prefix on the operand. calc(100%-20px) could be parsed as a token starting with 100%, followed by a negative 20px value — the parser cannot disambiguate without spaces. Multiplication and division operators are not signs, so spaces are optional. The spec requires whitespace around + and - to resolve the ambiguity.
Can I mix percentages and pixels in calc()?
Yes. calc(100% - 260px) is valid and one of the most common calc() use cases. The browser resolves the percentage to pixels at computed time using the containing block width, then subtracts the fixed pixel value. This mixed-unit arithmetic is the primary reason calc() exists — to combine values from different unit systems that would otherwise be incompatible in a single property value.
Why does calc() with a CSS custom property produce no result?
If the custom property is undefined or empty, var() returns the guaranteed-invalid value, which invalidates the entire calc() expression and the containing declaration. Confirm the property is declared on :root or a parent element before the rule runs. Use var(--my-prop, fallback) to provide a fallback that keeps the declaration valid even when the property is missing from the cascade.
Can I use calc() in media queries?
Only with static values. calc(100vw - 20px) inside a media query condition is valid in modern browsers. However, var() inside a media query condition does not work because custom properties are element-scoped and media queries are evaluated before any element exists. Use preprocessor variables like Sass $var that resolve at build time for token-driven breakpoints.
What is the difference between calc(), clamp(), min(), and max()?
calc() performs arithmetic on mixed-unit values. min() returns the smallest of its arguments, max() returns the largest. clamp(min, preferred, max) clamps a preferred value between minimum and maximum bounds. Use clamp() when the value has natural bounds; use calc() for arithmetic that the min, max, and clamp functions cannot express alone. All four functions compose with each other freely.
Why does calc() work in Chrome but fail in Safari?
Some Safari versions before 15.4 had limited support for calc() stored inside custom property declarations, and certain nested expression edge cases differ between engines. Test on BrowserStack or a real device with your target Safari version. Most Safari-specific calc() failures come from storing a calc() result as a custom property value — moving the calc() to the usage site often resolves it.
Is nested calc() valid CSS?
Yes. calc(calc(100% - 20px) / 3) is valid; the browser resolves the inner calc() first. In modern CSS you can write calc((100% - 20px) / 3) without nesting, which is simpler. CSS preprocessors that generate nested calc() output produce valid CSS that browsers handle correctly.
Why can I divide by 2 but not by 2px inside calc()?
Division in calc() requires the divisor to be unitless. Dividing a length by a length would produce a dimensionless number — not a length — which cannot be used in a property that expects a length. The spec defines this as a type error. Always divide by a plain number: calc(100px / 4) is valid, calc(100px / 4px) is rejected by the parser.
Does calc() work inside SVG presentation attributes?
No. SVG presentation attributes such as width, height, x, and y are parsed by the SVG attribute parser, which does not run the full CSS calc() engine. To use calc() for SVG element sizing, apply the values through CSS classes on the SVG element or use percentage values with the SVG viewBox attribute instead of presentation attributes.
All tools run in your browser. Your data never leaves your device. Last updated: 2026-05-06.