clamp(), min(), max(): CSS math functions
Responsive values that scale smoothly between a minimum and maximum. Replace breakpoints with single-line expressions.
How clamp() works
clamp(MIN, PREFERRED, MAX) returns the preferred value, clamped between the minimum and maximum. It's equivalent to max(MIN, min(PREFERRED, MAX)):
/* Font size: at least 1rem, at most 2rem, scales with viewport */
h1 {
font-size: clamp(1rem, 4vw, 2rem);
}
/* Padding: at least 1rem, at most 3rem */
.section {
padding-block: clamp(1rem, 5vw, 3rem);
}
/* Width: fluid but constrained */
.container {
inline-size: clamp(20rem, 90vw, 65rem);
margin-inline: auto;
}
The preferred value should include a viewport or container-relative unit so it actually scales. A static preferred value defeats the purpose.
Fluid typography
The most common use of clamp() is fluid type that scales smoothly between two viewport widths without breakpoints. The formula for the preferred value is:
/* Formula: preferred = base + (maxSize - minSize) / (maxVW - minVW) * 100vw
From 320px viewport (1rem) to 1200px viewport (2.5rem): */
h1 {
font-size: clamp(1rem, 0.318rem + 1.705vw, 2.5rem);
line-height: 1.1;
}
h2 {
font-size: clamp(0.875rem, 0.5rem + 1.2vw, 1.75rem);
line-height: 1.2;
}
body {
font-size: clamp(0.9375rem, 0.875rem + 0.25vw, 1.125rem);
line-height: 1.6;
}
clamp() with only viewport units (e.g., clamp(2vw, 3vw, 4vw)). The min and max values should use absolute units like rem so users can still zoom text.min() and max()
min() returns the smallest value from a comma-separated list. max() returns the largest. Both accept any number of arguments:
/* min(): take the smaller value — acts as a maximum cap */
.container {
inline-size: min(90vw, 1200px);
/* Same as: max-width: 1200px; width: 90vw; */
}
/* max(): take the larger value — acts as a minimum floor */
.sidebar {
inline-size: max(250px, 25vw);
/* At least 250px, grows with viewport */
}
/* Multiple arguments */
.element {
padding: min(2rem, 5vw, 40px);
/* Whichever is smallest wins */
}
A useful mnemonic: min() sets a ceiling (the value can never exceed the smallest option), while max() sets a floor.
Responsive spacing
Use math functions for spacing that adapts without breakpoints. This creates a more fluid layout than jumping between fixed values at media query boundaries:
:root {
--space-xs: clamp(0.25rem, 0.5vw, 0.5rem);
--space-s: clamp(0.5rem, 1vw, 0.75rem);
--space-m: clamp(1rem, 2vw, 1.5rem);
--space-l: clamp(1.5rem, 4vw, 3rem);
--space-xl: clamp(2rem, 6vw, 5rem);
}
.hero { padding-block: var(--space-xl); }
.card { padding: var(--space-m); gap: var(--space-s); }
Combining with container units
Math functions work with container query units (cqi, cqb) for component-level responsive sizing that adapts to the container rather than the viewport:
.card-container {
container-type: inline-size;
}
.card-title {
font-size: clamp(0.875rem, 3cqi, 1.5rem);
}
.card-body {
padding: clamp(0.75rem, 4cqi, 2rem);
}
Nesting and arithmetic
Math functions can be nested and combined with calc(). The inner function resolves first:
/* Nested: minimum of two clamped values */
.element {
inline-size: min(
clamp(300px, 50vw, 600px),
calc(100% - 2rem)
);
}
/* calc() inside clamp() */
.text {
font-size: clamp(
1rem,
calc(0.5rem + 2vw),
2.5rem
);
}
Note that calc() is implicit inside clamp(), min(), and max() — you can write clamp(1rem, 0.5rem + 2vw, 2rem) without wrapping the middle value in calc().