Dynamic Sizing with CSS max()

It wasn't that long ago that CSS functions like max() (or min(), or clamp()) were entirely unthinkable: these open up access to dynamic values and functionality that could only be achieved historically through the use of JavaScript. Now, they play a really important role in our ability to develop fluid, responsive layouts, elegantly adapting across different devices.
Unpacking max()
Much like it's sibling min(), max() is very straightforward at it's core; it accepts and evaluates a set of values, and returns the largest. This allows us as developers and designers to expand elements to fill available space, whilst also maintaining a limit for usability and aesthetics.
Here's a simple example to get us started:
.container { height: max(50vh, 30rem);}Here, we define the height of an element: .container. Using max(), the overall height of .container will be set to either 50% of the viewport height (50vh) or 30rem, whichever of the two is the greater. I should mention here that there's no limit to the number of different values you can place within max(), this could easily be a combination of viewport, percentage, fixed, and fluid units all together in a single statement. The browser would still select whichever of the values is largest at that time.
Practical Applications
Responsive or Adaptive Typography
In a very similar vein to that which I described for min(), one of the most interesting and powerful uses for max() is in adaptive (or responsive) typography.
Here's a quick example where the font size of an h1 heading will increase as the viewport does but will never be smaller than 2rem:
h1 { font-size: max(2rem, 5vw);}Using max() in this way ensures that the text never falls below the specified minimum. It does not provide an upper bound, so combine it with min() or clamp() if the viewport‑based value also needs a cap.
Responsive Spacing and Layouts
Beyond typography, max() can provide responsive spacing with a logical minimum.
Here's another example:
.section { padding: max(1rem, 3vw);}In this example, the padding around .section grows with the viewport but never falls below 1rem. The value is a minimum, not a cap: once 3vw is greater than 1rem, max() selects 3vw.
Browser Support and Considerations
As covered in this same section on my article about min(), support for max() is very good. By the article's publication in May 2024, the function was available across current browser engines. Internet Explorer did not support it, so a fallback is needed only where that browser remains in scope.
There are two general strategies that you could consider:
Media Queries
I would argue that the power of these types of dynamic value functions comes from the fact that they do away with the need for lots of media queries. Nevertheless, in browser environments where max() isn't available, media queries will achieve similar results.
Revisiting our example from up towards the top of the article, you could achieve the same with a little more code like this (and have it work on Internet Explorer):
.container { height: 30rem;}@media (min-height: 60rem) { .container { height: 50vh; }}The min-* and max-* properties
However, if we're only looking for a max between two values (rather than a wider array) and using certain dimensional properties (width, height, etc), then the same can be achieved using a combination of dimension properties (in this case: height), and the min-* or max-* properties. Like this:
.container { height: 50vh; min-height: 30rem;}This achieves sort of the same thing in that .container will have a height of 50vh, but will also always be a minimum of 30rem high.
Admittedly, the comparison between two values like this is a fairly rudimentary use for max() anyway, so this solution isn't going to cover more in‑depth use cases. However, there is an argument that if what you need to achieve is this simple anyway, there's really no need to use max() (or min()) at all...