# Responsive font size: readable text on phones with rem and clamp()

> A responsive font size keeps text readable on a 360 px phone and a 1440 px desktop without a jump at every breakpoint. The minimum sizes that work on mobile, fluid type with clamp(), and how to keep it zoomable.

Updated 2026-09-26 · Accessibility · HTML version: https://getreport.app/guides/responsive-font-size

A responsive font size is text that stays readable at every screen width: about 16 px for body copy on phones, headings that grow smoothly with the screen instead of jumping at breakpoints, and sizes set in `rem` so they follow the visitor's own text-size setting and zoom. The modern way to do it is one line of CSS per level with `clamp()`. This guide gives the sizes that work on mobile, the CSS to copy, and the mistakes that make fluid type unreadable or unzoomable. It is part of our guide on [how to make a website mobile friendly](https://getreport.app/guides/how-to-make-a-website-mobile-friendly).

## Quick answer

- **Body text: 16 px (1rem) on phones,** with a line height of about 1.5. Going larger on desktop (17–20 px) is fine; going smaller on phones is not.
- **Nothing under 12 px,** including captions, footers and legal text. getReport counts text under 12 px at each width.
- **Form fields at 16 px or more,** or iOS Safari zooms the page when someone taps them.
- **Use `rem`, not `px`,** for font sizes and `html { font-size: 100% }`, so visitors who set a bigger default text size get it.
- **Fluid headings with `clamp()`:** `font-size: clamp(1.75rem, 1.2rem + 2.5vw, 3rem)` grows with the screen between two limits.
- **Never size text in `vw` alone.** It stops text from growing when visitors zoom, which fails WCAG 1.4.4.
- **Check it** with the free [mobile-friendly test](https://getreport.app/tools/responsive-check), which renders your page at 360 to 1440 px and flags text under 12 px.

## What size should text be on mobile?

There is no legal minimum font size, but there are clear conventions:

| Text | Phone | Desktop | Why |
| --- | --- | --- | --- |
| Body | 16 px (1rem) | 16–20 px | Browser default; readable at arm's length |
| Form inputs | 16 px minimum | 16 px | Below 16 px, iOS Safari zooms in on focus |
| Small print, captions | 12–14 px | 12–14 px | Anything smaller is hard to read on a phone |
| H1 | 28–36 px | 40–64 px | Should fit a short title in two or three lines at 360 px |
| H2 | 22–28 px | 28–40 px | Clear step down from H1 |
| Line height (body) | 1.5 | 1.5–1.6 | WCAG 1.4.12 tests text at 1.5 |

The 16 px baseline is not arbitrary. It is the default font size in every major browser, the size platform guidelines use for body text (Material Design's largest body style is 16 sp; Apple's default body text on iOS is 17 pt), and the size iOS Safari treats as large enough not to zoom into form fields.

"Text too small to read" was one of the issues in Google's Mobile Usability report until Google retired it, along with the Mobile-Friendly Test, on 1 December 2023. The problem did not go away with the report: small text still sends visitors back to the search results.

### Line length

Size and line length work together. Comfortable reading is roughly 45–75 characters per line, a range typographers have used for decades. A phone at 16 px gives about 35–45 characters, which is fine. A desktop layout with 16 px text across a 1,200 px column gives 150; cap the text column instead:

```css
/* main stylesheet */
.prose { max-width: 65ch; }
```

## Use rem so visitors can make text bigger

Browsers let people choose a default text size: Chrome under Settings → Appearance → Font size, Firefox under Settings → General → Fonts, and phones through their accessibility settings. Sizes in `rem` are relative to that default; sizes in `px` ignore it.

```css
/* keep the visitor's default (16 px unless they changed it) */
html { font-size: 100%; }

body { font-size: 1rem; line-height: 1.5; }
small, .caption { font-size: 0.8125rem; }   /* 13 px at the default */
input, select, textarea { font-size: max(16px, 1rem); }
```

Avoid the old `html { font-size: 62.5% }` trick, which makes `1rem` equal 10 px for easier arithmetic. It works, but every size then depends on remembering to set it back up, and components copied from elsewhere end up tiny.

Use `em` or `rem` in media queries too, so breakpoints move when the visitor's text size changes; our guide to [media queries and breakpoints](https://getreport.app/guides/media-queries-and-breakpoints) explains why.

## Fluid type with clamp()

The traditional way to make type responsive is a new size at each breakpoint:

```css
h1 { font-size: 1.75rem; }
@media (min-width: 48em) { h1 { font-size: 2.5rem; } }
@media (min-width: 64em) { h1 { font-size: 3rem; } }
```

That works, but the heading jumps at 768 px and 1,024 px and is either too small or too large in between. `clamp()`, supported in every current browser, replaces it with one line:

```css
h1 { font-size: clamp(1.75rem, 1.2rem + 2.5vw, 3rem); }
```

It reads as: never smaller than 1.75rem (28 px), never larger than 3rem (48 px), and in between, 1.2rem plus 2.5 % of the viewport width. At 360 px wide that is about 28 px, at 1,280 px about 51 px capped to 48 px.

### How to calculate the middle value

Pick the size you want at the narrowest and widest screen, then solve for a straight line between them. For 28 px at 360 px and 48 px at 1,280 px:

1. **Slope:** (48 − 28) ÷ (1280 − 360) = 0.0217, which is 2.17vw.
2. **Intercept:** 28 − 0.0217 × 360 = 20.2 px, which is 1.26rem.
3. **Result:** `clamp(1.75rem, 1.26rem + 2.17vw, 3rem)`.

Many online "fluid type calculators" do the same arithmetic. What matters is that the middle value is **rem plus vw**, never vw alone.

### A fluid type scale

Apply the same idea to every level, and keep body text close to fixed, because fluid body text rarely helps:

```css
:root {
  --step--1: clamp(0.8125rem, 0.79rem + 0.1vw, 0.875rem);  /* small: 13–14 px */
  --step-0:  clamp(1rem, 0.96rem + 0.2vw, 1.125rem);       /* body: 16–18 px */
  --step-1:  clamp(1.25rem, 1.15rem + 0.45vw, 1.5rem);     /* h3: 20–24 px */
  --step-2:  clamp(1.5rem, 1.3rem + 0.9vw, 2rem);          /* h2: 24–32 px */
  --step-3:  clamp(1.75rem, 1.26rem + 2.17vw, 3rem);       /* h1: 28–48 px */
}
body { font-size: var(--step-0); }
h1 { font-size: var(--step-3); }
h2 { font-size: var(--step-2); }
h3 { font-size: var(--step-1); }
```

## Keep fluid type zoomable

WCAG 2.2 success criterion 1.4.4 (Resize Text) requires that text can be enlarged to 200 % without losing content or function. Fluid type can quietly break it:

- **`vw` alone does not zoom.** Browser zoom makes CSS pixels bigger, which makes the viewport narrower in CSS pixels, so text sized in `vw` stays the same size on screen or even shrinks. `font-size: 5vw` fails 1.4.4.
- **Adding `rem` fixes most of it,** because the `rem` part grows with zoom and with the visitor's text-size setting.
- **A wide clamp range can still cap growth.** When a visitor zooms, the maximum stops the text growing further. As a rule of thumb from Maxwell Barvian's analysis of fluid type and WCAG in Smashing Magazine, keeping the maximum at no more than 2.5 times the minimum lets text reach 200 % at every width. The scale above stays well within that.

Test it: at 1,280 px wide, zoom the browser to 200 % and check that headings and body text are roughly twice as large. Our guide to [the viewport tag and pinch-zoom](https://getreport.app/guides/zoom-and-viewport-never-disable-pinch-zoom) covers zoom on phones, where `maximum-scale=1` and `user-scalable=no` must stay out of the viewport tag.

> **Check: Visitors can zoom the page on their phone.** Many people zoom web pages on their phone to read small text. A viewport tag with user-scalable=no or a low maximum-scale turns that off in Chrome on Android; only iOS Safari ignores it.
>
> 1. Change the viewport meta tag to <meta name="viewport" content="width=device-width, initial-scale=1">.
> 2. Remove user-scalable=no, and remove maximum-scale or set it to 5 or more; in WordPress this usually lives in the theme header or a mobile plugin.

## Mobile browsers that change your font size

Two browser behaviours surprise people:

- **Font boosting without a viewport tag.** Without `<meta name="viewport" content="width=device-width, initial-scale=1">`, phones lay the page out at desktop width and some browsers enlarge paragraphs they judge unreadable, so text sizes come out uneven. Add the viewport tag and the browser uses your sizes.
- **iOS text inflation in landscape.** Safari on iOS can enlarge text when a phone is rotated. `-webkit-text-size-adjust: 100%` (and the unprefixed `text-size-adjust: 100%`) on `html` stops it without blocking user zoom. Do not use `none`, which can also stop desktop Safari users from enlarging text.

```css
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
```

> **Check: Viewport meta tag is present.** Without a viewport tag, phones render the page at desktop width and shrink it. Google indexes the mobile version first, so this hurts rankings directly.
>
> 1. Add <meta name="viewport" content="width=device-width, initial-scale=1"> in <head>.

## Fonts affect readability too

Two fonts at the same pixel size can look very different, because x-heights differ. A font with a small x-height may need 17–18 px to read like 16 px in a system font. Check real text at 360 px on a real phone, not only the number in the CSS. If you use a web font, load it so text shows immediately in a fallback font; the [font-display fix page](https://getreport.app/learn/font-display) and our guide to [system fonts versus web fonts](https://getreport.app/guides/web-fonts-without-layout-shift) cover the speed side.

> **Check: Web fonts show text while loading.** Without font-display, browsers show blank text for up to 3 s while a web font downloads. Visitors stare at empty headings.
>
> 1. Add font-display swap (or optional) to every @font-face rule.
> 2. For Google Fonts, append &display=swap to the stylesheet URL; self-hosted fonts get it in the @font-face block.
> 3. Preload the one or two fonts used above the fold.

## How to check your font sizes

1. **Render the page at phone widths.** The free mobile-friendly test loads your page in Chromium and resizes it to 360, 414, 768, 1024 and 1440 px. At each width it counts text smaller than 12 px and names where it is, alongside sideways scrolling and small tap targets.

> **Free tool:** [Mobile-friendly test and CSP generator](https://getreport.app/tools/responsive-check): Free mobile-friendly test to replace Google's retired one: screenshots at five widths, sideways scrolling, tiny text and tap targets, plus a CSP generator.

2. **Inspect computed sizes.** In Chrome DevTools, select a paragraph and open Computed → font-size, at the 360 px device preset. Check body, captions, footer links, form inputs and buttons.
3. **Change the browser's default text size** to "Large" or "Very large" and reload. Text set in `rem` should grow; text that stays the same is in `px`.
4. **Zoom to 200 %** at desktop width and confirm that text doubles and nothing is cut off.
5. **Run an accessibility check.** The [accessibility checker](https://getreport.app/tools/accessibility-checker) runs axe-core, which catches related problems such as low contrast on small text.

## Common mistakes

- **Body text at 14 px on phones** because it looked right in a desktop mockup.
- **Headings in `vw` only,** which stop zooming and become huge on wide screens.
- **Form inputs at 14 px,** then `maximum-scale=1` added to stop the iOS zoom. Set inputs to 16 px instead.
- **Light grey 12 px text** in footers and captions; small text needs more contrast, not less.
- **`62.5 %` on `html`** with components that assume a 16 px root.
- **Setting sizes in `px` in a page builder,** which ignores the visitor's text-size setting. Most builders accept `rem` or `em`.

## Questions people ask

### What is the minimum font size for mobile?

Use 16 px for body text and form inputs on phones, and keep small print such as captions and footer text at 12 px or more. WCAG sets no minimum pixel size, but it requires text to resize to 200 %, and iOS Safari zooms into form fields under 16 px. Set sizes in rem so visitors who choose larger text in their browser get it.

### How do I make font size responsive in CSS?

Set body text in rem, then use clamp() for headings: font-size: clamp(1.75rem, 1.26rem + 2.17vw, 3rem) grows a heading smoothly from 28 px on a phone to 48 px on a desktop. Always combine vw with rem in the middle value, keep html at font-size: 100 %, and cap the text column at about 65ch for comfortable line length.

### Is it bad to use vw units for font size?

Using vw alone is bad for accessibility. Text sized only in viewport units does not grow when a visitor zooms the browser, which fails WCAG 1.4.4 Resize Text, and it becomes very large on wide screens. Use vw only inside clamp() together with a rem value and sensible minimum and maximum sizes, so zoom and text-size settings still work.

### Why does iPhone zoom in when I tap a form field?

Because the field's font size is under 16 px. iOS Safari zooms the page so the text you type is readable, then leaves the page zoomed. The fix is to set inputs, selects and textareas to at least 16 px, for example input { font-size: max(16px, 1rem); }. Don't disable zoom in the viewport tag instead; that blocks visitors who need to enlarge text.
