CSSCSS Functions (calc, min, max, clamp)

CSS Functions (calc, min, max, clamp)

CSS functions are expressions that compute a value at render time. The math functions — calc(), min(), max(), and clamp() — are the most transformative of them, enabling fluid, responsive design without JavaScript or media queries. They let you express relationships between values that couldn't otherwise be written in CSS: "the font size should be 4% of the viewport width, but never smaller than 1rem and never larger than 2rem".

calc() — arithmetic in CSS

calc() performs arithmetic (+, -, *, /) on CSS values. The key feature is that you can mix units — something you can't do in any other CSS context:

CSS
/* Mix percentage and pixels */
.sidebar { width: calc(25% - 16px); }

/* Total width = 100% but with a gap subtracted */
.content { width: calc(100% - 280px); } /* 280px = sidebar width */

/* Spacing calculations */
.card {
  padding: calc(var(--spacing) * 1.5);
}

/* Font size between two breakpoints */
.responsive-text {
  font-size: calc(1rem + 0.5vw);
}

/* Centering with transform */
.centered {
  position: absolute;
  top: calc(50% - 100px); /* 50% minus half the element's height */
  left: calc(50% - 150px);
}

/* Rules: + and - require spaces around them; * and / do not */
calc(100% - 16px)    /* ✓ correct */
calc(100%-16px)      /* ✗ invalid — spaces required around - */
calc(2*8px)          /* ✓ valid but harder to read */
calc(2 * 8px)        /* ✓ preferred style */
The + and - operators in calc() must be surrounded by whitespace — calc(100%-16px) is invalid; calc(100% - 16px) is correct
This is the most common syntax error in `calc()`. The browser is strict: `calc(100% -16px)` is also invalid because of the missing space before the minus. The reason: without spaces, the parser can't distinguish `-16px` (a negative length) from `16px` preceded by a minus operator. Always write `calc(A - B)` with spaces on both sides of `-` and `+`.
min() — use the smallest value

min() takes two or more values and evaluates to the smallest one. This is equivalent to "at most this wide" — or the CSS equivalent of max-width in a more composable form:

CSS
/* The container is 100% wide, but never more than 800px */
.container {
  width: min(100%, 800px);
  /* On a 1200px screen: min(1200px, 800px) = 800px */
  /* On a 600px screen: min(600px, 800px) = 600px */
  /* Equivalent to: width: 100%; max-width: 800px; */
}

/* Padding that doesn't exceed a fixed value */
.section {
  padding-inline: min(5%, 48px);
}

/* Image that fills its container but not beyond natural size */
img {
  width: min(100%, 600px);
}

/* Gap that scales with the viewport but doesn't grow too large */
.grid {
  gap: min(4vw, 32px);
}
max() — use the largest value

max() takes two or more values and evaluates to the largest. This is equivalent to "at least this wide" — or the CSS equivalent of min-width:

CSS
/* Minimum font size of 1rem, but grows with viewport */
p {
  font-size: max(1rem, 2vw);
  /* On a 400px screen: max(16px, 8px) = 16px — never smaller than 1rem */
  /* On a 1200px screen: max(16px, 24px) = 24px — grows beyond 1rem */
}

/* Minimum padding */
.content {
  padding: max(16px, 3vw);
}

/* Minimum touch target size */
.btn {
  min-width: max(80px, 10vw);
}
clamp() — fluid values with limits

clamp(min, preferred, max) is the power tool. It takes three values: a minimum, a preferred value (usually viewport-relative), and a maximum. The result is always between min and max, growing smoothly with the preferred value in between. This enables fluid typography and spacing without media queries:

CSS
/* clamp(minimum, preferred, maximum) */

/* Fluid font size */
h1 {
  font-size: clamp(1.75rem, 5vw, 3.5rem);
  /* At 400px viewport: 5vw = 20px — clamped to min 28px (1.75rem) */
  /* At 800px viewport: 5vw = 40px — result is 40px */
  /* At 1200px viewport: 5vw = 60px — clamped to max 56px (3.5rem) */
}

/* Fluid padding */
.section {
  padding-block: clamp(2rem, 5vw, 5rem);
}

/* Fluid container width */
.container {
  width: clamp(320px, 90%, 1200px);
}

/* Fluid line-length */
.prose {
  max-width: clamp(45ch, 60ch, 75ch);
}

/* Fluid gap */
.grid {
  gap: clamp(1rem, 3vw, 2rem);
}
clamp() with a viewport unit in the middle lets you create perfectly fluid values that scale smoothly between a minimum and maximum — no media queries needed
The preferred value in `clamp()` is usually a viewport unit like `vw` or a `calc()` expression. The relationship between the viewport size and the preferred value determines the slope — how quickly the value grows. A common formula for fluid typography: `clamp(minPx, minPx + (maxPx - minPx) * (100vw - minViewport) / (maxViewport - minViewport), maxPx)`. Tools like utopia.fyi can generate these values for you.
Nesting functions

CSS
/* Functions can be nested */
.element {
  width: min(calc(100% - 2rem), max(300px, 50vw));
}

/* clamp with calc in the preferred value */
h1 {
  font-size: clamp(1.5rem, calc(1rem + 2vw), 3rem);
}

/* Using custom properties inside functions */
:root {
  --max-width: 1200px;
  --padding: 1.5rem;
}

.container {
  width: min(calc(100% - var(--padding) * 2), var(--max-width));
}
Next
A deep dive into calc() — mixing units, nesting, and real-world patterns: [calc()](/css/calc).