Skip to content
AgileLogics
6 min read

Math Functions and Fluid Sizing

Prerequisites: Custom properties, Units and values

CSS can calculate values at run time, mixing units the browser only knows during layout.

calc()

.main { width: calc(100% - 16rem); }
.hero { min-height: calc(100dvh - var(--header-height)); }
.grid { gap: calc(var(--space) * 2); }

Spaces are required around + and - (calc(100% -2rem) is invalid; -2rem would be read as a negative number).

min(), max(), clamp()

<style>
  .box { width: min(100%, 500px); }
  .pad { padding: max(1rem, 5%); }
  .title { font-size: clamp(1.5rem, 1rem + 3vw, 3rem); }
  .narrow { width: 300px; }
  .wide { width: 1200px; }
</style>
<div class="narrow"><div class="box pad"><h2 class="title">A</h2></div></div>
<div class="wide"><div class="box pad"><h2 class="title">B</h2></div></div>
<script>
  for (const box of document.querySelectorAll(".box")) {
    const s = getComputedStyle(box);
    console.log(box.parentElement.className.padEnd(6), "width:", s.width, "padding:", s.paddingLeft,
      "title:", getComputedStyle(box.querySelector(".title")).fontSize);
  }
</script>
narrow width: 300px padding: 16px title: 48px
wide   width: 500px padding: 60px title: 48px
  • min(100%, 500px) — at most 500px, but never wider than the container: 300px in the narrow one, 500px in the wide one. It replaces width: 100%; max-width: 500px in one line.
  • max(1rem, 5%) — at least 16px, or 5% of the container width if that's larger (5% of 1200px = 60px).
  • clamp(min, preferred, max) — the preferred value, kept between the limits. Both titles computed the same size because vw depends on the viewport, not the container (the example ran in a 1280px-wide window: 16px + 3 × 12.8px = 54.4px, clamped to 48px).

Fluid typography

clamp() with a rem + vw middle value scales type smoothly between a minimum and maximum, without breakpoints:

: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(2rem, 1.4rem + 2.8vw, 3.5rem);
}
h1 { font-size: var(--step-3); }

Always include a rem part: text sized purely in vw doesn't grow when people zoom, which fails accessibility guidelines.

More functions

  • round(), mod(), rem(), abs(), sign(), and trigonometric functions (sin(), cos(), ...) exist in current browsers — useful for snapping values and circular layouts.
  • attr() reads an HTML attribute; in content it has always worked, and newer browsers allow typed use in other properties.

Practice

Write a .container that is 90% of the viewport wide on phones, but never wider than 72rem, centered — in one width declaration.

Show solution
.container {
  width: min(90vw, 72rem);
  margin-inline: auto;
}