Skip to main content
  • Created:
  • Updated:
  • Author:
    Takeshi Takatsudo

Screen-Width Based Font Size Definition

The Problem

Basic clamp() typography scales font size linearly across the entire viewport range. This works for simple cases, but falls short when you need precise control over how text scales within specific breakpoint ranges. For example, a site logo might need to:

  • Stay at 15–24px within the lg breakpoint range (1024–1280px)
  • Scale to 24–30px within the xl range (1280–1536px)
  • Reach 30–36px in the 2xl range (1536–1920px)

A single clamp() cannot express this piecewise scaling. You would need media queries with fixed sizes, creating jarring jumps — or a single clamp that doesn't precisely hit the sizes you want at each breakpoint boundary.

The Solution

Combine breakpoint-specific media queries with per-range clamp() values. Each breakpoint gets its own clamp() that smoothly scales within that range, and the ranges are stitched together so the maximum of one range equals the minimum of the next.

This article builds on Fluid Font Sizing with clamp(). Make sure you understand basic clamp() usage before reading further.

The Formula

For a given breakpoint range from startVw to endVw, scaling font size from minSize to maxSize:

slope = (maxSize - minSize) / (endVw - startVw)
intercept = minSize - slope × startVw

This gives you:

font-size: clamp(minSize, calc(intercept + slope × 1vw), maxSize);

For example, scaling 15px → 24px across the 1024px → 1280px range:

slope = (24 - 15) / (1280 - 1024) = 9 / 256 ≈ 0.03516 (3.516vw)
intercept = 15 - 0.03516 × 1024 = 15 - 36 ≈ -21px

Result: clamp(15px, calc(-21px + 3.516vw), 24px)

Verification: At 1024px → calc(-21 + 0.03516 × 1024) = calc(-21 + 36) = 15px. At 1280px → calc(-21 + 0.03516 × 1280) = calc(-21 + 45) = 24px.

Code Examples

Basic Segmented Fluid Font Size

.site-title {
/* Base: fixed size for small screens */
font-size: 15px;
}

/* lg: 1024px – 1280px → scale 15px to 24px */
@media (min-width: 1024px) {
.site-title {
font-size: clamp(15px, calc(-21px + 3.516vw), 24px);
}
}

/* xl: 1280px – 1536px → scale 24px to 30px */
@media (min-width: 1280px) {
.site-title {
font-size: clamp(24px, calc(-6px + 2.344vw), 30px);
}
}

/* 2xl: 1536px – 1920px → scale 30px to 36px */
@media (min-width: 1536px) {
.site-title {
font-size: clamp(30px, calc(6px + 1.563vw), 36px);
}
}

Note: The demos below use scaled-down breakpoints (320px / 500px / 768px) so the fluid scaling is visible within the preview iframe. The code examples above show production-appropriate breakpoints.

Segmented fluid font size — switch between Mobile and Full viewports to see scaling

Design Token System with Segmented Scaling

Define a reusable token system where each font size step has precise per-breakpoint scaling:

:root {
/* Small screens: fixed base sizes */
--font-size-display: 24px;
--font-size-title: 20px;
--font-size-heading: 18px;
--font-size-body: 16px;
}

/* lg: 1024px – 1280px */
@media (min-width: 1024px) {
:root {
--font-size-display: clamp(28px, calc(-4px + 3.125vw), 36px);
--font-size-title: clamp(22px, calc(-2px + 2.344vw), 28px);
--font-size-heading: clamp(18px, calc(2px + 1.563vw), 22px);
--font-size-body: clamp(16px, calc(8px + 0.781vw), 18px);
}
}

/* xl: 1280px – 1536px */
@media (min-width: 1280px) {
:root {
--font-size-display: clamp(36px, calc(-24px + 4.688vw), 48px);
--font-size-title: clamp(28px, calc(-12px + 3.125vw), 36px);
--font-size-heading: clamp(22px, calc(2px + 1.563vw), 26px);
--font-size-body: clamp(18px, calc(8px + 0.781vw), 20px);
}
}
Design token font sizes — each step scales independently per breakpoint

Calculating clamp() Values for Any Range

Here is a step-by-step example for calculating the clamp() preferred value for any viewport range.

Goal: Scale font from 22px at 1024px viewport to 28px at 1280px viewport.

Step 1: Calculate the slope
slope = (28 - 22) / (1280 - 1024)
= 6 / 256
≈ 0.02344

Step 2: Calculate the intercept (base offset in px)
intercept = minSize - slope × startVw
= 22 - 0.02344 × 1024
≈ 22 - 24
= -2px

Step 3: Assemble the clamp()
font-size: clamp(22px, calc(-2px + 2.344vw), 28px);

Verification: At 1024px viewport → calc(-2 + 0.02344 × 1024) = calc(-2 + 24) = 22px. At 1280px → calc(-2 + 0.02344 × 1280) = calc(-2 + 30) = 28px.

Full Page Header Example

A real-world example showing a site header with logo text that scales smoothly across all breakpoints:

Site header with segmented fluid logo text — compare Mobile vs Full viewport

Common AI Mistakes

  • Using a single clamp() across the full viewport range when precise per-breakpoint control is needed — the scaling rate cannot be tuned for different ranges
  • Forgetting to align the max of one range with the min of the next, causing visible jumps at breakpoint boundaries (e.g., 24px max at lg but 26px min at xl creates a 2px jump)
  • Getting the formula wrong — the denominator is the viewport range (endVw - startVw), not (endVw - minSize). Mixing viewport width with font size produces incorrect slopes
  • Using vw alone without calc() offset — font-size: 2vw gives 20px at 1000px viewport but you cannot control where it starts and stops
  • Applying segmented clamp to body text or small UI labels where a single clamp() or fixed size is sufficient — this technique is best for display and title text
  • Not verifying the math — always check that calc(intercept + slope × startVw) equals your intended minimum size

When to Use

  • Site logos and brand text that must hit exact sizes at specific breakpoints
  • Design token font-size definitions where each token step needs independent scaling behavior
  • Display headings in hero sections that require different scaling rates at different viewport ranges
  • Any element where a single clamp() doesn't give enough control over the scaling curve

Prefer a single clamp() (covered in Fluid Font Sizing) when:

  • The scaling range is simple (one min → one max across all viewports)
  • Pixel-precise control at breakpoint boundaries is not required
  • You want minimal CSS for straightforward responsive text

References