CSS math functions let one value adapt to the screen. calc() mixes units, as in calc(100% - 2rem). min() picks the smallest option, so width: min(100%, 640px) never overflows. max() picks the largest, and clamp(min, preferred, max) keeps a fluid value between two limits. dvh follows the mobile browser's changing toolbar, unlike plain vh.
| Job | CSS |
|---|---|
| Full width minus a fixed gutter | width: calc(100% - 2rem) |
| A column that caps at 640px | width: min(100%, 640px) |
| Padding that never drops below 1rem | padding: max(1rem, 4vw) |
| Heading that scales between 2rem and 3.5rem | font-size: clamp(2rem, 1.2rem + 3vw, 3.5rem) |
| Hero that fills the visible mobile screen | min-height: 100dvh |
Updated
Instead of resizing your browser to test a formula, drag the simulated viewport width and every function resolves to pixels in front of you, with the point where clamp() hits its limits marked. The page also draws what vh, svh, lvh and dvh measure on a phone and shows the intrinsic keywords working on real boxes.
| Unit | 1 unit equals | Changes while scrolling? | Use for |
|---|---|---|---|
| vh / vw | 1% of the viewport (on mobile, usually the large one) | No | Desktop layouts, older browser fallback |
| svh / svw | 1% of the small viewport, toolbars shown | No | Full-screen sections that must never be hidden |
| lvh / lvw | 1% of the large viewport, toolbars hidden | No | Backgrounds that should cover everything |
| dvh / dvw | 1% of the current viewport | Yes | Layouts that should track the toolbar |
| vmin / vmax | 1% of the smaller or larger side | No | Square elements that fit either orientation |
| cqi / cqw | 1% of a size container's inline size | With the container | Components inside container queries |
The first line is the fallback for browsers without the newer units. The second overrides it where supported.
.hero {
min-height: 100vh;
min-height: 100svh;
display: grid;
place-items: center;
}min() caps the width; the padding uses max() so it never shrinks below 12px. Both rules adapt to the frame width on their own.
<style>
.col { width: min(100%, 40ch); margin-inline: auto; padding: max(12px, 3vw); background: #f1f5f9; border-radius: 10px }
h3 { font-size: clamp(1.1rem, 0.8rem + 2vw, 1.6rem); margin: 0 0 6px }
</style>
<div class="col"><h3>Opening hours</h3><p style="margin:0">Tuesday to Sunday, eight until four. Closed on public holidays.</p></div>The main area takes whatever is left after the 120px sidebar and the 12px gap.
<style>
.wrap { display: flex; gap: 12px }
aside { width: 120px; background: #fde68a; padding: 8px }
main { width: calc(100% - 120px - 12px); background: #dbeafe; padding: 8px }
</style>
<div class="wrap"><aside>Filters</aside><main>Results</main></div>| Keyword | Width becomes | Tailwind |
|---|---|---|
| min-content | The longest unbreakable word or widest image | w-min |
| max-content | All content on one line, even if it overflows | w-max |
| fit-content | max-content, but no wider than the space available | w-fit |
| fit-content(300px) in grid | Like fit-content with a 300px ceiling | grid-cols-[fit-content(300px)_1fr] |
row-gap and column-gap can differ. Neither adds space at the outer edges.
<style>
.flex { display: flex; flex-wrap: wrap; gap: 8px 16px; margin-bottom: 14px }
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(90px, 1fr)); gap: 12px }
span { background: #dbeafe; padding: 8px; border-radius: 8px; font-size: 13px }
</style>
<div class="flex"><span>Bread</span><span>Pastries</span><span>Cakes</span><span>Coffee</span><span>Gift cards</span></div>
<div class="grid"><span>One</span><span>Two</span><span>Three</span><span>Four</span><span>Five</span></div>Each step grows between roughly a 360px phone and a 1280px desktop. Tweak the numbers in the lab above.
:root {
--step-0: clamp(1rem, 0.95rem + 0.25vw, 1.125rem);
--step-1: clamp(1.25rem, 1.1rem + 0.6vw, 1.5rem);
--step-2: clamp(1.5rem, 1.2rem + 1.4vw, 2.25rem);
--step-3: clamp(1.9rem, 1.3rem + 2.6vw, 3.25rem);
--space-s: clamp(0.75rem, 0.6rem + 0.6vw, 1rem);
--space-m: clamp(1rem, 0.7rem + 1.4vw, 2rem);
--space-l: clamp(2rem, 1.2rem + 3.5vw, 4.5rem);
}
body { font-size: var(--step-0); }
h1 { font-size: var(--step-3); }
section { padding-block: var(--space-l); }
calc() waits until layout time to resolve, so it can combine a percentage that depends on the parent with a rem or pixel value. You can nest calc() inside min(), max() and clamp() and use custom properties inside it, such as calc(var(--gutter) * 2).
Multiplication and division need at least one plain number: calc(2 * 1rem) is fine, calc(1rem * 1rem) is not. Division by zero makes the declaration invalid.
min-content is the narrowest a box can get without overflowing, usually the width of its longest word or widest image. max-content is the width it would take if text never wrapped. Both are useful for grid tracks and for labels that should hug their text.
fit-content sits between the two: it is as wide as the content wants but wraps when the container is narrower. That makes it a clean way to centre a shrink-wrapped box with margin-inline: auto.
The same clamp() trick works for padding, margins and gaps. A section with padding-block: clamp(2rem, 6vw, 6rem) breathes on large screens without wasting space on a phone.
Define the fluid values once as custom properties, for example --space-m and --step-2, and use them everywhere. Changing the scale later then means editing one line rather than hunting through components.
On phones the browser toolbar grows and shrinks. svh is 1% of the small viewport, with the toolbar showing. lvh is 1% of the large viewport, with it hidden. dvh tracks whichever is current and updates as you scroll. Classic vh usually equals lvh, which is why 100vh sections get cut off on mobile.
Yes, all three have worked in every major browser since 2020. The small, large and dynamic viewport units arrived later, during 2022, and are now in all current major browsers. Add a plain vh fallback line before them if you still support older devices.
Pick the size at a narrow screen and at a wide screen, then write clamp(small, preferred, large). The preferred value combines rem and vw, such as 1rem + 2vw, so the size grows with the window but stays between the two limits. The lab below shows the exact widths where the limits take over.
width: fit-content sizes a box to its content, like a shrink-wrapped inline-block, but never wider than the space available. It is the same as min(max-content, max(min-content, available space)). The fit-content() function in grid tracks does the same with a limit you choose.
Yes. gap, row-gap and column-gap work in flex containers in all current browsers, not only in grid. Gap is added only between items, never at the outer edges, which is simpler than margins on every child.
Related: CSS box model visualiser, Flexbox and grid cheat sheet, CSS media queries, CSS variables, PX to REM converter, What is my screen resolution.
Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.
We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.