Suggest an editImprove this articleRefine the answer for “CSS functions: calc(), clamp(), min(), max()”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**CSS math functions** `calc()`, `min()`, `max()`, and `clamp()` compute values at layout time, mixing units without JavaScript. `calc()` handles arithmetic across units: `width: calc(100% - 250px)`. `min/max` pick one extreme from a list. `clamp(min, val, max)` constrains to a range: `font-size: clamp(1rem, 2.5vw, 1.5rem)`. **Key rule:** custom math → `calc()`, one bound → `min/max`, both bounds → `clamp()`.Shown above the full answer for quick recall.Answer (EN)Image**CSS math functions** `calc()`, `min()`, `max()`, and `clamp()` compute values at layout time, letting you mix units like `%`, `px`, and `vw` in a single declaration without JavaScript. ## Theory ### TL;DR - `calc()` does arithmetic across units: `width: calc(100% - 250px)` subtracts a fixed sidebar from full width - `min(a, b)` picks the smaller value; `max(a, b)` picks the larger one - `clamp(min, val, max)` means "use val, but never below min or above max" - Think of a thermostat: `min/max` enforce one boundary; `clamp` enforces both; `calc` does the math in between - Decision rule: custom math → `calc()`, one boundary → `min/max`, both boundaries → `clamp()` ### Quick example ```css .card { /* Old approach: two separate declarations */ width: 100%; max-width: 600px; /* Modern: one declaration handles everything */ width: clamp(300px, 50vw, 600px); /* min 300px, ideal 50vw, max 600px */ font-size: min(2rem, 5vw); /* scales with viewport, never too large */ padding: calc(var(--gutter) * 2 + 1rem); /* theme-aware spacing */ } ``` `clamp(300px, 50vw, 600px)` returns `50vw` on most screens, drops to `300px` on small ones, and caps at `600px` on wide ones. One line replaces two media query breakpoints. ### Key difference `calc()` evaluates an expression and returns the result. `min()` and `max()` pick from a list of resolved values. `clamp(min, val, max)` is equivalent to `max(min, min(val, max))` but reads more clearly. All four are evaluated during layout, not at parse time, so the browser recalculates on every reflow triggered by resize or DOM changes. ### When to use - **Fluid typography** → `clamp()`: `font-size: clamp(1rem, 2.5vw, 1.5rem)` grows smoothly across screen sizes, no breakpoints needed - **Container width with a cap** → `min(100% - 2rem, 800px)`: full width on mobile, fixed maximum on desktop - **Offset-based sizing** → `calc()`: `height: calc(100vh - 80px)` accounts for a fixed header - **Minimum guarantee** → `max()`: `padding: max(1rem, 2vmin)` never collapses on small screens - **Theme-aware spacing** → `calc()` with custom properties: `calc(var(--gutter) * 2 + 1rem)` ### Comparison table | Function | Syntax | Returns | Typical use | |----------|--------|---------|-------------| | `calc()` | `calc(expression)` | Result of math ops | `width: calc(100% - 250px)` | | `min()` | `min(val1, val2, ...)` | Smallest value | `font-size: min(3rem, 8vw)` | | `max()` | `max(val1, val2, ...)` | Largest value | `padding: max(1rem, 2vmin)` | | `clamp()` | `clamp(min, val, max)` | val clamped to [min, max] | `width: clamp(320px, 90vw, 1200px)` | | **When to use** | Custom math | Pick one bound | Enforce a range | ### How the browser handles this Browsers parse these functions in the CSSOM during style resolution. `calc()` resolves expressions after converting `%` and viewport units to logical pixels at layout time. `min/max/clamp` compare fully resolved lengths at the used-value stage per the CSS Values spec. No JavaScript is involved. The calculation runs again on every reflow, so resize events automatically produce updated values. ### Common mistakes **Mistake: no spaces around `+` and `-` in `calc()`** ```css /* Wrong - parser reads 100%-20px as a single token */ width: calc(100%-20px); /* Correct */ width: calc(100% - 20px); ``` `*` and `/` do not require spaces. But `+` and `-` without spaces break the rule because the parser cannot tell if the minus is an operator or a sign prefix. This catches most developers at least once. **Mistake: wrong argument order in `clamp()`** ```css /* Wrong - min is larger than preferred, result is always clamped to 2rem */ font-size: clamp(2rem, 1rem, 4rem); /* Correct */ font-size: clamp(1rem, 2rem, 4rem); ``` If the minimum exceeds the preferred value, `clamp` always returns the minimum. The order must satisfy `min <= preferred <= max`. **Mistake: missing units in `min()` or `max()`** ```css /* Wrong - 500 has no unit, the declaration is invalid */ width: min(100%, 500); /* Correct */ width: min(100%, 500px); ``` **Mistake: dividing by a variable that might be zero** ```css /* Wrong - if --cols resolves to 0, result is NaN, falls back to 0, breaks layout */ margin: calc(100% / var(--cols, 0)); /* Correct - guard the variable */ margin: calc(100% / max(1, var(--cols, 1))); ``` **Mistake: unnecessary nesting of `calc()`** ```css /* Works, but redundant */ width: calc(calc(100% - 20px) - 10px); /* Simpler - nested calc expressions evaluate left-to-right anyway */ width: calc(100% - 30px); ``` ### Real-world usage - **TailwindCSS v3**: `clamp()` in arbitrary values, for example `w-[clamp(20rem,50vw,40rem)]` - **Bootstrap 5.3**: `clamp(1rem, 1.5vw, 1.5rem)` in fluid font-size utilities - **Chakra UI**: `clamp(300px, 60vh, 500px)` for responsive modal heights - **React Native Web**: `calc(100vw - ${sidebarWidth}px)` via StyleSheet - Replaces `ResizeObserver` and JS sizing logic wherever the constraint is purely dimensional ### Follow-up questions **Q:** Why does `calc()` require spaces around `+` and `-` but not `*` and `/`? **A:** The CSS parser cannot distinguish `-` as an operator from `-` as a sign prefix on a number like `-20px`. Without spaces, `100%-20px` looks like a single token. Multiplication and division have no such ambiguity, so spaces are optional there. **Q:** What is the difference between `min(90%, 800px)` and `clamp(0px, 90%, 800px)`? **A:** In this specific case the result is the same: both return `90%` until it hits `800px`. The difference is intent. `clamp` makes an explicit lower bound visible, which matters when that lower bound is meaningful. When you only need a ceiling, `min` is simpler to read. **Q:** `width: calc(100% - var(--sidebar))` works on desktop but returns `0px` on mobile. Why? **A:** The variable `--sidebar` is probably not defined in the mobile scope or not inherited into that element. When a custom property is unset, `calc()` resolves to the initial value and collapses the width. Fix: add a fallback `calc(100% - var(--sidebar, 0px))` or scope the variable correctly. **Q:** Do these functions have a meaningful performance cost? **A:** Negligible for static layout. They are evaluated once per layout pass, far faster than equivalent JavaScript. Avoid deep nesting inside CSS animations where recalculation happens every frame, but for sizing and spacing they are fine. **Q (senior):** How do container query units work with these functions? **A:** `calc(100cqw - 40px)` sizes relative to the nearest `container-type` ancestor, not the viewport. `cqw` resolves at the container's used size during layout. This works in Chrome 105+ and Firefox 110+. `clamp(200px, calc(100cqw - 40px), 400px)` inside `@container` is valid and evaluated correctly. ## Examples ### Responsive container without media queries ```css .container { /* Full width on small screens, capped at 800px, side margins included inline */ width: min(100% - 2rem, 800px); margin-inline: auto; /* Spacing that scales between 1rem and 3rem based on viewport width */ padding: clamp(1rem, 3vw, 3rem); } /* 375px screen: width = 343px (375 - 32). 1200px screen: width = 800px. */ ``` `min(100% - 2rem, 800px)` replaces the classic `width: 100%; max-width: 800px; margin: 0 auto` pattern. The side margins are handled directly in the expression, so the extra wrapper element is not needed. ### Fluid typography system ```css :root { --text-sm: clamp(0.875rem, 1vw, 1rem); --text-base: clamp(1rem, 1.5vw, 1.125rem); --text-lg: clamp(1.125rem, 2vw, 1.5rem); --text-xl: clamp(1.5rem, 3vw, 2.5rem); --text-2xl: clamp(2rem, 4vw, 3.5rem); } h1 { font-size: var(--text-2xl); } p { font-size: var(--text-base); } ``` Every size scales fluidly between its minimum and maximum. No breakpoints. On a 320px screen the base size is `1rem`; on a 1440px screen it reaches `1.125rem`. The scale is defined once in `:root` and reused everywhere via custom properties. ### Layout with header offset and auto-fit grid ```css .hero { /* Viewport height minus a CSS variable for the header height */ min-height: calc(100vh - var(--header-h, 80px)); width: min(100% - 2rem, 960px); margin-inline: auto; } .grid { display: grid; /* Columns auto-fit, each at least 250px wide */ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: clamp(1rem, 3vw, 2rem); } ``` `calc(100vh - var(--header-h, 80px))` deducts the header height dynamically. If the variable is not set, the fallback `80px` applies. `clamp(1rem, 3vw, 2rem)` on `gap` keeps spacing proportional to the viewport without any manual breakpoints.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.