Published

Fluid typography with clamp(), without the guesswork

closeup photo of cutout decors
Photo by Raphael Schaller on Unsplash

The old way to size headings was a base size and then a media query or three: 32px on phones, 40px on tablets, 56px on desktop. It works, and it also means the heading jumps in size at arbitrary widths and is wrong for every screen in between. clamp() does the same job in one declaration and scales continuously.

How clamp() works

clamp(min, preferred, max) returns the preferred value, but never less than the minimum or more than the maximum. For type, the minimum and maximum are the sizes you want at the smallest and largest viewports, and the preferred value is a formula in viewport units that passes through both:

font-size: clamp(2rem, 1.25rem + 2.5vw, 3.5rem);

At a 480px viewport that evaluates to 1.25rem + 12px = 32px; at 1440px it is 1.25rem + 36px = 56px; in between it slides. The part that puts people off is deriving the 1.25rem + 2.5vw, which is the equation of a straight line through two points. The clamp calculator does that from four numbers: minimum size, maximum size, and the viewport widths at which each should apply. It returns the declaration ready to paste.

Do the whole scale, not one heading

One fluid heading is a start; a fluid type scale is the real win. Choose a base body size (16px to 18px is normal) and a ratio (1.2 for a modest scale, 1.25 or 1.333 for more contrast between levels), and each step up the scale multiplies by the ratio. The fluid typography generator builds the complete set of steps as custom properties, each its own clamp(), with a different ratio at the small and large ends if you want headings to grow faster than body text on wide screens. The output is a block of :root variables you can use as font-size: var(--step-3) throughout.

Use rem in the fixed parts

Two rules keep fluid type accessible. First, the minimum and maximum must be in rem, not px, so that a user who has set a larger default font size in their browser gets larger text. Second, the preferred value should include a rem component alongside the vw one, exactly as the calculators output it; a pure vw value ignores the user's preference entirely and can fail WCAG's 200% zoom requirement. The px to rem converter turns the pixel sizes in a design file into rem at whatever base you are using, which matters if the design was built at 16px and your body text is 18px.

Spacing scales the same way

Once the type is fluid, fixed margins look wrong: a 24px gap under a 32px heading is fine, and under a 56px heading it is cramped. The fluid spacing generator applies the same clamp() approach to a spacing scale, so gaps grow with the type and the page keeps its proportions at every width.

Check it in the browser

Resize the window slowly from 320px to 1600px and watch the headings. If any step of the scale ends up smaller than the step below it at some width, the two clamps cross and one of the ranges needs adjusting; the generator shows the curves together so you can spot this before it ships. And check the 200% zoom case: at that setting the text should be roughly twice the size, not the same size in a narrower viewport.

The short version

Pick a min and max size, let a calculator derive the middle, keep rem in every part of the expression, and generate the whole scale rather than one heading at a time. Then delete the media queries.

More from the blog

← All posts