Skip to content
Typography

Setting up fluid typography with clamp

How two sizes turn into a type scale that grows between phone and desktop, why line length is the setting that really matters and how text-wrap calms the line breaks in your headlines.

10 min readUpdated: 14 September 2026
TypographyCSSTailwindResponsive
Two sizes01
Series 04
Light that stays all day
Six lamps from one workshop in Utrecht.
See the rangeShowroom

Stepping font sizes at breakpoints gives you a page that is right in three places and jumps in between. Fluid typography replaces the steps with a straight line: you set two points, the browser works out the rest. This guide shows the formula behind it, how it grows into a full scale in your theme, why line length matters more than any single size and how two new CSS values calm down line breaks.

The short version
  • clamp describes a straight line between two points: one size at a narrow width, one at a wide one. In between the browser does the math, and the breakpoint prefixes disappear.
  • The middle value always needs a rem part. Without it the size hangs on the window alone and ignores whatever someone set in their browser.
  • Line length is the setting that really matters. Around 65 characters, with line height running opposite to size.
  • text-wrap: balance calms headlines, pretty keeps single words off the last line of a paragraph. Where a browser does not know the value, nothing changes.
01

Why are fixed font sizes not enough?

The usual route is steps at breakpoints: text-3xl on the phone, md:text-5xl from 768 pixels, lg:text-6xl from 1024. That is correct in exactly three places. In between nothing happens, and then the headline jumps twenty pixels because the window got one pixel wider.

The jump itself would be bearable, nobody drags a window open slowly and watches. The stretch before it is the annoying part. Between 768 and 1024 pixels the area grows by a third while the type stands still. Right there, on small laptops and tablets in landscape, a page ends up either cramped or lost.

Fluid typography turns this around. Instead of setting steps you describe a straight line: this big on the narrowest device, this big on the widest, and in between the browser does the math. Three classes with breakpoint prefixes become one without.

A breakpoint is a decision about layout. For a font size it is rarely the right answer.

02

How does clamp actually work?

clamp takes three values: a lower limit, a preferred value and an upper limit. The browser uses the preferred value as long as it sits between the limits, otherwise the nearest limit. The interesting part is in the middle.

The preferred value has two parts: a fixed base in rem and a slope in vw. The vw part ties the size to the width of the window, the rem part keeps it tied to the root font size. Together they describe a straight line between two points.

You set those two points: one size at a narrow width, one at a wide one. The slope follows from them, the base from the slope. For 32 pixels at 360 and 56 pixels at 1280 that is 24 pixels of growth over 920 pixels of distance, so 2.61vw, and a base of 1.41rem.

HandyAa20px
TabletAa30px
DesktopAa42px
The same line at three widths. Between the endpoints the math decides, not a breakpoint.
/* 32px bei 360px Breite, 56px bei 1280px Breite */.ueberschrift {  font-size: clamp(2rem, 1.41rem + 2.61vw, 3.5rem);  line-height: 1.05;} /* 16px bei 360px, 18px bei 1280px */.absatz {  font-size: clamp(1rem, 0.95rem + 0.22vw, 1.125rem);  line-height: 1.6;}
A straight line between two points, in one line.
Without a rem part the text stops listeningA pure vw value hangs on the width of the window alone. Anyone who set a larger base size in their browser no longer notices any of it, because vw does not know about that setting. The rem part in the middle value restores the connection. It is not polish, it is the reason the formula looks the way it does.
03

How do two sizes become a whole scale?

A single headline is quick work. A page, though, needs five to seven sizes that belong together. The usual route is a ratio: every step is larger than the one before it by the same factor, say 1.2 on the phone and 1.333 on the desktop.

That the ratio may be larger at the top than at the bottom is where the real gain sits. On a narrow screen the sizes have to stay close together, otherwise the headline bursts its line. On a wide one they may spread out, because there is room for a clear difference. Fluid values handle that spreading on their own once you pick separate endpoints per step.

Name the steps after their job, not their size. display, title, lead, body, small: those names survive a redesign. text-56 does not, because the moment it becomes 48 pixels there is a lie in your code.

/* app.css */@theme {  --text-display: clamp(2.5rem, 1.72rem + 3.48vw, 4.5rem);  --text-display--line-height: 1.02;  --text-display--letter-spacing: -0.03em;   --text-title: clamp(1.75rem, 1.41rem + 1.52vw, 2.625rem);  --text-title--line-height: 1.15;   --text-lead: clamp(1.125rem, 1.03rem + 0.43vw, 1.375rem);  --text-lead--line-height: 1.45;}
Five roles instead of fifteen classes with breakpoint prefixes.
The limits matter more than the decimal placesYou can round the slope, nobody will see it. The upper and lower limits are the real protection: below the narrowest and above the widest width nothing should move any more. So check the scale at 320 and at 2560 pixels first, not in the middle.

Few roles beat many values.

A list of hex numbers is an inventory. It becomes a system only once every value has a job.

ColourFour roles instead of sixteen values.
TypographyOne scale, no one-offs.
SpacingA four pixel step is enough.
04

Why does line length matter more than font size?

A fluid scale solves one problem and creates another. When the type grows with the width, the line grows too, and past a certain length the eye loses the next one on the way back. Between sixty and seventy-five characters is the range in which longer texts stay calmly readable.

The unit for this is ch. It measures the width of the digit zero, so it is an approximation rather than a count, but a usable one: max-width: 65ch hits the intended line pretty closely in most typefaces. In Tailwind the same size is called max-w-prose.

Line height belongs with it, and it runs opposite to size. Large type needs little leading, because the lines are far apart anyway. Small type needs more, otherwise the paragraph sticks together. Around 1.05 for a large headline and 1.6 for body copy is a workable starting point.

Looks impressive on the desktop
  • Letting body copy run the full width. At 1600 pixels that is a good 160 characters per line, and after the third paragraph nobody is reading closely any more.
  • Using the same line height for headline and paragraph, usually 1.5. The headline then falls apart into two lines with nothing to do with each other.
  • Putting the lower limit for body copy below 16 pixels so more fits on the phone.
  • Carrying the letter spacing from the design over to every size. What helps a headline makes body copy airy to the point of unreadable.
Reads well everywhere
  • Capping the text column at around 65ch and giving the width you free up to the layout, not to the line.
  • Tying line height to size: tight at the top, open at the bottom. In Tailwind v4 it sits right next to the value of the step.
  • Starting body copy at 16 pixels. That is the browser default, and it is not a weakness, it is an agreement.
  • Using negative letter spacing only from the title size upwards, and there in small increments.

Font size decides how it looks. Line length decides whether it gets read.

05

How do you make headlines wrap calmly?

A fluid headline has one unpleasant side effect: the line break keeps shifting with it. Sometimes five words sit on top and one below, sometimes the last word is left alone on the second line. Nothing about that is broken, but it looks accidental, and accidents read as restless.

Two CSS values have arrived recently that take over exactly this work. text-wrap: balance distributes the words across the lines so that all of them come out roughly equal in length. It is meant for headlines and it is genuinely good there. text-wrap: pretty works the other way around: it largely leaves the wrapping as it is, but stops a single word from ending up alone at the end.

Both values cost computation, so they are capped. balance works up to six lines in Chromium, up to ten in Firefox and without a fixed limit in Safari. Beyond that it falls back to normal wrapping, with no error and no jump. That matches the use case well, because what you want balanced are headlines of two or three lines, not paragraphs.

.ueberschrift {  text-wrap: balance;} .absatz {  text-wrap: pretty;} /* dasselbe in Tailwind */  <h1 className="text-display text-balance">  <p className="text-lead max-w-prose text-pretty">
One value for the headline, one for the paragraph.
pretty is not everywhere yetbalance has been available in all major browsers since 2024. Chrome has had pretty since version 117 and Safari since version 26, while Firefox is still missing it as of this article. That is not critical, because an unknown value is simply ignored: the paragraph then wraps as it did before. Nothing shifts, only the polish is missing.
06

What belongs in the theme and what in the markup?

Tailwind v4 has everything you need for this. A variable under --text- inside the @theme block becomes a utility class, and you attach the accompanying settings with a double hyphen: --text-title--line-height and --text-title--letter-spacing. Three lines of CSS become one class.

That leaves one word in the markup. text-title sets size, line height and letter spacing at once, and it does so at every window width. The breakpoint prefixes disappear because they have nothing left to do.

Individual outliers can join in square brackets, but sparingly. When the same square bracket shows up for the third time it is no longer an exception, it is a step, and it belongs in the theme. That line is what keeps a scale small.

/* vorher */<h1 className="text-3xl leading-tight md:text-5xl lg:text-6xl"> /* nachher */  <h1 className="text-display text-balance"> /* und der Absatz darunter */  <p className="text-lead max-w-prose text-pretty">
Three classes and two breakpoints before, one class after.
Square brackets are no substitute for the themeTechnically a clamp formula fits straight into a class as well, as text-[clamp(2rem,1.41rem+2.61vw,3.5rem)]. It is not readable, and it is not shared either: with the same formula in four places you will later change it in three. The theme is where a size gets a name.
07

Which mistakes cost the most?

The formula is the easy part. The places where a fluid scale tips over are elsewhere: at the limits, while the font loads and in the question of whether the window was ever what you meant.

  1. The upper limit is missing. With no third value the headline keeps growing on a wide monitor. At 2560 pixels a line ends up on screen that nobody reads as a headline any more, only as a poster.
  2. The lower limit is too small. On a phone, 14 pixels instead of 16 wins you a few characters per line and loses everyone who has to hold the device closer. Body copy starts at 16.
  3. The type jumps while loading. A fluid size makes the switch from fallback font to real font more visible, because the line is calculated tightly anyway. font-display: swap plus a size-adjust on the fallback keeps the jump small.
  4. You meant the box, not the window. In a narrow sidebar the font size still hangs on the width of the window, and a card in a third of the page gets the same headline as one across the full width. For that there is cqi instead of vw, inside a @container.

The last point is also the bridge to the next tool. cqi relates to a container exactly as vw relates to the window: one percent of the inline width. If you build blocks that end up in several places, swap vw for cqi and you get type that follows the space actually available instead of a number decided somewhere else.

vw asks the window. cqi asks the space the block actually has.

Share
Written by
Amelie RoesmannCreative directionLeonie RoesmannDesign engineering

We build websites and web apps at Systra Studios in Münster and put the building blocks from that work here. What you read comes from real projects, not from a brochure.

Systra Studios: web design from Münster

You know what matters.
The reading is the tool's job.

Enter a URL, get colours, typography and spacing. As a DESIGN.md for people or a tokens.json for your build.

  • Colours are checked against the rendered page.
  • Values close to each other get merged.
  • Output as markdown or JSON, both ready to copy.
Open Style Extractor
Result
  • primary#1f5eff
  • surface#f8fafc
  • text#0f172a
  • border#e2e8f0
TypographyAaInter · 16 / 20 / 28 / 40
FAQ

Frequently asked questions

No, it only saves time. The math is a straight line through two points: the slope is the difference in size divided by the difference in width, the base is the smaller size minus slope times the smaller width. Do it once for five steps and the scale is yours for years. Generators handle the decimal places, nothing more.

Didn't find your question? We're happy to help.

Get in touch