Type something to search...
How to use viewport units for responsive typography?

How to use viewport units for responsive typography?

To use viewport units for responsive typography, combine a viewport unit such as vw with a rem value and wrap the result in limits, for example font-size: clamp(1.5rem, 1rem + 2vw, 3rem). One vw is 1% of the viewport's width, so text sized partly in vw grows as the browser window gets wider. Never size text in viewport units alone, because it becomes tiny on phones, enormous on large monitors and fails to grow properly when users zoom. Use vw (or the newer svi, lvi and dvi variants) for width-based scaling, avoid height units for body text, and reach for container query units like cqi when text belongs to a component rather than the whole page.

Viewport units are the engine behind most responsive heading styles, but they're also behind some of the most common accessibility failures in web typography. Understanding exactly what each unit measures lets you use them confidently. In this article you'll learn what each viewport unit means, why vw on its own is a problem, how to blend viewport units with rem, how the newer small, large and dynamic units work on mobile, when container units are a better fit, and how to test the results.

What Viewport Units Measure

The viewport is the visible area of the page in the browser. Viewport units express a length as a percentage of that area:

UnitEquals
vw1% of the viewport width
vh1% of the viewport height
vi1% of the viewport size in the inline direction (width in horizontal writing modes)
vb1% of the viewport size in the block direction (height in horizontal writing modes)
vminThe smaller of vw and vh
vmaxThe larger of vw and vh

On a 1440px-wide browser window, 1vw is 14.4px. On a 375px-wide phone it's 3.75px. That difference is the whole point: viewport units let a single value produce very different sizes on different screens.

The logical units vi and vb matter if you support vertical writing modes, such as some Japanese layouts, because they follow the text direction rather than the physical axes. For most left-to-right horizontal sites, vw and vi behave identically.

Small, Large and Dynamic Viewport Units

Mobile browsers complicate things, because their address bars and toolbars expand and collapse as you scroll. The viewport height changes, which used to make 100vh sections jump around. The CSS Values and Units Level 4 specification added three sets of units to deal with this:

  • Small viewport units (svw, svh, svi, svb, svmin, svmax): based on the viewport when browser toolbars are fully expanded, so the smallest possible area.
  • Large viewport units (lvw, lvh and so on): based on the viewport when toolbars are retracted, the largest possible area.
  • Dynamic viewport units (dvw, dvh and so on): change in real time as the toolbars expand and retract.

For typography, the distinction mostly affects height-based units. Toolbars change the height, not the width, so svw, lvw and dvw are usually the same as vw. All these units are supported in current versions of Chrome, Edge, Firefox and Safari.

Why Pure Viewport Type Fails

It's tempting to write something like this:

h1 {
  font-size: 5vw;
}

It looks great when you resize your desktop browser. But it causes three real problems.

Problem 1: No Limits

At 320px wide, 5vw is 16px, which is too small for a main heading. At 2560px wide, it's 128px, which is far too big. Without a minimum and maximum, there's no width at which you've designed for both ends.

Problem 2: Zoom Doesn't Work

When someone zooms a page to 200%, the browser doubles the size of a CSS pixel. The viewport, measured in CSS pixels, becomes half as wide. So 5vw computes to half as many CSS pixels, each drawn twice as large. The heading stays the same physical size on screen. Zoom effectively does nothing to the text.

WCAG 2.2 Success Criterion 1.4.4 (Resize Text) requires that text can be resized to 200% without assistive technology. Text sized purely in viewport units can fail that requirement, because the most common resize method, browser zoom, has little or no effect on it.

Problem 3: The Font Size Setting Is Ignored

Users can set a larger default font size in their browser. That setting changes what 1rem means, but it has no effect on vw. Viewport-only text ignores the preference entirely.

Combining Viewport Units with rem

The fix is to make the font size a sum of a fixed part and a fluid part. CSS calc() lets you add units together:

h1 {
  font-size: calc(1.5rem + 2vw);
}

Now the heading has a base of 1.5rem that responds to zoom and user settings, plus a 2vw part that responds to screen width. At 375px wide it's 24 + 7.5 = 31.5px. At 1440px it's 24 + 28.8 = 52.8px.

Adding clamp() puts firm limits on both ends:

h1 {
  font-size: clamp(2rem, 1.5rem + 2vw, 3.5rem);
}

This is the standard pattern for responsive headings today. The mathematics of picking the exact numbers for a given pair of sizes is covered in what fluid typography is and how to build it with clamp().

How Much vw Is Too Much?

The larger the vw part relative to the rem part, the less zoom affects the text. A practical guideline:

  • Body text: keep the vw contribution small, something like 0.25vw to 0.5vw, or don't make body text fluid at all.
  • Subheadings: 0.5vw to 1.5vw is usually plenty.
  • Large display headings: up to around 3vw to 4vw is common, with a rem maximum to cap it.

You can quickly see how much of the size comes from each part by testing the extremes in the console:

function sizeAt(remPart, vwPart, viewport, root = 16) {
  return remPart * root + (vwPart / 100) * viewport;
}

for (const width of [375, 768, 1280, 1920]) {
  console.log(width, sizeAt(1.5, 2, width).toFixed(1) + "px");
}
375 31.5px
768 39.4px
1280 49.6px
1920 62.4px

The output shows how the heading would grow without any clamp() limits, which helps you decide where to put the maximum.

Using Height and vmin Units

Height-based units are rarely a good idea for text. Phones in landscape orientation, short laptop screens and split-screen windows all have small heights, which would shrink your text to unreadable sizes. Page height also changes as on-screen keyboards appear.

There are a few legitimate uses:

  • Full-screen hero text: when a heading must fit inside a single-screen hero, vmin keeps it proportional to whichever dimension is smaller, so it fits in both orientations.
  • Presentation or kiosk layouts: fixed-format screens where the height is known.
  • Poster-style splash pages: where the heading is effectively an image.

Even then, include a rem component and limits:

.hero-title {
  font-size: clamp(2.5rem, 1rem + 6vmin, 6rem);
  line-height: 1.05;
}

.hero {
  min-height: 100svh;
  display: grid;
  place-items: center;
}

Here the hero uses 100svh so it always fits inside the visible area on mobile, even with the browser toolbar shown, and the title scales with the smaller viewport dimension.

Container Query Units for Components

Viewport units are tied to the browser window, which isn't always the right reference. Consider a card component used in three places: a full-width feature row, a three-column grid and a narrow sidebar. The viewport is identical in all three, but the space available to each card is very different.

Container query units measure the nearest ancestor that has been set as a size container:

UnitEquals
cqw1% of the container's width
cqh1% of the container's height
cqi1% of the container's inline size
cqb1% of the container's block size
cqmin / cqmaxThe smaller or larger of cqi and cqb

To use them, declare a container and size the text relative to it:

<article class="promo">
  <h3 class="promo__title">Summer sale on all plans</h3>
  <p class="promo__text">Save on annual subscriptions until the end of the month.</p>
</article>
.promo {
  container-type: inline-size;
}

.promo__title {
  font-size: clamp(1.25rem, 0.75rem + 4cqi, 2.25rem);
  line-height: 1.15;
}

.promo__text {
  font-size: clamp(0.9375rem, 0.85rem + 0.75cqi, 1.125rem);
}

The same component now has a large title in a wide slot and a compact title in a narrow one, with no media queries or modifier classes. The same rules about zoom apply: keep a rem component and rem limits. Container units have been supported in all major browsers since 2023.

If an element has no ancestor size container, container units fall back to the small viewport units, so the text still renders at a sensible size.

Line Length and Viewport Units

When text grows with the viewport, line length changes too, and it's easy to end up with very long lines on large screens. Cap the width of text containers with ch or rem, which keeps measure under control regardless of viewport:

.article {
  max-width: 68ch;
  margin-inline: auto;
  padding-inline: clamp(1rem, 5vw, 3rem);
}

Viewport units are excellent for side padding like this. Gutters that grow with the screen look natural, and padding doesn't have the same zoom concerns as text.

Testing Viewport-Based Typography

Run these checks whenever you use viewport units for text:

  1. Resize across the full range: use DevTools responsive mode and drag from about 320px to 1920px. Text should grow smoothly and stop at your limits.
  2. Zoom to 200%: at a desktop width, press Ctrl or Cmd and the plus key until you reach 200%. Body text and headings should become noticeably larger.
  3. Change the default font size: set the browser's default to "Very large" or 24px and reload. Text should grow.
  4. Rotate a phone: check landscape mode, especially if you've used any height-based units.
  5. Check with the address bar visible and hidden: on a real mobile device, scroll up and down to see if anything jumps.

In DevTools, the Computed panel shows the final font-size in px. Watching it as you zoom is the clearest way to confirm that text actually scales.


FAQ: Viewport Units for Typography

Using vw alone is risky because the text has no limits and barely changes size when users zoom. Combining vw with a rem value inside clamp() keeps the responsive behaviour while respecting zoom and user font settings.

Vw is 1% of the browser viewport's width. Cqi is 1% of the inline size of the nearest container element. Use cqi when text should respond to the space a component has, rather than the size of the whole window.

Generally neither. Height-based units make text depend on screen height, which varies a lot on mobile. If you need height-based sizing for a full-screen hero, svh is the more stable choice because it doesn't change as toolbars move.

Yes. Vw, vh, vmin and vmax have worked everywhere for many years. The small, large and dynamic variants and the container query units are supported in all current major browsers.

For font sizes, often yes. A clamp() value with a viewport unit covers the full range of widths smoothly. You may still want media queries for layout changes or for cases where a stepped size is clearer.

Use rem for the minimum and maximum, include a rem part in the preferred value, keep the viewport part modest, and test that text clearly grows at 200% browser zoom.


Conclusion

Viewport units let text respond to the size of the screen, and they're the reason modern headings can scale smoothly without a stack of breakpoints. Used carelessly, they also produce text that's too small on phones, too large on wide monitors and immune to zoom. The safe pattern is simple: add a rem value to a modest viewport value, and wrap it in clamp() with rem limits.

Prefer width-based units for type, keep height units for special full-screen cases, and switch to container query units when text belongs to a reusable component. Then test by resizing, zooming to 200% and changing the default font size. If your text passes those three checks, your viewport-based typography is both responsive and accessible.

Share :

Related Posts

How to make typography accessible?

How to make typography accessible?

You make typography accessible by choosing clear typefaces, setting text in relative units so it scales with user preferences, giving it enough colou

Dive Deeper
What are the parts of a letterform?

What are the parts of a letterform?

A letterform is the shape of a single letter, and typographers break it down into named parts. The main ones are the stem (the main vertical

Dive Deeper
What are ascenders, descenders and the baseline?

What are ascenders, descenders and the baseline?

The baseline is the invisible line that letters sit on. Ascenders are the parts of lowercase letters that rise above the x-height, such as th

Dive Deeper