CSS Sizing Functions: calc(), min(), max(), clamp() and Viewport Units

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.

Which function for which job
JobCSS
Full width minus a fixed gutterwidth: calc(100% - 2rem)
A column that caps at 640pxwidth: min(100%, 640px)
Padding that never drops below 1rempadding: max(1rem, 4vw)
Heading that scales between 2rem and 3.5remfont-size: clamp(2rem, 1.2rem + 3vw, 3.5rem)
Hero that fills the visible mobile screenmin-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.

vh vs svh, lvh and dvh

Viewport-percentage units
Unit1 unit equalsChanges while scrolling?Use for
vh / vw1% of the viewport (on mobile, usually the large one)NoDesktop layouts, older browser fallback
svh / svw1% of the small viewport, toolbars shownNoFull-screen sections that must never be hidden
lvh / lvw1% of the large viewport, toolbars hiddenNoBackgrounds that should cover everything
dvh / dvw1% of the current viewportYesLayouts that should track the toolbar
vmin / vmax1% of the smaller or larger sideNoSquare elements that fit either orientation
cqi / cqw1% of a size container's inline sizeWith the containerComponents inside container queries

A mobile-safe full-height hero

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;
}

calc(), min(), max() and clamp() examples

A readable column with no media query

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>

calc() for a fixed sidebar beside a fluid main area

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>

min-content, max-content and fit-content

Intrinsic sizing keywords
KeywordWidth becomesTailwind
min-contentThe longest unbreakable word or widest imagew-min
max-contentAll content on one line, even if it overflowsw-max
fit-contentmax-content, but no wider than the space availablew-fit
fit-content(300px) in gridLike fit-content with a 300px ceilinggrid-cols-[fit-content(300px)_1fr]

gap in flex and grid

The same gap in a wrapping flex row and a grid

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>

Fluid type and spacing scale

A small fluid scale as custom properties

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); }

How calc() resolves

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.

Intrinsic sizes: min-content, max-content and fit-content

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.

Fluid spacing as well as fluid type

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.

How it works

  1. Drag the viewport width slider from phone size up to a wide monitor.
  2. Adjust the minimum, preferred and maximum values of the clamp() formula and watch its pixel result.
  3. Compare the min(), max() and calc() rows resolving at the same width.
  4. Copy the formula, then try the content-sizing boxes and the gap examples below.

Tips

Frequently asked questions

What is the difference between vh, svh, lvh and dvh?

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.

Are clamp(), min() and max() supported everywhere?

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.

How do I write a fluid font size with clamp()?

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.

What does fit-content do?

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.

Can gap be used with flexbox?

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.

More in Guides

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.

Product

Who It's For

Features

Resources

Company

View Sitemap