CSScalc()

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).