Skip to main content
Syllabus
On this page

Fluid Typography with clamp()

Badar KhalilUpdated September 30, 2026 9 min read

Fluid Typography with clamp(): Complete Guide

Focus keyword: fluid typography. Fluid typography means text that grows smoothly with the screen instead of jumping at breakpoints. With clamp() you set a minimum size, a preferred size that scales with the viewport and a maximum size. In this lesson you will learn the formula behind the preferred value, how to keep fluid text accessible when users zoom, how to build a complete fluid type scale with custom properties, how to choose line height and line length, and how to use container units for component-based typography.

fluid typography clamp, responsive font size, clamp rem vw formula, fluid type scale, modular scale CSS, Utopia fluid type, WCAG 1.4.4 resize text, browser zoom accessibility, line-height unitless, measure 65ch, fluid spacing, text-wrap balance, cqi container units typography, variable fonts, font-display swap, vw units problem, type scale CSS variables, responsive headings.

1. From Breakpoints to Fluid Type

The old method sets the font size at each breakpoint: 16px, then 18px, then 20px. The text jumps suddenly at each step. Fluid typography calculates the size continuously between a small screen and a large screen, so one line of CSS replaces several media queries and every screen width gets a well-proportioned size.

2. The clamp() Formula

font-size: clamp(min, preferred, max). The preferred value is a line: intercept + slope * 100vw. To move from size minSize at viewport minVw to size maxSize at viewport maxVw (all in pixels):

  • slope = (maxSize - minSize) / (maxVw - minVw)
  • intercept = minSize - slope * minVw
  • preferred = intercept (in rem) + (slope * 100) vw

Example: 16px at 320px and 20px at 1200px gives slope 0.004545 and intercept 14.55px, so clamp(1rem, 0.909rem + 0.4545vw, 1.25rem). You can use the JavaScript generator in the last example to calculate it for any pair of values.

3. Accessibility: Keep Text Zoomable

WCAG 1.4.4 requires that text can be resized to 200 percent without loss of content. A font size made only of vw units does not react to browser zoom in the expected way, because zoom changes the viewport width in CSS pixels only slightly for the text. Always include a rem part in the preferred value, and use rem for the minimum and maximum. As a rule of thumb, keep the maximum no larger than about 2.5 times the minimum. Test by zooming to 200 percent and by changing the default font size in the browser settings. Also keep html { font-size: 100% } so the user setting is respected, and never set the root size in pixels.

4. A Fluid Type Scale

A type scale is a set of sizes related by a ratio, for example 1.25 (major third). Use a smaller ratio on small screens, such as 1.2, and a larger ratio on big screens, such as 1.25, so headings have more contrast on large displays. Define the steps as custom properties: --step--1 for small print, --step-0 for body text, and --step-1 to --step-5 for headings. Then write h1 { font-size: var(--step-5); }. The scale is easy to change in one place, and all sizes stay in harmony.

5. Line Height

  • Use a unitless line-height so it scales with each element font size.
  • Body text: 1.5 to 1.7.
  • Large headings need less: 1.05 to 1.25, because big text looks too loose with a large line height.
  • Small text such as captions may need a little more, about 1.6.
  • WCAG text spacing guidance expects that users can set line height to 1.5 without breaking the layout, so avoid fixed heights on text containers.

6. Line Length (Measure)

A comfortable line has about 45 to 75 characters. Use the ch unit: max-width: 65ch. On mobile the screen width itself limits the line, and on desktop the max-width does. Combine ch with fluid text and the line length stays readable at every size. Remember that ch depends on the font, so test with your real font.

7. Fluid Spacing

Apply the same idea to spacing so text and space stay in proportion: --space-m: clamp(1rem, 0.8rem + 1vw, 2rem). Use it for padding, margin and gap. Build small tokens such as --space-s, --space-m and --space-l, and multiply them for bigger gaps with calc(). Another useful option is the em unit for spacing inside components: padding in em follows the component font size.

8. Container-Based Fluid Type

Viewport units follow the screen, but a card in a narrow sidebar on a big screen needs smaller text. Container query units solve this: set container-type: inline-size on the parent and use font-size: clamp(1.25rem, 6cqi, 2.5rem) on the child. 1cqi is 1 percent of the container inline size. Always keep a rem part or fixed minimum and maximum so the text still respects zoom.

9. Polish the Typography

  • text-wrap: balance for headings creates even line lengths. text-wrap: pretty for paragraphs avoids a lonely last word. Both are progressive enhancements.
  • font-display: swap in @font-face shows fallback text immediately while the web font loads.
  • Variable fonts allow a range of weights and widths in one file, for example font-weight: 100 900, and can change weight smoothly at different sizes.
  • font-size-adjust helps the fallback font match the x-height of the web font, which reduces layout jump.
  • Use overflow-wrap: break-word for long words and URLs.
  • Use hyphens: auto with a lang attribute on narrow columns of justified or long text.

10. Testing Checklist

  1. Resize from 320px to 1600px and watch the headings and body text.
  2. Zoom to 200 percent and to 400 percent. Text must grow and nothing may be cut off.
  3. Change the default font size in the browser settings.
  4. Check the longest heading and the longest word.
  5. Check contrast for both small and large text.

11. Common Mistakes

  • Using only vw for font size, which breaks accessibility.
  • Writing clamp arguments in the wrong order.
  • Making the maximum much more than 2.5 times the minimum.
  • Setting html { font-size: 62.5% } or pixel sizes and hurting user preferences.
  • Using a fixed line-height in pixels on text that scales.
  • Fluid body text that changes too fast, which feels unstable. Headings can change more than body text.
  • Forgetting a measure, so lines become too long on large screens.
  • Using fluid type inside a narrow container while basing it on the viewport instead of the container.

Key Takeaways

Use clamp() with a rem-based preferred value so text scales smoothly and still zooms, build a type scale with custom properties, use unitless line height, limit line length with ch, make spacing fluid too, consider container units for components, and always test with zoom and larger default font sizes.

Try it Yourself HTML
Output

Press Run to execute.

Try it Yourself HTML
Output

Press Run to execute.

Try it Yourself HTML
Output

Press Run to execute.

Try it Yourself HTML
Output

Press Run to execute.

Try it Yourself HTML
Output

Press Run to execute.

Try it Yourself HTML
Output

Press Run to execute.

Try it Yourself JAVASCRIPT
Output

Press Run to execute.

Exercise: Create a Fluid Type ScaleHTML

Define four custom properties (--step-0, --step-1, --step-2, --step-3) with clamp(). Each preferred value must include a rem part. Use them for the paragraph, h3, h2 and h1. Give headings a unitless line-height below 1.3 and limit the paragraph to 65ch.

Try it Yourself HTML
Output

Press Run to execute.

Show expected output
Four --step variables using clamp(minrem, rem + vw, maxrem), headings using var(--step-n) with line-height under 1.3, and the paragraph with max-width: 65ch

This is a self-check — compare your result with the expected output above.

Exercise: Fix a vw-Only HeadingHTML

This heading uses only viewport units, so it is tiny on phones and ignores browser zoom. Replace it with clamp() so it is never smaller than 1.75rem, never larger than 4rem, and includes a rem part in the preferred value. Also add text-wrap: balance.

Try it Yourself HTML
Output

Press Run to execute.

Show expected output
h1 uses clamp(1.75rem, X rem + Y vw, 4rem) with a rem term in the middle value and text-wrap: balance

This is a self-check — compare your result with the expected output above.

Was this page helpful?