CSSmin(), max() & clamp()

min(), max() & clamp()

The comparison functions — min(), max(), and clamp() — were introduced in CSS in 2020 and immediately became essential for fluid, responsive design. They reduce or eliminate the need for media queries in many common scenarios by expressing sizing as a range rather than a fixed value. Instead of "16px at mobile, 20px at desktop", you write "between 16px and 20px, scaling smoothly with the viewport".

min() — the smaller of two values

min(a, b) evaluates both arguments and uses whichever is smaller. Think of it as a built-in max-width that works in any context:

CSS
/* Equivalent patterns */
.old { width: 100%; max-width: 800px; }
.new { width: min(100%, 800px); }

/* min() with multiple arguments */
.container {
  padding-inline: min(5%, 48px); /* max 48px, but scales below 960px viewport */
}

/* min() in grid */
.grid {
  grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr));
}

/* Never let an image grow beyond its natural size */
img {
  width: min(100%, var(--image-natural-width, 600px));
}

/* On small screens: min(100%, 800px) evaluates to 100%
   On large screens: min(100%, 800px) evaluates to 800px */
max() — the larger of two values

max(a, b) uses the larger of two values. It's a built-in min-width — it sets a floor:

CSS
/* Font size that scales with viewport but has a minimum */
p { font-size: max(1rem, 1.5vw); }
/* At 600px viewport: max(16px, 9px) = 16px — minimum maintained */
/* At 1600px viewport: max(16px, 24px) = 24px — grows beyond rem */

/* Minimum padding */
.section {
  padding-block: max(2rem, 4vh);
  /* Never less than 2rem regardless of viewport height */
}

/* Minimum touch target — WCAG recommends 44×44px */
.icon-btn {
  width: max(44px, 3rem);
  height: max(44px, 3rem);
}

/* max() ensures values never go below a threshold */
clamp() — the fluid range function

clamp(min, preferred, max) is effectively max(min, min(preferred, max)) — a min and max applied to a preferred value. The preferred value grows freely between the bounds. This is the foundation of modern fluid typography:

CSS
/* Fluid typography — grows between 18px and 28px as viewport grows */
p {
  font-size: clamp(1.125rem, 0.5rem + 2vw, 1.75rem);
}

/* Fluid headings — a type scale */
h1 { font-size: clamp(2rem, 5vw + 1rem, 4rem);   }
h2 { font-size: clamp(1.5rem, 3vw + 1rem, 2.5rem); }
h3 { font-size: clamp(1.25rem, 2vw + 0.5rem, 2rem); }

/* Fluid spacing */
.section {
  padding-block: clamp(2rem, 5vw, 6rem);
}

/* Fluid container */
.container {
  width: clamp(320px, 90%, 1200px);
  margin-inline: auto;
}
The middle argument in clamp() should use a viewport unit (vw) so the value actually changes — clamp(1rem, 1.5rem, 2rem) with a fixed middle is just the middle value
`clamp(1rem, 1.5rem, 2rem)` is technically valid but pointless — the middle value `1.5rem` is always between 1rem and 2rem, so it always wins. The clamp is only useful when the middle can go below the minimum or above the maximum. Use viewport units or `calc()` in the middle: `clamp(1rem, 2vw + 0.5rem, 2rem)`.
The fluid type formula

The preferred value in clamp() can use a linear interpolation formula to smoothly scale between a specific minimum font-size at a minimum viewport width and a maximum font-size at a maximum viewport width:

CSS
/*
  Target: font grows from 18px at 320px viewport to 28px at 1200px viewport

  Formula:
  preferred = minSize + (maxSize - minSize) * (100vw - minVW) / (maxVW - minVW)

  With numbers:
  = 18 + (28 - 18) * (100vw - 320px) / (1200px - 320px)
  = 18 + 10 * (100vw - 320px) / 880px

  Simplified to calc():
  = calc(18px + 10 * ((100vw - 320px) / 880))
  = calc(1.125rem + (100vw - 20rem) * (10/880))

  In practice, use a tool like utopia.fyi or type-scale.com to generate these values:
*/

/* Example output from Utopia for 18px→28px at 320px→1200px: */
p {
  font-size: clamp(1.125rem, 0.6563rem + 1.4583vw, 1.75rem);
}
Combining all three

CSS
/* A complete fluid design system using just these three functions */
:root {
  /* Spacing scale — fluid */
  --space-xs:  clamp(0.25rem, 0.5vw, 0.5rem);
  --space-sm:  clamp(0.5rem,  1vw,   1rem);
  --space-md:  clamp(1rem,    2vw,   1.5rem);
  --space-lg:  clamp(1.5rem,  3vw,   2.5rem);
  --space-xl:  clamp(2rem,    5vw,   4rem);

  /* Type scale — fluid */
  --text-sm:   clamp(0.875rem, 0.8rem + 0.25vw, 1rem);
  --text-base: clamp(1rem,     0.9rem + 0.5vw,  1.125rem);
  --text-lg:   clamp(1.125rem, 1rem + 0.75vw,   1.5rem);
  --text-xl:   clamp(1.5rem,   1.2rem + 1.5vw,  2.5rem);
  --text-2xl:  clamp(2rem,     1.5rem + 2.5vw,  3.5rem);

  /* Container */
  --container: min(90%, 1200px);
}

.container {
  width: var(--container);
  margin-inline: auto;
}

body {
  font-size: var(--text-base);
  padding: var(--space-md);
}
Browser support

All three functions — min(), max(), and clamp() — are supported in all modern browsers (Chrome 79+, Firefox 75+, Safari 11.1+). They are safe to use in production without fallbacks for any site targeting modern browsers. If you need IE11 support (rare), provide a fixed px or rem fallback before the clamp() declaration.

Next
How CSS expresses colors — from hex codes to the modern oklch color space: [Color Values](/css/color-values).