Type something to search...
What is the difference between px, em and rem for font sizes?

What is the difference between px, em and rem for font sizes?

The difference between px, em and rem is what each unit is measured against. A px value is an absolute CSS pixel and stays the same no matter what surrounds it. An em is relative to the font size of the current element, or of its parent when used on font-size itself, so it compounds as elements are nested. A rem ("root em") is relative to the font size of the root html element, so it's consistent across the whole page and still respects the user's browser font size setting. For most font sizes on the web, rem is the best default, em is useful for values that should scale with a component's own text, and px is best kept for things like borders that shouldn't scale.

Choosing the wrong unit doesn't usually break a design on your own screen, which is why it's easy to get wrong. The problems appear for users who have changed their default font size, or when a component is reused somewhere with a different text size. In this article you'll learn how each unit is calculated, how they behave with zoom and user settings, the compounding problem with em, the 62.5% trick and why you probably don't need it, and a practical set of rules for which unit to use where.

How Each Unit Is Calculated

px: The CSS Pixel

A CSS pixel is not the same as a physical pixel on your screen. It's a reference unit defined so that text looks roughly the same size across devices at a typical viewing distance. On a high-density phone display, one CSS pixel may be drawn with two or three physical pixels in each direction. When you write font-size: 18px, the browser renders text 18 CSS pixels tall, whatever the device.

p {
  font-size: 18px;
}

That value never changes based on where the paragraph sits in the page.

em: Relative to the Current Font Size

An em equals the computed font size of the element it's used on. There's one twist: when you use em in the font-size property itself, it refers to the parent's font size, because the element doesn't have its own size yet.

.card {
  font-size: 20px;
}

.card p {
  font-size: 0.9em;     /* 0.9 × 20px = 18px */
  margin-bottom: 1em;   /* 1 × 18px = 18px (the paragraph's own size) */
}

The margin-bottom uses the paragraph's own computed size of 18px, not the card's 20px. This is what makes em so useful for spacing that should stay proportional to the text it surrounds.

rem: Relative to the Root Font Size

A rem equals the computed font size of the html element. In every major browser the default is 16px, unless the user has changed it in their settings.

h2 {
  font-size: 1.5rem;   /* 1.5 × 16px = 24px by default */
}

.sidebar h2 {
  font-size: 1.5rem;   /* still 24px, no matter what .sidebar sets */
}

Because rem always looks back to the same element, nesting has no effect on it.

A Quick Comparison

UnitRelative toCompounds when nestedFollows user font size setting
pxNothing (absolute CSS pixel)NoNo
emCurrent element's font size (parent's for font-size)YesYes
remRoot html font sizeNoYes

The User's Default Font Size

This is the most important reason to prefer relative units. Every major browser lets people change their default font size. In Chrome it's under Settings, Appearance, Font size. In Firefox it's under Settings, General, Fonts. People with low vision often raise it, and some people simply prefer larger text.

That setting changes the root font size. If the user picks a 20px default:

  • font-size: 1rem becomes 20px.
  • font-size: 1.5em on an element whose parent is at the root size becomes 30px.
  • font-size: 16px stays 16px, ignoring the user's choice.

So if you set your body text in px, you override a deliberate accessibility preference. Setting html { font-size: 16px; } does the same thing for the whole page, which is why you should avoid setting the root size in px.

What About Browser Zoom?

Browser zoom (Ctrl or Cmd and the plus key) is different from the font size setting. Zoom scales everything, including px values, because it changes the size of the CSS pixel itself. So px text does get bigger when zoomed. That's why px-based sites can technically pass WCAG 1.4.4 (Resize Text) through zoom alone.

However, not everyone uses zoom. Some people rely on the default font size setting instead because zoom also enlarges images and layout, causing more horizontal scrolling. Using rem means your text respects both methods.

You can check what a user has set with a tiny script in the console:

const rootSize = getComputedStyle(document.documentElement).fontSize;
console.log(`Root font size: ${rootSize}`);
Root font size: 16px

Change the default font size in your browser settings, reload, and run it again to see the difference.

The Compounding Problem with em

Em values multiply as you nest elements. That's useful when you want it and confusing when you don't. Consider a nested list:

<ul class="menu">
  <li>Products
    <ul>
      <li>Software
        <ul>
          <li>Desktop apps</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>
.menu li {
  font-size: 0.875em;
}

Each li is 0.875 times its parent. Assuming a 16px body, the results are:

Level 1: 16 × 0.875 = 14px
Level 2: 14 × 0.875 = 12.25px
Level 3: 12.25 × 0.875 = 10.72px

The third level is barely readable. Switching to rem fixes it, because every level looks back to the root:

.menu li {
  font-size: 0.875rem;  /* 14px at every level */
}

The same issue appears with components dropped into containers that have their own font size. A button styled with font-size: 0.875em will look different in a hero section set to 20px than in a footer set to 14px. Sometimes that's what you want; often it isn't.

When em Is the Right Choice

Em's compounding behaviour becomes an advantage when you want a component's spacing to stay in proportion with its text. The classic example is a button with size variants.

.button {
  font-size: 1rem;
  padding: 0.5em 1.25em;
  border-radius: 0.375em;
  gap: 0.5em;
}

.button--small {
  font-size: 0.875rem;
}

.button--large {
  font-size: 1.25rem;
}

You set the font size once in rem for each variant. The padding, radius and gap are in em, so they scale automatically. The large button gets proportionally larger padding without any extra rules.

Other good uses for em:

  • Icon sizing next to text: width: 1em; height: 1em; makes an inline SVG icon match the text it sits beside.
  • Letter spacing: letter-spacing: 0.05em stays proportional when the font size changes, which is essential for uppercase labels.
  • Margins around headings: margin-top: 1.5em on an h2 relates the gap to the heading's own size.
  • Underline offsets: text-underline-offset: 0.15em keeps underlines in the right place at every size.

The 62.5% Trick and Why You Might Skip It

A long-standing technique sets the root font size to 62.5% so that 1rem equals 10px by default, making maths easier:

html {
  font-size: 62.5%;  /* 10px when the default is 16px */
}

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

This does respect user settings, because it's a percentage. If a user's default is 20px, the root becomes 12.5px and body text becomes 20px. So it isn't an accessibility failure in itself.

The downsides are practical:

  • Every component must reset its size: anything that inherits the root size directly, including some third-party widgets, will render at 10px.
  • Third-party CSS breaks: libraries that assume 1rem is 16px will have tiny spacing and text.
  • It solves a maths problem tools already solve: preprocessors, CSS custom properties and editor plugins can convert px to rem for you.

A cleaner approach is to leave the root alone and use a small conversion mindset: 16px is 1rem, 14px is 0.875rem, 18px is 1.125rem, 24px is 1.5rem. If you use Sass, a function keeps it readable:

@use "sass:math";

@function rem($px, $base: 16) {
  @return math.div($px, $base) * 1rem;
}

h3 {
  font-size: rem(22); // 1.375rem
}

Units in Media Queries

Media queries are a special case. Inside a media query, both em and rem are based on the browser's initial font size, not on anything you set in your CSS. That means 48em in a media query is 768px for a user with the default 16px setting, and 960px for a user with a 20px setting.

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

This is useful. If a user has increased their default font size, the layout switches to the narrow version earlier, which gives their larger text more room. With a px breakpoint, the two-column layout would kick in at the same width regardless, squeezing larger text into narrow columns.

Historically, em-based media queries also behaved more consistently across browsers than rem-based ones, so em remains the common recommendation for breakpoints.

A Practical Unit Strategy

Here's a set of defaults that works for most sites:

  • Root font size: leave it alone, or set it as a percentage. Never set it in px.
  • Body and heading font sizes: rem.
  • Component-internal spacing (padding, gaps, icon sizes): em, so it scales with the component's text.
  • Layout spacing between sections: rem, so it stays consistent across the page.
  • Line height: a unitless number such as 1.5, which multiplies the element's own font size and inherits cleanly.
  • Letter spacing: em.
  • Borders and hairlines: px, because a 1px border shouldn't grow to 1.25px.
  • Media queries: em.

In CSS, that looks like this:

body {
  font-size: 1rem;
  line-height: 1.6;
}

h1 { font-size: 2.5rem; line-height: 1.15; }
h2 { font-size: 1.75rem; line-height: 1.25; margin-top: 1.5em; }

.tag {
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 0.25em 0.6em;
  border: 1px solid currentColor;
}

@media (min-width: 64em) {
  body { font-size: 1.125rem; }
}

If you'd like headings to scale smoothly instead of jumping at breakpoints, rem also forms the base of fluid formulas; see how to build fluid typography with clamp().

Inspecting Computed Sizes

Whatever units you write, the browser converts them to px internally. To see the final value, open DevTools, select an element and look at the Computed tab. The font-size there is always shown in px. This is the quickest way to debug em compounding: if a nested element is much smaller than you expected, walk up its ancestors in the Computed tab and look for one that sets an em or percentage size.


FAQ: px, em and rem

For most text, yes. Rem respects the user's default font size setting and still scales with zoom, while px ignores the font size setting. Px is still fine for borders and other details that shouldn't grow with text.

By default, 1rem is 16px in all major browsers. If the user changes their default font size, or if your CSS sets the root font size as a percentage, 1rem changes with it.

Em values multiply through nested elements. If each level sets a size like 0.875em, each one is smaller than its parent. Switch to rem for font sizes that should be consistent regardless of nesting.

Use em when padding should scale with the element's own text, such as inside buttons, badges and form fields. Use rem when spacing should be consistent across the page, such as gaps between sections.

Not automatically, because browser zoom still enlarges px text. But it ignores the user's font size preference, which many people with low vision rely on, so rem is the more accessible choice.

In media queries both are based on the browser's initial font size, so they behave similarly. Em has historically been the more consistent of the two across browsers, and both are better than px because breakpoints then adapt to the user's font size setting.


Conclusion

Px, em and rem differ in what they're measured against. Px is a fixed CSS pixel, em is relative to the current element's text and compounds through nesting, and rem is relative to the root and stays consistent. Rem gives you predictable sizes that still honour the user's default font size, which makes it the best general choice for type.

Use rem for font sizes and page-level spacing, em for spacing and details inside components that should scale with their own text, unitless numbers for line height, and px only for borders and similar fine details. Leave the root font size alone, write breakpoints in em, and check the Computed tab when something looks off. Those few habits will keep your typography accessible and easy to maintain.

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