Dynamic Sizing with CSS clamp()

Abstract image used to represent Dynamic Sizing with CSS clamp()
Image by Matt Artz.

In Brief

clamp() gives one CSS value a minimum, a preferred size, and a maximum. It works well for fluid type, spacing, and layout values that should respond to the viewport without becoming unreadably small or excessively large. The important part is choosing sensible bounds, not just replacing every media query with a function.

When a size needs room to grow but also needs sensible limits, clamp() lets you put the minimum, preferred value and maximum in one declaration. It builds on the same sizing decisions we can express with min(), max() and, for simpler dimensions, the older sizing properties.


Understanding clamp()

With clamp(), what you're getting is a CSS function which enables you to set a scalable value for a property, within an allowed range. The syntax looks like this:

clamp(min, val, max)

This provides us with a really straightforward method for defining a value (val) which can shrink or grow within the boundaries of min (minimum value) and max (maximum value).

Here's a quick example:

.text {
  font-size: clamp(1rem, 2vw, 2.5rem);
}

Here, the font size of .text follows 2vw, bounded by 1rem and 2.5rem. That gives us fluid sizing, but the bounds alone don't make the text accessible. Choose a readable minimum and check that people can enlarge the text to 200% without losing content or functionality. A viewportonly preferred value can counteract browser zoom over part of the range.

I should also mention here that you can combine clamp() with min() and max(), so you could have a declaration that looks like this:

.container {
  width: clamp(min(5vw, 15rem), 250px, max(95vw, 65rem));
}

Practical Applications

As with any feature of CSS, the practical applications for clamp() are extensive: quite literally in any situation where you want to set an element's property dynamically but still limit its upper and lower values without the need for media queries.

Nevertheless, here are the two main applications I tend to find myself using it for:

Responsive/Fluid Typography

One useful application is letting text grow gently as more space becomes available. I prefer to start with the size people need to read comfortably, then choose how much growth the layout can support.

Here's an example:

body {
  font-size: clamp(1rem, 0.9rem + 0.5vw, 1.2rem);
}

This sets the text size on body; it does not set the root font size. The rem unit refers to the font size on html. Here, the preferred value combines 0.9rem with 0.5vw, with a minimum of 1rem and a maximum of 1.2rem.

That's a deliberately modest range. Including a rem term lets the root font size contribute to the preferred value, but I'd still test browser zoom, text resizing and the actual content. A useful formula is a starting point, not an accessibility guarantee.


Adaptive Spacing and Layouts

Beyond typography, clamp() is also ideal for creating layouts with responsive (or adaptive) spacing and dimensions that respond to the viewport size but can still be restrained within a designerdefined range.

For example:

.container {
  margin: clamp(1rem, 5%, 3rem);
}

Here, the percentage in .container's margin is resolved against the containing block's width, which may be much narrower than the viewport. The margin follows 5% of that width, bounded by 1rem and 3rem.


Considerations and Browser Support

As you will no doubt be familiar if you've read my articles on max() and min(), browser support is generally very good, as long as you don't intend to support legacy versions of Internet Explorer (or very early versions of Edge) then you should be able to use it relatively unencumbered.

Nevertheless, it's worth spending a moment discussing the alternatives that might afford you a little more flexibility in your userbase.

Media Queries

The obvious answer is media queries. I've covered this in detail in the companion min() and max() articles already, and I'm sure you're already familiar with how media queries work so it wouldn't be too difficult to see how you could approximate the same behaviour using them. I'll save you another code example!

The min-* and max-* properties

Although the big power behind clamp() and its cousins is that they can be used in conjunction with any property that accepts a value, if you are only using them for dimensional properties (i.e., width, height, etc) then you could still just fall back to these much older and more established properties.

For example:

/* With clamp(): */
.container {
  width: clamp(15rem, 250px, 95vw);
}

/* Without clamp(): */
.container {
  min-width: 15rem;
  width: 250px;
  max-width: 95vw;
}

Although the code is a little more cumbersome (three lines instead of one), I would argue that the older style is easier to read, and will give you better overall browser support.

I have no doubt that min(), max(), and clamp() will continue to play a pivotal role in future web design and development. For now, though, if you intend to use them for dimensional properties, then there is always an alternative that might just be a tiny bit better (for now).


Planning a platform change?

I help teams make difficult platform work clearer, from architecture decisions and migrations to launch recovery, performance, and search visibility.