Guide

← CSS Clamp Calculator

CSS clamp() and Browser Zoom: Why Pure vw Units Fail Accessibility

The one-line rule: never let the middle value of clamp() be vw alone. Here is the zoom-safe formula, the demo numbers that prove the problem, and where even the fix runs out of road.

The safest clamp() pattern for fluid type is exactly one line:

font-size: clamp(MINrem, Xrem + Yvw, MAXrem);

The rem values are the floor and ceiling, and the preferred value mixes a rem term with a vw term. That is the whole artifact, and you can copy it into a stylesheet right now. The rest of this article is about why the vw term must never stand alone, told through the numbers from the demo that convinced me.

What actually happens when a low-vision user zooms your page

WCAG 1.4.4, Resize Text, level AA, requires text to scale up to 200% without assistive technology and without breaking the page. Here is the catch: when a user zooms to 200%, the CSS viewport shrinks to half its width, but vw units follow the viewport, not the zoom level. So font-size: 4vw or clamp(1.5rem, 5vw, 3rem) barely budges, because the viewport got smaller exactly when the text needed to get bigger.

Adrian Roselli demonstrated this with font-size: clamp(1.5rem, 5vw, 3rem) in a 1,024px-wide window. At normal zoom the text rendered 64px tall. At 200% zoom it had grown to only 68px, a 6.25% increase when the user asked for 200%. Even at 300% zoom it reached just 96px. Roselli's conclusion was blunt: used as-is, that code guarantees a WCAG failure.

Why rem + vw fixes it

The fix is the rem term in the preferred value. A browser's default font-size setting scales the rem, and zooming scales the rem too, so 0.9rem + 1.125vw responds to both the viewport width and the user's zoom or font-size preference. The vw term still does the fluid scaling between the breakpoints you designed for, and the rem term keeps the floor honest: even when the viewport contribution collapses toward zero, the size stays anchored to something the user controls.

Concrete example. This is a genuinely safe fluid heading:

h2 { font-size: clamp(1.125rem, 0.9rem + 1.125vw, 1.5rem); }

MIN and MAX are both rem, so a user who sets their browser default to 24px gets the floor and ceiling scaled too. The preferred value carries the rem component, so zoom works. One pattern, no exceptions for text.

Where the formula breaks down

Two limits worth knowing, because this is where I see people over-trust the pattern.

First, the ceiling still clamps. If a user zooms aggressively enough that the rem-scaled preferred value would exceed MAX, the browser holds MAX and the text stops growing. For headings with a tight ceiling this can still read as stuck. The honest response is not to delete the ceiling, which protects your layout at wide viewports, but to keep ceilings generous on text that matters, or to test at 200% zoom with a large root font size and confirm it still grows enough to pass.

Second, this whole discussion applies to text. Spacing is different: padding and margins in vw or px do not carry the same accessibility obligation, and a clamp() with px bounds on padding is not a WCAG failure. Do not spread the rem rule where it does not belong; the requirement is about text resizing, and body text should usually stay a plain 1rem so user preferences pass through untouched anyway.

My actual rule, the short version

Body text: fixed 1rem, always. Headings and display type: the rem + vw pattern above, generated once and sanity-checked at 200% zoom with the browser's default font size bumped up. If a heading never gets exercised at zoom, assume it fails. The demo numbers are from 2019 and the behavior has not changed, because it is how the spec defines viewport units, not a browser bug.

Frequently asked questions

Does CSS clamp() break browser zoom?

It can. A preferred value using only vw units tracks the CSS viewport width and barely responds to zoom: in Adrian Roselli's demo, text at 200% zoom grew only 6.25%. Adding a rem component to the preferred value fixes it.

What is the accessible way to write clamp() for font sizes?

Use rem for the min and max, and a rem + vw expression for the preferred value, e.g. clamp(1.125rem, 0.9rem + 1.125vw, 1.5rem). The rem part anchors to the user's font setting so zoom works; the vw part gives the fluid scaling.

What does WCAG 1.4.4 require for text resizing?

Text must be resizable up to 200% without assistive technology and without loss of content or functionality. Viewport-only fluid type can prevent that from working, which is a level AA failure.

Should body text use clamp() at all?

Usually not. Body text has a hard readability floor, so 1rem keeps user font-size preferences intact. Reserve clamp() for headings and display type where the scaling is a design choice.

Generate accessible clamp() values

Every value this calculator emits uses rem-based bounds, so your fluid type stays zoom-safe.

Open the CSS Clamp Calculator

Keep reading

CSS clamp(), Finally Explained: Min, Preferred, and Max

How the browser resolves min, preferred, and max, with a worked fluid-type example.

Using CSS clamp() for Fluid Spacing: Padding, Margins, and Gaps That Scale Themselves

Fluid section padding, card gutters, and grid gaps that scale with the viewport.

CSS clamp() Fallbacks for Older Browsers: Two Methods That Work

Two fallback approaches for clamp(), plus the IE11 edge case that breaks the fancy one.

Prefer this by email? Get future guides in your inbox.