calc()
calc() is one of those CSS features that changes how you think about layout. Before it existed, mixing unit types was impossible — you couldn't express "fill the full width minus a fixed sidebar". calc() solves this by doing the arithmetic at render time, after all the unit types have been resolved to pixels. It works anywhere a length, percentage, time, angle, or number is expected.
Basic syntax and operators
CSS
/* The four operators */ calc(100% - 64px) /* subtraction — space around - is required */ calc(50% + 20px) /* addition — space around + is required */ calc(1.5rem * 2) /* multiplication — one value must be unitless */ calc(100px / 4) /* division — divisor must be unitless */ /* Nested calc() — valid but outer calc() is optional in modern CSS */ calc(calc(100% - 32px) / 3) /* old style */ calc((100% - 32px) / 3) /* modern — parentheses handle nesting */ /* Multiple operations */ calc(100% - 2 * 24px) /* 100% minus 48px */ calc(16px + 0.5vw) /* base size plus viewport-based addition */
Always put spaces around + and - in calc() — calc(100%-16px) is parsed as a single token, not as subtraction, and will be ignored
The CSS spec requires whitespace around `+` and `-` inside `calc()` because of ambiguity: `calc(10px-5px)` could be a measurement of `10px` followed by `-5px` (a dimension with a negative value). With spaces — `calc(10px - 5px)` — the meaning is unambiguous. The `*` and `/` operators don't have this requirement but spaces improve readability.
Mixing units — the killer feature
CSS
/* Before calc(): impossible to express "100% minus 280px" in CSS */
/* After calc(): trivial */
.main-content {
width: calc(100% - 280px); /* 280px = sidebar width */
}
/* Percentage + rem spacing */
.column {
width: calc(33.333% - 1.5rem); /* three columns with 1.5rem total gap each */
}
/* Viewport + absolute */
.safe-height {
height: calc(100vh - 64px); /* full viewport minus header height */
height: calc(100dvh - 64px); /* mobile-safe version */
}
/* em + px — rare but valid */
.nudge {
margin-left: calc(1em + 2px); /* 1 character-width plus a 2px offset */
}
/* Custom property in calc */
:root { --header-height: 64px; }
.page {
padding-top: calc(var(--header-height) + 16px);
}Rules for multiplication and division
You cannot multiply two lengths together — that would produce a unit like px², which has no CSS meaning. Multiplication and division require that one side is a unitless number:
CSS
/* ✓ Valid: length × unitless number */
calc(16px * 2) /* = 32px */
calc(1.5rem * 1.5) /* = 2.25rem */
/* ✓ Valid: length ÷ unitless number */
calc(100% / 3) /* = 33.333% */
calc(400px / 4) /* = 100px */
/* ✗ Invalid: length × length */
calc(16px * 2px) /* = 32px² — meaningless */
/* ✗ Invalid: length ÷ length */
calc(100% / 3rem) /* invalid — divisor must be unitless */
/* Tip: use custom properties to avoid magic numbers */
:root {
--columns: 3;
}
.column {
width: calc(100% / var(--columns));
}calc() in animation and transition
CSS
/* Animate from a calc() value */
@keyframes slide-in {
from {
transform: translateX(calc(-100% - 20px)); /* off screen plus gap */
}
to {
transform: translateX(0);
}
}
/* Animate height with calc() — avoid animating height when possible (prefer max-height or scale) */
.accordion {
height: 0;
overflow: hidden;
transition: height 0.3s ease;
}
.accordion.open {
height: calc(var(--content-height) + 32px); /* JS sets --content-height */
}Real-world patterns
CSS
/* Responsive columns without media queries */
.auto-grid {
display: grid;
grid-template-columns: repeat(
auto-fill,
minmax(calc(min(100%, 280px)), 1fr)
);
gap: 1.5rem;
}
/* Sticky header offset */
.section-anchor {
scroll-margin-top: calc(var(--header-height) + 16px);
}
/* Negative margin full-bleed inside padded container */
:root { --container-padding: 1.5rem; }
.full-bleed {
margin-inline: calc(var(--container-padding) * -1);
padding-inline: var(--container-padding);
}
/* Perfect centring with position: absolute */
.badge {
position: absolute;
top: calc(50% - 10px); /* 50% minus half the badge's height (20px) */
left: calc(50% - 10px);
width: 20px;
height: 20px;
}calc() is now supported everywhere — it works in all modern browsers and IE11, making it safe to use in production without any fallbacks
`calc()` has been supported since IE9 and all modern browsers. Custom properties inside `calc()` require IE11 or better, but custom properties themselves don't work in IE11, so that's a moot point in 2024. `calc()` with static values is universally safe.
Next
The comparison functions that enable truly fluid, responsive design without media queries: [min(), max() & clamp()](/css/min-max-clamp).