Published 2026-02-22 · 3 min read
By Akash Yadav · Frontend Engineer
Fluid Typography Explained (Without Magic Numbers)
Derive fluid type from two anchor points, understand slope and intercept, and ship clamp() strings your team can document.
Why fluid typography
Fixed-size type feels wrong at the extremes. On a 320px mobile screen, a 32px headline eats half the viewport width. On a 2560px monitor, 16px body copy looks like fine print. Responsive typography with hard breakpoints is an improvement but still creates noticeable jumps.
Fluid typography uses clamp() to draw a straight line between a minimum size at a narrow viewport and a maximum size at a wide viewport. Every point on that line is intentional — no jumps, no surprises.
The result is type that feels designed at every viewport width, not just at the sizes you tested.
Choose your anchors
A fluid type definition needs four inputs:
- Minimum font size — the size at your narrowest viewport
- Minimum viewport — the narrow anchor (usually 320px or 375px)
- Maximum font size — the size at your widest comfortable reading width
- Maximum viewport — the wide anchor (usually 1200px, 1440px, or 1920px)
Practical starting points for a content site:
| Type role | Min size (320px) | Max size (1440px) |
|---|---|---|
| Body copy | 1rem (16px) | 1.125rem (18px) |
| Small/caption | 0.875rem (14px) | 0.875rem (14px) — fixed |
| H3 subheading | 1.125rem (18px) | 1.375rem (22px) |
| H2 section heading | 1.375rem (22px) | 2rem (32px) |
| H1 page heading | 1.75rem (28px) | 3rem (48px) |
| Display/hero | 2rem (32px) | 4.5rem (72px) |
Document those decisions in your design system README or token file. Numbers without context rot — future engineers need to know the anchor viewports to maintain or extend the scale.
The math (slope and intercept)
Fluid type is a linear interpolation between two points. The math is straightforward once you understand the coordinate system:
- X-axis: viewport width (in px, then converted to vw units)
- Y-axis: font size (in rem)
Given two anchor points (vp_min_px, size_min_rem) and (vp_max_px, size_max_rem):
slope (rem per px) = (size_max_rem - size_min_rem) / (vp_max_px - vp_min_px)
slope_as_vw = slope × 100 (convert to per-100px = per-1vw)
intercept_rem = size_min_rem - slope × vp_min_px
clamp(size_min_rem, intercept_rem + slope_as_vw * 1vw, size_max_rem)
Concrete example: scaling an H1 from 1.75rem at 320px to 3rem at 1440px:
slope = (3 - 1.75) / (1440 - 320) = 1.25 / 1120 ≈ 0.001116 rem/px
slope_vw = 0.001116 × 100 = 0.11161 rem/vw ≈ 11.161 per 100vw
intercept = 1.75 - 0.001116 × 320 = 1.75 - 0.3571 = 1.3929rem
Result:
font-size: clamp(1.75rem, 1.3929rem + 1.1161vw, 3rem);
The fluid typography generator performs this calculation instantly. Input the four numbers, copy the output.
Generator output and usage
The fluid generator outputs REM-first clamp strings:
/* H1: 28px → 48px, viewport 320px → 1440px */
font-size: clamp(1.75rem, 1.3929rem + 1.1161vw, 3rem);
/* Body: 16px → 18px, viewport 320px → 1440px */
font-size: clamp(1rem, 0.9643rem + 0.1786vw, 1.125rem);
Drop these directly into your CSS or into CSS custom properties:
:root {
--font-display: clamp(2rem, 1.2rem + 4vw, 4.5rem);
--font-h1: clamp(1.75rem, 1.39rem + 1.12vw, 3rem);
--font-h2: clamp(1.375rem, 1.15rem + 1.12vw, 2rem);
--font-h3: clamp(1.125rem, 1.04rem + 0.45vw, 1.375rem);
--font-body: clamp(1rem, 0.96rem + 0.18vw, 1.125rem);
--font-small: 0.875rem; /* fixed — no meaningful range */
}
Pair with related utilities:
- PX to REM for secondary text styles that stay fixed
- REM to EM when a component needs local rhythm relative to a fluid parent
Building a fluid type scale
A fluid type scale has consistent proportions at every viewport. The key is deriving all steps from the same pair of anchor viewports, using a consistent ratio between adjacent steps.
/* Major Third scale (1.25 ratio) at 320px and 1440px */
:root {
--font-xs: clamp(0.64rem, 0.61rem + 0.14vw, 0.72rem);
--font-sm: clamp(0.8rem, 0.76rem + 0.18vw, 0.9rem);
--font-base: clamp(1rem, 0.96rem + 0.18vw, 1.125rem);
--font-md: clamp(1.25rem, 1.2rem + 0.27vw, 1.4rem);
--font-lg: clamp(1.563rem, 1.5rem + 0.32vw, 1.75rem);
--font-xl: clamp(1.953rem, 1.87rem + 0.41vw, 2.188rem);
--font-2xl: clamp(2.441rem, 2.33rem + 0.54vw, 2.75rem);
--font-3xl: clamp(3.052rem, 2.91rem + 0.68vw, 3.438rem);
}
Each step maintains the 1.25× ratio at both the minimum and maximum viewport. The system is self-consistent — there are no jumps or proportional breaks at any screen size.
Tip: Keep max font sizes conservative on data-dense dashboards — tables and forms with very large headings feel unbalanced. Marketing pages can handle more dramatic ranges.
Line-height and fluid type
Line-height should not be fluid for most text. A unitless multiplier (1.4–1.7 for body, 1.1–1.3 for large headings) is typically correct across the size range.
What does benefit from responsive adjustment:
/* Heading: tighter line-height at large sizes */
h1 {
font-size: var(--font-h1);
line-height: 1.15;
}
/* Body: comfortable reading line-height */
p {
font-size: var(--font-base);
line-height: 1.65;
}
For calculating the precise line-height value for a given font-size and desired visual baseline, see the line-height calculator.
Browser compatibility
| Feature | Chrome | Firefox | Safari | Notes |
|---|---|---|---|---|
clamp() | 79+ | 75+ | 13.1+ | Safe in all modern browsers |
| CSS custom properties | 49+ | 31+ | 9.1+ | Safe in all modern browsers |
vw unit | 20+ | 19+ | 6+ | Universal |
Global support for clamp() is above 97% in 2026. No fallback is needed in most production contexts.
Key takeaways
- Four numbers define fluid type: min size, min viewport, max size, max viewport.
- The math is a linear interpolation — the generator handles it automatically.
- Use REM for min and max — it respects user font-size preferences.
- Build a scale, not one-offs — consistent ratios between steps at both anchors.
- Line-height is usually fixed — unitless multipliers work better than fluid line-heights for most text.
Frequently asked questions
- Should line-height be fluid too?
- Often line-height tracks better as a unitless multiplier (1.4–1.7) rather than a fluid value. At very small sizes, tight line-height aids compact layouts; at large display sizes, slightly tighter line-height (1.1–1.2) improves readability. Minor breakpoint adjustments are cleaner here than continuous fluid values.
- What is the difference between fluid type and responsive type?
- Responsive type uses discrete breakpoints to jump between fixed sizes (e.g. 16px on mobile, 18px on desktop). Fluid type uses clamp() to interpolate continuously between sizes as viewport width changes. Fluid type produces smoother results and requires fewer breakpoints.
- How do I choose the right anchor viewports?
- Use your actual minimum and maximum layout widths. If your site is comfortable at 320px and stops growing past 1440px, use those as your anchors. Avoid extreme ranges (e.g. 200px–3000px) as they produce very shallow slopes that barely scale.
- Should I use px or rem in my clamp() values?
- Use rem for the min and max values to respect user font-size preferences. The vw component does not have a rem equivalent, but the overall value will still scale correctly relative to the root.
- Can I use fluid typography in Tailwind?
- Yes. Generate the clamp() strings with the fluid generator, then add them to your Tailwind config's `fontSize` values, or inject them as CSS custom properties and reference them with Tailwind's arbitrary value syntax.
- Does fluid typography affect SEO?
- No. Font sizes are presentation, not content. What matters for SEO is that headings use correct semantic tags (h1, h2, etc.) and that text remains legible (minimum ~14px rendered size at all viewports).