Type something to search...
Why You Should Use rem Instead of px for Font Sizes

Why You Should Use rem Instead of px for Font Sizes

Open Chrome's settings, go to Appearance, and change "Font size" from Medium to Very Large. Now reload a handful of sites you've built. On some, the text grows noticeably. On others, nothing happens at all, because every font size is hard-coded in pixels. The people who change that setting are mostly readers with low vision, older users, and anyone reading on a high-resolution laptop where default text feels small. A site set in px quietly ignores all of them.

This article explains how rem and px differ, why rem respects user preferences while px overrides them, how zoom and font-size settings behave differently, how to migrate an existing codebase, and the places where px is still the right unit.

What Are px and rem?

px is an absolute CSS unit. One CSS pixel is defined as 1/96 of an inch at a typical viewing distance. It doesn't map to physical device pixels on high-density screens, but within CSS it's a fixed reference: 16px is always 16px, no matter what the user prefers.

rem stands for "root em." It's relative to the font size of the root element, the html element. If the root font size is 16px, then 1rem equals 16px, 1.5rem equals 24px, and 0.875rem equals 14px.

The crucial detail is where the root font size comes from. If you don't set it, or set it with a percentage, it comes from the user's browser default, which is 16px out of the box but can be changed by the user.

User's default font size16px renders as1rem renders as
16px (Medium, default)16px16px
20px (Large)16px20px
24px (Very Large)16px24px

At default settings, the two units look identical. The difference only appears for users who've asked for larger text, and those are exactly the users who need it most.

Zoom vs. Default Font Size: Two Different Settings

Developers often argue that px is fine because browser zoom scales everything anyway. Zoom does scale pixel-based text, but zoom and the default font size are separate features with different effects:

  • Page zoom (Ctrl or Cmd plus) enlarges everything: text, images, spacing, and layout. It changes the size of a CSS pixel, so px, rem, and em all grow. It also reduces the effective viewport width, which triggers responsive breakpoints.
  • Default font size (browser settings) changes only the root font size. Text set in rem and em grows; text set in px does not. Images and pixel-based layout stay the same.

Many users prefer the font size setting because it enlarges text without making images huge or forcing horizontal scrolling on sites that don't reflow well. Some set it once and never think about it again. Others rely on operating system settings that feed into it, such as Android's font scale in Chrome. Firefox also offers a "Zoom text only" option, which behaves similarly.

If your site uses px, it works with the first method and silently fails the second. Using rem supports both. The companion post on supporting browser zoom and user font preferences covers testing both in detail.

What WCAG Says

WCAG 2.2 success criterion 1.4.4 Resize Text (Level AA) requires that text can be resized up to 200% without assistive technology and without loss of content or functionality. Because browser zoom satisfies this, a px-only site can technically pass.

But passing the letter of the criterion isn't the same as serving users well. The WCAG technique C28, "Specifying the size of text containers using em units," and the related guidance both encourage relative units, and many accessibility auditors flag fixed px font sizes as a usability issue. Using rem is the low-effort way to support more of the methods people actually use to enlarge text.

Don't Override the Root Font Size in px

The fastest way to undo all the benefits of rem is to set the root in pixels:

/* Bad: locks the root and ignores user preferences */
html {
  font-size: 16px;
}

Now 1rem always equals 16px, and you're back to fixed sizing with extra steps. Leave the root alone, or use a percentage if you need to change it:

/* Good: leaves the user's preference in control */
html {
  font-size: 100%;
}

What About the 62.5% Trick?

A once-popular pattern sets the root to 62.5% so that 1rem equals 10px, making conversions easy:

html {
  font-size: 62.5%; /* 10px at default settings */
}

body {
  font-size: 1.6rem; /* back to 16px */
}

This does respect user preferences, because it's a percentage. The problems are practical:

  1. Third-party components break. Any widget, embed, or library that assumes 1rem equals 16px will render 37.5% too small.
  2. Unstyled elements shrink. Anything you forget to size explicitly drops to 10px.
  3. It fights frameworks. Tailwind CSS, for example, defines its spacing and type scale assuming a 16px root. With 62.5%, text-base becomes 10px.

You don't need the trick. Divide by 16 in your head, keep a short conversion table, or let a CSS function or design token do it.

px to rem Conversion Table

At the default 16px root:

pxremCommon use
120.75remFine print (use sparingly)
140.875remCaptions, metadata
161remBody text minimum
181.125remComfortable body text
201.25remLead paragraph, H4
241.5remH3
301.875remH2
362.25remH1 on mobile
483remH1 on desktop

If you'd rather keep thinking in pixels while writing CSS, use calc() with a custom property:

:root {
  --px: 0.0625rem; /* 1/16 rem */
}

.caption {
  font-size: calc(14 * var(--px)); /* 0.875rem */
}

Where Else Should You Use rem?

Font sizes are the most important place to use rem, but spacing that surrounds text benefits too:

  • Padding and margins around text in buttons, cards, and form fields. If text grows and padding stays in px, components look cramped.
  • Max widths for text containers. A max-width in rem or ch grows with the text, preserving a readable line length. A fixed 680px container will hold fewer and fewer words as text grows.
  • Media queries. Breakpoints in rem or em trigger based on text size, so a user with large text gets a single-column layout sooner, which is usually what they need.
.card {
  padding: 1.25rem 1.5rem;
  border-radius: 0.5rem;
}

.prose {
  max-width: 42rem; /* or 68ch */
}

@media (min-width: 48rem) {
  .layout {
    grid-template-columns: 1fr 18rem;
  }
}

One quirk worth knowing: rem inside a media query is always relative to the browser's initial font size, not to any font-size you set on html. That's actually helpful, because breakpoints then follow the user's preference directly.

When px Is Still the Right Choice

Not everything should scale with text. Use px for things that should stay crisp and fixed:

  • Borders and hairlines. A 1px border should stay 1px. A border of 0.0625rem becomes 1.5px at a 24px root, which can render blurry.
  • Box shadows and outlines that are decorative rather than structural. Focus outlines are an exception worth considering; a thicker outline for users with large text is not a bad thing.
  • Icon sizes in some cases, though icons placed next to text usually look better in em so they match the text they sit beside.
  • Fixed-size media, such as a logo with specific dimensions.
.input {
  font-size: 1rem;
  padding: 0.625rem 0.75rem;
  border: 1px solid #6b7280;
}

.icon-inline {
  width: 1em;
  height: 1em;
}

rem vs. em: Which One for Font Sizes?

Both are relative, but they compound differently. em is relative to the parent element's font size, so nested em values multiply. A list inside a list with font-size: 0.9em gets smaller at every level. rem always refers back to the root, so it's predictable no matter where an element sits.

A good rule of thumb:

  • Use rem for font sizes, so components look the same wherever they're placed.
  • Use em for spacing that should scale with a component's own text, such as padding inside a button or the size of an inline icon.

For a broader look at all the units, including vw, ch, and the newer rlh and cap, see understanding CSS font-size units.

Migrating an Existing Codebase from px to rem

If you inherit a px-heavy stylesheet, migrate in stages rather than all at once.

  1. Remove any pixel root size. Delete html { font-size: 16px; } or replace it with 100%.
  2. Convert font sizes first. Search for font-size: declarations in px and divide by 16. Font sizes deliver almost all of the accessibility benefit.
  3. Convert text-related spacing next. Padding on buttons and inputs, margins between paragraphs, and container max widths.
  4. Leave borders in px.
  5. Test with a 24px default font size and at 200% zoom.

To find the declarations, a quick search helps:

grep -rnE "font-size:\s*[0-9.]+px" src/styles

For larger projects, the postcss-pxtorem plugin can automate conversion during the build, with an exclusion list for properties like border. Converting at the source is cleaner long term, but the plugin is useful when you can't touch every file.

Tailwind CSS v4

Tailwind already uses rem for its type scale and spacing, so its defaults respect user preferences. If you define custom sizes in your theme, keep them in rem too:

@import "tailwindcss";

@theme {
  --text-base: 1.0625rem;
  --text-base--line-height: 1.65;
  --text-lead: 1.25rem;
  --text-lead--line-height: 1.6;
}

This creates text-base and text-lead utilities that scale with the user's settings. For a complete setup, see how to configure typography in Tailwind CSS v4.

rem vs px FAQ

Yes. Page zoom enlarges everything, including text set in pixels. What px does not respond to is the browser's default font size setting, which many low-vision users rely on instead of zoom. rem responds to both.

Only when the user has not changed their browser's default font size and you have not changed the root font size. If a user sets their default to 20px, 1rem becomes 20px, which is exactly why rem is better for text.

Usually not. Borders and hairlines should stay crisp, and a rem-based border can become a fractional pixel value at larger root sizes. Keep borders in px and use rem for font sizes and the spacing around text.

It respects user settings because it is a percentage, but it causes practical problems. Third-party components and frameworks that assume a 16px root will render too small, and any element you forget to size drops to 10px. It is simpler to leave the root at 100 percent and convert by dividing by 16.

Using rem or em for breakpoints means layouts respond to the user's text size as well as the screen width. A user with a large default font will switch to a narrower layout sooner, which usually improves readability. Pixel breakpoints still work but ignore that preference.

rem is relative to the root html element, so it is consistent everywhere. em is relative to the parent element's font size, so nested em values compound. Use rem for font sizes and em for spacing or icons that should scale with their own component's text.

Conclusion

Using rem for font sizes costs nothing at default settings, since it renders exactly like px, and it makes your text respond to the browser font-size preference that many readers depend on. Pixel font sizes only respond to page zoom, which is one method among several. Leave the root font size alone or set it as a percentage, avoid the 62.5% trick, and you get predictable sizing that still bends to the user.

Apply rem to font sizes first, then to the spacing and container widths around text, and keep px for borders and other details that should stay fixed. Test with a large default font size and at 200% zoom, and you'll catch any components that still hard-code their text.

Here are some useful references for going deeper on rem and px units:

  1. MDN Web Docs: The length data type — reference for absolute and font-relative length units, including rem and px.
  2. W3C: CSS Values and Units Module Level 4 — the specification for font-relative lengths including rem and em.
  3. W3C WAI: Understanding Success Criterion 1.4.4: Resize Text — the requirement that text resize to 200 percent.
  4. Tailwind CSS Docs: Theme variables — how Tailwind v4 defines its rem-based type and spacing scales.
  5. Josh W. Comeau: The Surprising Truth About Pixels and Accessibility — an in-depth look at when to use rem, em, and px.
Share :

Related Posts

Ascenders, Descenders, and Baselines: The Anatomy of a Letterform

Ascenders, Descenders, and Baselines: The Anatomy of a Letterform

You align an icon next to a button label and it looks a pixel or two too high, no matter how you adjust vertical-align. You set overflow: hidden

Continue Reading
Are Google Fonts GDPR-Compliant?

Are Google Fonts GDPR-Compliant?

In late 2022, thousands of small business owners in Germany and Austria opened letters demanding a few hundred euros in "damages" because their websi

Continue Reading
How to Audit Web Font Performance with Lighthouse?

How to Audit Web Font Performance with Lighthouse?

A client sends you a screenshot of their PageSpeed Insights report: performance score 61, LCP 3.9 seconds, and a vague list of warnings. They want to

Continue Reading