calc(), clamp(), min() & max()
CSS math functions let you combine units and set fluid bounds without media queries. calc() does the math, clamp() caps a value between a floor and ceiling, and min()/max() pick the smaller or larger candidate.
Four math functions for values that compute themselves
CSS has four math functions that turn a property value into a small calculation. **calc()** evaluates one expression and can mix units that nothing else can: calc(100% - 2rem) subtracts a fixed gutter from a fluid width. This single capability solves layout problems that used to need wrappers or JavaScript.
**clamp(MIN, PREFERRED, MAX)** takes three comma-separated values. It returns PREFERRED, but never below MIN and never above MAX. **min()** and **max()** take a comma-separated list and return the smallest or largest candidate. Together these four let a value respond to viewport or container size with hard bounds and no media query.
Fluid values replace most media queries
Before clamp(), responsive type meant a breakpoint ladder: 1rem at mobile, 1.25rem at 768px, 1.5rem at 1024px. The steps are visible and the rules multiply. With clamp(1rem, 2vw + 1rem, 2rem) the font-size slides smoothly between two sane limits and you write one declaration instead of three.
min() and max() do the same for dimensions. A reading column of width: min(100% - 2rem, 65ch) is always full width minus padding on small screens and never wider than a comfortable line length on large ones. One value, two constraints, zero breakpoints. This is the modern responsive default.
The rules each function follows
**calc()** accepts +, -, *, /. The non-obvious rule: + and - MUST be surrounded by whitespace on both sides. calc(100% - 10px) works; calc(100%-10px) is invalid and the browser silently drops the whole declaration. For * and /, at least one operand must be a unitless number: calc(2rem * 3) is valid, calc(2rem * 3rem) is not. Nesting is allowed and the inner calc() acts as parentheses: calc(calc(100% - 2rem) / 2).
**clamp(MIN, PREFERRED, MAX)** evaluates PREFERRED first, then clamps it. All three values must resolve to the same type (all lengths, all percentages, or all numbers). The most readable fluid-type recipe is clamp(1rem, 2vw + 1rem, 2rem): the 2vw + 1rem preferred value scales with the viewport but is anchored to a readable base, and the floor and ceiling stop it from getting too small or too large.
**min(a, b, ...)** returns the smallest value, **max(a, b, ...)** returns the largest. Each comma-separated value is computed independently and the function picks one. min(100% - 2rem, 60ch) is a self-contained responsive column: whichever is smaller wins, so the column shrinks on narrow screens and caps at 60ch on wide ones.
All four compose. clamp(), min(), and max() can contain calc() inside any argument, and they work with var() custom properties. A spacing scale defined as --gap: clamp(1rem, 2vw, 2rem) then used as calc(var(--gap) * 2) gives you a fluid, bounded system with no breakpoints at all.
Try it
Drag the viewport width and watch clamp(1rem, 5vw, 3rem) scale the heading, then hit the floor and ceiling. See calc() subtract a fixed gutter from a fluid width.
Simulated viewport width
400pxDrag to resize. The heading below uses clamp(1rem, 5vw, 3rem) against this width.
clamp(1rem, 5vw, 3rem) on a live heading
Fluid Heading
Preferred active: 5vw = 20.0px, inside the range.
calc(100% - 2rem): fixed gutter off a fluid width
Container is 400px (100% of the simulated width, capped to fit). The box is calc(100% - 2rem) = 400px - 32px = 368px. The 2rem gutter stays fixed while the width flows.
Check yourself
Pick an answer to lock it in, then read why. Getting one wrong is part of how it sticks.
Remember this
calc()mixes units:calc(100% - 2rem).+and-MUST have whitespace on both sides or the declaration is silently dropped.clamp(MIN, PREFERRED, MAX)returns PREFERRED, clamped to the range.clamp(1rem, 2vw + 1rem, 2rem)is the fluid-type recipe.min(a, b)returns the smaller,max(a, b)returns the larger. Usemin(100% - 2rem, 60ch)for a bounded responsive column.- All four compose with
var():calc(var(--gap) * 2)insideclamp()gives a fully fluid, bounded spacing system with no breakpoints.
Done with this concept?
Mark it complete to track your progress. No login needed.