# Fluid Type and Spacing with clamp()

clamp() keeps a size between a minimum and a maximum and lets it grow smoothly in between, so headings and spacing fit a phone and a wide screen without a single breakpoint, while still following the reader's zoom.

Journeyman · CSS · Updated November 1, 2026

## After this lesson you can

- Work out the size a clamp() value gives at a window width
- Write a fluid font size or space with clamp(), with limits in rem and a preferred size of rem plus vw

## Key takeaways

- clamp(minimum, preferred, maximum) uses the preferred size, but never less than the minimum or more than the maximum.
- A fluid heading's preferred size grows with the window, such as 1rem + 3vw, and stops growing at its minimum and its maximum.
- Put a rem in the preferred size. vw alone ignores the reader's zoom and font size setting, and the rem part brings them back.
- Write the minimum and the maximum in rem, so the limits follow the reader's font size setting too.
- Spacing can be fluid the same way. Padding and gaps with clamp() stay snug on a phone and open up on a wide screen.

Spoke & Chain's home page opens with a big heading: *Bikes for every road*. At 3rem it looks bold and confident on a laptop. On a phone, the same 3rem heading wraps onto four lines and pushes everything else off the screen. A media query can switch it to a smaller size below a breakpoint, but then it jumps: one pixel narrower, and the heading suddenly shrinks. Designers call the smooth alternative **fluid type**: text that grows a little with every pixel of width, between a smallest and a largest size. Most modern sites, from news sites to game studios, size their headings this way, and it takes one function.

## A thermostat's range: clamp()

A thermostat in a shared office might be set to "follow the room, but never below 65°F and never above 75°F". Inside that range the temperature can drift; outside it, the thermostat holds the line.

**`clamp()`** (read "clamp", as in clamping something between two jaws) gives a size the same kind of range. It takes three values, in this order:

```css
h1 {
  font-size: clamp(minimum, preferred, maximum);
}
```

- **The minimum:** the smallest the size can be.
- **The preferred size:** what the browser uses while it's between the two limits. To be fluid, it grows with the window.
- **The maximum:** the largest the size can be.

The commas between them matter: leave one out, and the browser skips the declaration. The order matters too. With the minimum and the maximum swapped, nothing is skipped, but the larger number always wins, and the size stops being fluid. `clamp(a, b, c)` gives the same answer as `max(a, min(b, c))`, if you've met `min()` and `max()`, but it reads far more clearly.

## Reading a fluid heading

Here is Spoke & Chain's heading, made fluid:

```css
h1 {
  font-size: clamp(1.75rem, 1rem + 3vw, 3rem);
}
```

The preferred size is `1rem + 3vw`: 16 pixels, plus 3% of the window's width. You can do math inside `clamp()` with no `calc()`, with the same rule as in `calc()`: a space on each side of the plus sign. With the usual 16-pixel font size, here's what the browser works out:

| Window width | 1rem + 3vw | The heading is |
|---|---|---|
| 320 pixels | 16 + 9.6 = 25.6 pixels | **28 pixels**: below the minimum (1.75rem), so the minimum wins |
| 800 pixels | 16 + 24 = 40 pixels | **40 pixels**: inside the range, so the preferred size is used |
| 1440 pixels | 16 + 43.2 = 59.2 pixels | **48 pixels**: above the maximum (3rem), so the maximum wins |

Between 400 and about 1070 pixels wide, the heading grows smoothly with the window. Below that range it stays at 28 pixels, and above it, at 48. No breakpoint, no jump.

![A graph of the heading's size against the window's width: a flat line at 28 pixels for narrow windows, a straight ramp up through the middle, and a flat line at 48 pixels for wide windows, like a ramp between two floors](https://fsdtrainingmedia.blob.core.windows.net/public/images/2026/10/91967639f8ae47ac9927af50ea41b6df.webp)

**Try it.** Give a heading `font-size: clamp(1.75rem, 1rem + 3vw, 3rem)`, open the page with Live Server, and drag the window from narrow to wide. Watch the heading grow and then stop. Inspect it: the Computed pane shows the size it has right now.

## Why the rem is in there: zoom

It's tempting to write `font-size: 5vw`, and on your own screen it even looks right. But a size in `vw` alone has a serious problem: **it ignores the reader.**

- **Zoom.** When someone zooms in with **Ctrl and +** (**Cmd and +** on a Mac), every CSS pixel gets bigger, but the window holds fewer of them, so 5% of the window stays the same size on the screen. The text refuses to grow, for exactly the people who need it to.
- **The font size setting.** A reader who set their browser's text to *Large* gets nothing: `vw` follows the window, never the font size.

```css
.sale-heading {
  font-size: clamp(1.5rem, 1rem + 2.5vw, 2.5rem);
}
```

The `1rem` part of the preferred size is what fixes both. A rem grows with zoom and with the font size setting, so the whole size grows with it. And because the minimum and the maximum are in rem as well, the limits grow too: with the setting on *Large* (20 pixels), the `1.5rem` minimum is 30 pixels instead of 24. Keep a rem in the preferred size, and the limits in rem, and fluid text stays readable for everyone.

**Try it.** Give one heading `font-size: 5vw` and another `clamp(1.5rem, 1rem + 2.5vw, 2.5rem)`. Zoom the page to 200%. Which heading grew?

## Picking the numbers

You don't need a formula to start. Pick the two sizes you'd use without fluid type, then find a preferred size that moves between them:

1. **The minimum:** the size that looks right on a phone, such as `1.75rem`.
2. **The maximum:** the size that looks right on a wide screen, such as `3rem`.
3. **The preferred size:** start with `1rem + 3vw`. If the heading reaches its maximum too soon, lower the vw part (`2vw`); if it stays at its minimum too long, raise it (`4vw`).

```css
h2 {
  font-size: clamp(1.25rem, 1rem + 1.5vw, 2rem);
}
```

Smaller headings get a smaller range, and body text usually needs no clamp at all: `1rem` is the size the reader chose. When you want exact numbers, free tools such as the Utopia fluid type calculator (utopia.fyi) work out the preferred size from the two widths and the two sizes you give them.

## Fluid spacing

Space works the same way. The padding around a section and the gaps between cards can be snug on a phone and generous on a wide screen:

```css
.section {
  padding: clamp(1rem, 5vw, 4rem);
}

.catalog {
  display: grid;
  gap: clamp(0.75rem, 2vw, 2rem);
}
```

On a phone 320 pixels wide, the section's padding is 16 pixels and the cards are 12 pixels apart. On a laptop 1440 pixels wide, the padding is 64 pixels (5vw would be 72, so the maximum wins) and the gap is 28.8 pixels. Spacing isn't text, so a preferred size of plain `vw` is fine here: nobody zooms in to read a gap.

**Try it.** Give a page's sections padding with `clamp()`, and check it at 320, 800 and 1440 pixels wide in device mode. Compare it with a fixed padding of `4rem`, which leaves very little room for words on the small phone.

## Check yourself

2 questions to try on the lesson page.

## So what

Fixed sizes look right at one width and wrong at the others; a media query fixes a few widths and jumps between them. `clamp()` makes a size right at every width, with a floor and a ceiling you choose, and with a rem inside, it still respects the reader who zooms in.

## Key terms

- [clamp()](https://www.ask-codey.com/glossary/clamp)
- Fluid typography

## Cheat sheets

- [Fluid Type and Spacing with clamp()](https://www.ask-codey.com/cheat-sheets/css-clamp-fluid-type-cheat-sheet)

## Related

- [calc(), min() and max(): Math in CSS](https://www.ask-codey.com/lessons/css-calc-min-max)
- [Units: px, rem, em, % and Viewport Units](https://www.ask-codey.com/lessons/css-units)
- [Media Queries and Breakpoints](https://www.ask-codey.com/lessons/css-media-queries)

Source: https://www.ask-codey.com/lessons/css-clamp-fluid-type
