Body: 16px to 24px, from 320px to 1280px
- Input
- 16, 24, 320, 1280
- Expected output
- clamp(1rem, 0.8333rem + 0.8333vw, 1.5rem)
At 800px the size is 20px, exactly the midpoint.
how to calculate clamp() fluid typography formula
A fluid clamp() makes text size grow linearly between two viewports and lock at the ends. The numbers come from a straight line: the slope becomes the vw and the starting point becomes the rem term. The generator below shows each step of the math with your values.
At 800px the size is 20px, exactly the midpoint.
Slope 32 ÷ 960 = 0.03333, fixed term 32 - 0.03333 × 320 = 21.333px = 1.3333rem. At 800px: 48px.
Slope 16 ÷ 800 = 0.02, or 2vw; fixed term 16 - 0.02 × 400 = 8px = 0.5rem.
Inside the range the size follows the line; outside it, the clamp() locks at 16px or 24px.
Divide the pixels by the root font-size. With a 16px base: 24px ÷ 16 = 1.5rem, 14px ÷ 16 = 0.875rem and 10px ÷ 16 = 0.625rem. To go the other way, multiply: 1.25rem × 16 = 20px.
The extremes where the design actually changes: a narrow phone as the floor (320px to 375px) and the point where the layout stops growing as the ceiling (1200px to 1440px). There is no official value; choose from your breakpoints.
It is supported in current browsers, but check the MDN compatibility table for your audience. For older browsers, declare a fixed rem font-size first as a fallback.
Yes. The math is the same for any length property, such as padding, margin and gap. Use the Fluid type mode with your spacing sizes and copy just the clamp().
Here, % means a percentage of the reference font-size (as in font-size: 150%). In width, padding and other properties, % refers to something else, so this conversion does not apply to them.
To rem: 24 ÷ 16 = 1.5 rem
Paste a CSS block and swap every value in one unit for another, leaving everything else alone. Comments, strings, url() and numbers inside names (classes, variables, colors) are left untouched.
Paste a CSS block above to convert. The result shows up here instantly.
Quick reference with a root font-size of 16px. Click a rem value to copy it. The Tailwind column shows the step n where n × 0.25rem gives the same size.
| px | rem | Tailwind (n) |
|---|---|---|
| 4px | 1 | |
| 6px | 1.5 | |
| 8px | 2 | |
| 10px | 2.5 | |
| 12px | 3 | |
| 14px | 3.5 | |
| 16px | 4 | |
| 18px | - | |
| 20px | 5 | |
| 24px | 6 | |
| 28px | 7 | |
| 32px | 8 | |
| 36px | 9 | |
| 40px | 10 | |
| 48px | 12 | |
| 56px | 14 | |
| 64px | 16 | |
| 72px | - | |
| 80px | 20 | |
| 96px | 24 | |
| 112px | 28 | |
| 128px | 32 |
| n | rem | px |
|---|---|---|
| 0 | 0 | 0 |
| 0.5 | 0.125 | 2 |
| 1 | 0.25 | 4 |
| 1.5 | 0.375 | 6 |
| 2 | 0.5 | 8 |
| 2.5 | 0.625 | 10 |
| 3 | 0.75 | 12 |
| 3.5 | 0.875 | 14 |
| 4 | 1 | 16 |
| 5 | 1.25 | 20 |
| 6 | 1.5 | 24 |
| 7 | 1.75 | 28 |
| 8 | 2 | 32 |
| 9 | 2.25 | 36 |
| 10 | 2.5 | 40 |
| 11 | 2.75 | 44 |
| 12 | 3 | 48 |
| 14 | 3.5 | 56 |
| 16 | 4 | 64 |
| 20 | 5 | 80 |
| 24 | 6 | 96 |
| 28 | 7 | 112 |
| 32 | 8 | 128 |
| 36 | 9 | 144 |
| 40 | 10 | 160 |
| 44 | 11 | 176 |
| 48 | 12 | 192 |
| 52 | 13 | 208 |
| 56 | 14 | 224 |
| 60 | 15 | 240 |
| 64 | 16 | 256 |
| 72 | 18 | 288 |
| 80 | 20 | 320 |
| 96 | 24 | 384 |
In Tailwind, spacing is n × 0.25rem (the --spacing variable defaults to 0.25rem), so p-4 is 1rem. The px column follows the root font-size set above. The listed steps are the classic scale; the table on the left shows - when the size does not land on one of them.
Generate a clamp() that grows the size linearly between two viewports and locks at the ends. The output uses rem + vw, with the formula shown step by step.
The maximum size is less than twice the minimum. Adrian Roselli notes that with vw or a clamped size the user may be unable to reach 200% (WCAG 1.4.4, level AA), and suggests never letting the maximum fall below twice the base size. This does not guarantee conformance either way: test at 200% and 300% zoom.
1) Slope = (24 - 16) ÷ (1280 - 320) = 0.008333 font px per screen px
2) In vw: 0.008333 × 100 = 0.8333vw
3) Fixed term = 16 - 0.008333 × 320 = 13.3333px = 0.8333rem (÷ 16)
4) Preferred value = 0.8333rem + 0.8333vw, locked between minimum and maximum by clamp()
Pure vw ignores the user font-size preference and can break text zoom. That is why the fixed term is in rem: it follows the preference, and only the part that grows with the screen uses vw.
| Viewport | px | rem |
|---|---|---|
| 320px | 16 | 1 |
| 375px | 16.46 | 1.029 |
| 414px | 16.78 | 1.049 |
| 768px | 19.73 | 1.233 |
| 1024px | 21.87 | 1.367 |
| 1280px | 24 | 1.5 |
| 1440px | 24 | 1.5 |
| 1920px | 24 | 1.5 |
Fluid typography changes size with the screen
Size now: 20px (1.25rem)
The box uses the same formula with cqw (container unit) in place of vw, so the browser resolves the clamp() for real. On narrow screens the box cannot exceed the screen width.
Each step is the base size multiplied by the ratio raised to the step number. Pick a ratio, see the scale in rem and px, and export it as CSS variables or Tailwind.
Samples stop at 72px to fit the screen; the table values are the real ones.
:root {
--text-7xl: 5.9605rem;
--text-6xl: 4.7684rem;
--text-5xl: 3.8147rem;
--text-4xl: 3.0518rem;
--text-3xl: 2.4414rem;
--text-2xl: 1.9531rem;
--text-xl: 1.5625rem;
--text-lg: 1.25rem;
--text-base: 1rem;
--text-sm: 0.8rem;
--text-xs: 0.64rem;
}Convert breakpoints from px to em and generate the media queries. Paste a list (one per line, or comma separated) in px, em or rem.
| px | em | Condition | Copy |
|---|---|---|---|
| 640 | 40 | (min-width: 40em) | |
| 768 | 48 | (min-width: 48em) | |
| 1024 | 64 | (min-width: 64em) | |
| 1280 | 80 | (min-width: 80em) | |
| 1536 | 96 | (min-width: 96em) |
@media (min-width: 40em) {
/* ... */
}
@media (min-width: 48em) {
/* ... */
}
@media (min-width: 64em) {
/* ... */
}
@media (min-width: 80em) {
/* ... */
}
@media (min-width: 96em) {
/* ... */
}The Media Queries specification says relative units in media queries are based on the initial value, never on declarations in the page. In HTML, em is the initial font-size defined by the browser or the user preferences, not what your CSS declares.
Practical consequence: em and rem are the same inside a media query, and an html with font-size: 62.5% does not change the breakpoints. If the user raises the default font size, em breakpoints rise with it and the layout switches earlier, which is usually what you want for larger text.
Range syntax (width >= 48em, width < 48em) removes the need for the 0.02px trick, because the upper bound is exclusive. It only works in recent browsers; check support before using it in production.
rem is a multiple of the root element font-size, which by default is the font size set in the browser (16px in the default setting). If the person raises that size in settings, everything in rem grows with it. A px value stays fixed, whatever the preference. Pick the browser default below and compare.
Does the text adapt to the reader?
font-size: 16px results in 16px, always
Does the text adapt to the reader?
font-size: 1rem results in 16px
Browser zoom (Ctrl and +) enlarges the whole page, including what is in px. The font-size preference, available in browsers like Chrome, Edge and Firefox, only affects what is relative (rem, em, %).