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:
/* 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 */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:
/* 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:
/* 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:
/* 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);
}Nesting functions
/* 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));
}