What min(), max(), and clamp() are

These three CSS functions help you write responsive values without a pile of media queries. They are “comparators”:

  • min(a, b, c...) picks the smallest result.
  • max(a, b, c...) picks the largest result.
  • clamp(min, preferred, max) picks the preferred value, but never smaller than min and never larger than max.

You can use them anywhere a length/size makes sense: width, font-size, padding, gap, border-radius, and more.

The mental model

  • min(): “I want it fluid, but don’t let it get too big.”
  • max(): “I want it fluid, but don’t let it get too small.”
  • clamp(): “I want it fluid in the middle, but with a minimum and a maximum.”

min() basics

min() evaluates each option and chooses the smallest computed value. A super common use is adding a “cap” to a fluid size.

Cap a fluid width

This pattern is everywhere: width: min(90vw, 520px). It means: “Use 90vw, but never exceed 520px.”

.card {
  width: min(90vw, 520px);
}
  
.card {
  width: min(100%, 34rem);
}
  
.card {
  width: min(600px, 100%);
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  min-height: 240px;
  display: grid;
  place-items: center;
  padding: 20px;
  font-family: system-ui, Arial, sans-serif;
}

.card {
  padding: 18px;
  border: 3px solid #111;
  border-radius: 16px;
  background: #fff;
}

.card h4 {
  margin: 0 0 10px;
  font-size: 18px;
}

.card p {
  margin: 0;
  line-height: 1.4;
  opacity: 0.9;
}
  

min() capped card

Resize the preview. The card grows with the viewport, but it stops at a maximum width.

Notice how the last snippet min(600px, 100%) is basically “never overflow the container.” That can be handy inside narrow layouts.

min() for responsive padding (with a maximum)

You can also cap spacing so it doesn’t become absurd on very large screens. Here, the padding grows with vw, but never exceeds a fixed size.

.card {
  padding: min(4vw, 28px);
}
  
.card {
  padding: min(6vw, 40px);
}
  
.card {
  padding: min(2vw, 18px);
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  display: grid;
  place-items: center;
  padding: 20px;
  font-family: system-ui, Arial, sans-serif;
}

.card {
  width: min(92vw, 560px);
  border: 3px solid #111;
  border-radius: 16px;
  background: #fff;
}

.card h4 {
  margin: 0 0 10px;
  font-size: 18px;
}

.card p {
  margin: 0;
  line-height: 1.5;
}
  

Padding that knows when to stop

With min(), padding can grow with the viewport, but it has a built-in “nope, that’s enough” limit.

max() basics

max() chooses the largest computed value. It’s great for enforcing a minimum “floor.”

Set a minimum padding

padding: max(16px, 3vw) means: “Use 3vw, but never go below 16px.” On small screens, you still get comfortable padding.

.card {
  padding: max(16px, 3vw);
}
  
.card {
  padding: max(12px, 2vw);
}
  
.card {
  padding: max(20px, 4vw);
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  display: grid;
  place-items: center;
  padding: 20px;
  font-family: system-ui, Arial, sans-serif;
}

.card {
  width: min(92vw, 560px);
  border: 3px solid #111;
  border-radius: 16px;
  background: #fff;
}

.card h4 {
  margin: 0 0 10px;
  font-size: 18px;
}

.card p {
  margin: 0;
  line-height: 1.5;
}
  

Padding with a floor

max() makes sure the padding never becomes too tiny, even when the viewport is small.

max() for minimum readable font-size

This is a way to prevent text from shrinking too far: font-size: max(16px, 1.8vw).

.demo {
  font-size: max(16px, 1.8vw);
}
  
.demo {
  font-size: max(14px, 2.2vw);
}
  
.demo {
  font-size: max(18px, 0.4vw);
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  padding: 20px;
  display: grid;
  gap: 12px;
  place-items: center;
  font-family: system-ui, Arial, sans-serif;
}

.demo {
  width: min(92vw, 720px);
  padding: 18px;
  border: 3px solid #111;
  border-radius: 16px;
  background: #fff;
  line-height: 1.4;
}

.demo strong {
  font-weight: 700;
}
  
Readability test: this text refuses to become microscopic, even when the viewport gets small.

Mixing units and calc()

A big reason these functions are useful is that you can compare different unit types. For example, vw is responsive to the viewport, while px and rem are more stable.

min() + calc() for smoother growth

A common trick is to add a baseline and then grow from there. Here we say: “Grow with the viewport, but cap it.”

.title {
  font-size: min(48px, calc(18px + 3vw));
}
  
.title {
  font-size: min(56px, calc(16px + 4vw));
}
  
.title {
  font-size: min(44px, calc(20px + 2.6vw));
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  padding: 20px;
  font-family: system-ui, Arial, sans-serif;
}

.panel {
  width: min(92vw, 540px);
  margin: 0 auto;
  padding: 20px;
  border: 3px solid #111;
  border-radius: 16px;
  background: #fff;
}

.title {
  margin: 0 0 10px;
  line-height: 1.1;
}

.panel p {
  margin: 0;
  line-height: 1.6;
  opacity: 0.9;
}
  

Fluid title with a cap

The title grows as the viewport grows, but min() prevents it from turning into a billboard.

Nesting min() and max()

You can nest them, but keep it readable. This pattern is basically “clamp without clamp”: max(minValue, min(fluidValue, maxValue)). It works, but clamp() is usually clearer.

.box {
  width: max(260px, min(80vw, 680px));
}
  
.box {
  width: max(320px, min(90vw, 740px));
}
  
.box {
  width: max(240px, min(70vw, 620px));
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  padding: 20px;
  display: grid;
  place-items: center;
  font-family: system-ui, Arial, sans-serif;
}

.box {
  padding: 18px;
  border: 3px solid #111;
  border-radius: 16px;
  background: #fff;
}

.box p {
  margin: 0;
  line-height: 1.5;
}
  

This box has a minimum width, a fluid middle, and a maximum width. It’s basically clamp(), but expressed with max(min()) nesting.

clamp() basics

clamp(min, preferred, max) is the “sweet spot” function. It gives you a fluid preferred value that can grow and shrink, but it stays within limits.

Syntax and meaning

  • Min: the smallest allowed value.
  • Preferred: usually a fluid expression (often vw or calc()).
  • Max: the largest allowed value.

Think of it like a responsible friend: it lets your design be flexible, but it won’t let it do something embarrassing.

Clamp a fluid font-size

Here are a few common clamp styles. In the snippets below, the middle value is a viewport-based size.

.title {
  font-size: clamp(1.3rem, 3vw, 2.4rem);
}
  
.title {
  font-size: clamp(1.1rem, 2.4vw, 2rem);
}
  
.title {
  font-size: clamp(1.5rem, 3.6vw, 2.8rem);
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  padding: 20px;
  font-family: system-ui, Arial, sans-serif;
}

.panel {
  width: min(92vw, 540px);
  margin: 0 auto;
  padding: 20px;
  border: 3px solid #111;
  border-radius: 16px;
  background: #fff;
}

.title {
  margin: 0 0 10px;
  line-height: 1.1;
}

.panel p {
  margin: 0;
  line-height: 1.6;
  opacity: 0.9;
}
  

Clamp me, I’m responsive

Resize the preview. The title grows and shrinks, but it stays between the min and max.

These are for demonstration purposes only, in the real world, you would use a fluid typography and fluid spacing generator such as Fluid type scale calculator.

clamp() with live sliders

Let’s make clamp() feel real by controlling its three parts. The sliders below edit the three values inside font-size: clamp(...).

A tiny detail: each slider uses the same name (font-size), so they update the 1st, 2nd, and 3rd value inside the same declaration, in order.

.headline {
  font-size: clamp(1.2rem, 3vw, 2.6rem);
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  padding: 20px;
  display: grid;
  gap: 12px;
  place-items: center;
  font-family: system-ui, Arial, sans-serif;
}

.card {
  width: min(92vw, 540px);
  padding: 20px;
  border: 3px solid #111;
  border-radius: 16px;
  background: #fff;
}

.headline {
  margin: 0 0 10px;
  line-height: 1.05;
}

.card p {
  margin: 0;
  line-height: 1.6;
  opacity: 0.9;
}
  

Slide the clamp

Try setting a high min, a low max, or a tiny preferred value. You’ll quickly see why the order matters: min, then preferred, then max.

The order matters (a lot)

clamp() expects clamp(min, preferred, max). If your min is larger than your max, the browser has to resolve the conflict. In practice, you’ll get a value that doesn’t behave like you intended.

Real-world recipes

Recipe: fluid typography that stays readable

A simple beginner-friendly approach:

  • Set a comfortable minimum in rem.
  • Use a viewport-based middle value (vw or calc()).
  • Set a maximum so it doesn’t become enormous on wide screens.
.page h2 {
  font-size: clamp(1.4rem, 2.6vw, 2.2rem);
}

.page p {
font-size: clamp(1rem, 1.6vw, 1.2rem);
} 
.page h2 {
  font-size: clamp(1.2rem, 3vw, 2.4rem);
}

.page p {
  font-size: clamp(1rem, 1.8vw, 1.25rem);
}
  
.page h2 {
  font-size: clamp(1.5rem, 2.2vw, 2.1rem);
}

.page p {
  font-size: clamp(1rem, 1.4vw, 1.15rem);
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.page {
  width: min(92vw, 540px);
  margin: 0 auto;
  padding: 20px;
  font-family: system-ui, Arial, sans-serif;
  border: 3px solid #111;
  border-radius: 16px;
  background: #fff;
}

.page h2 {
  margin: 0 0 10px;
  line-height: 1.1;
}

.page p {
  margin: 0;
  line-height: 1.6;
  opacity: 0.9;
}
  

Fluid typography, no drama

This paragraph scales gently with the viewport, but it never gets too tiny or too huge. That’s clamp() doing its job.

Recipe: responsive spacing with clamp()

Spacing is where clamp() feels like a superpower. Your layout breathes on larger screens, but stays tight enough on smaller screens.

.stack {
  gap: clamp(10px, 2.5vw, 24px);
}

.card {
padding: clamp(12px, 3vw, 28px);
} 
.stack {
  gap: clamp(8px, 2vw, 18px);
}

.card {
  padding: clamp(10px, 2.5vw, 22px);
}
  
.stack {
  gap: clamp(12px, 3vw, 30px);
}

.card {
  padding: clamp(14px, 3.2vw, 34px);
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  padding: 20px;
  font-family: system-ui, Arial, sans-serif;
}

.stack {
  width: min(92vw, 540px);
  margin: 0 auto;
  display: grid;
}

.card {
  border: 3px solid #111;
  border-radius: 16px;
  background: #fff;
}

.card h4 {
  margin: 0 0 8px;
  font-size: 18px;
}

.card p {
  margin: 0;
  line-height: 1.5;
  opacity: 0.9;
}
  

Card A

Gap and padding scale, but within limits.

Card B

No media queries were harmed in the making of this layout.

Card C

Clamp keeps spacing from getting too tight or too loose.

Recipe: responsive card width (min + max vs clamp)

These two approaches are conceptually similar:

  • Nesting: width: max(minWidth, min(fluidWidth, maxWidth))
  • Clamp: width: clamp(minWidth, fluidWidth, maxWidth)

In many cases, clamp() reads better.

.card {
  width: max(280px, min(90vw, 640px));
}
  
.card {
  width: clamp(280px, 90vw, 640px);
}
  
.card {
  width: clamp(320px, 80vw, 720px);
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  padding: 20px;
  display: grid;
  place-items: center;
  font-family: system-ui, Arial, sans-serif;
}

.card {
  padding: 18px;
  border: 3px solid #111;
  border-radius: 16px;
  background: #fff;
}

.card p {
  margin: 0;
  line-height: 1.5;
}
  

Resize the preview. Both approaches create a min size, a fluid middle, and a max size. Clamp() is just the “single function” version.

Recipe: responsive images with min() and clamp()

Images often need a max width so they don’t dominate the layout, but you still want them responsive. Here’s a simple pattern: clamp the width and keep the image filling its container.

.media {
  width: clamp(220px, 60vw, 520px);
}
  
.media {
  width: min(80vw, 520px);
}
  
.media {
  width: clamp(260px, 50vw, 640px);
}
  
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  padding: 20px;
  display: grid;
  place-items: center;
  gap: 12px;
  font-family: system-ui, Arial, sans-serif;
}

.media {
  border: 3px solid #111;
  border-radius: 16px;
  overflow: hidden;
  background: #fff;
}

.media img {
  width: 100%;
  height: auto;
  display: block;
}

.caption {
  width: min(92vw, 720px);
  opacity: 0.9;
  line-height: 1.5;
}
  
Random demo image
This image wrapper changes size responsively, but it has sensible limits.

Common pitfalls and “why isn’t this working?”

Pitfall: min() is not min-width

min() chooses the smallest computed value. min-width is a property that enforces a minimum size. They sound similar, but they solve different problems.

Pitfall: max() is not max-width

Same idea: max() is a function that picks the largest value, while max-width is a property that caps width. You can use either approach, depending on what reads clearer for you.

Pitfall: invalid comparisons (the browser can’t compare)

Most of the time, the browser can compare values like px, rem, and vw just fine. But if you plug these functions into a property that expects something else (like a color), it will fail and the declaration will be ignored.

Pitfall: wrong order in clamp()

Always remember: clamp(min, preferred, max). If your values don’t make sense (like a larger min than max), the result will not match your intent.

Debugging checklist

  1. Is the property you’re setting expecting a length/size (like width or font-size)?
  2. Do your values include units where needed (for example, 12 vs 12px)?
  3. For clamp(), is your argument order correct: min, preferred, max?
  4. If it “looks stuck,” try extreme values to confirm which branch is being chosen.

Quick reference cheatsheet

  • Cap something fluid: width: min(90vw, 520px)
  • Give something a floor: padding: max(16px, 3vw)
  • Fluid but bounded: font-size: clamp(1rem, 3vw, 2.2rem)
  • Readable nesting alternative: max(minVal, min(fluidVal, maxVal))

If you only memorize one thing, make it this: clamp() is usually the cleanest way to do “responsive in the middle, safe on both ends.”

Learn even more about clamp() in the CSS Clamp Interactive Tutorial.

Are you looking for a minmax() in CSS Grid? Check out the CSS Minmax() Interactive Tutorial.