Published on

Mastering CSS Font Size: The Ultimate Guide to `em` vs. `rem`

Authors

'Mastering CSS Font Size: The Ultimate Guide to em vs. rem'

A comprehensive guide to CSS font-size, diving deep into the differences between absolute (px) and relative (em, rem) units to help you build more scalable, accessible, and maintainable websites.

Table of Contents

Mastering CSS Font Size: The Ultimate Guide to em vs. rem

Typography is the bedrock of web design. It’s more than just choosing a pretty font; it's about readability, hierarchy, and user experience. And at the heart of web typography lies a seemingly simple CSS property: font-size. But ask any seasoned developer, and they'll tell you that the choice of unit for this property—be it px, em, rem, or something else—is one of the most critical decisions you'll make for the long-term health of your stylesheet.

Choosing the wrong unit can lead to a cascade of problems: websites that don't scale, accessibility nightmares for users with visual impairments, and a maintenance headache that grows with every new component you build.

In this guide, we're going to demystify the world of CSS font-size units. We'll start with the basics of absolute vs. relative units, then take a deep dive into the two most powerful relative units in your toolkit: em and rem. By the end, you'll not only understand how they work but also have a clear, practical strategy for when and why to use each one.

The Foundation: Absolute vs. Relative Units

Before we can compare em and rem, we need to understand the two fundamental categories of units in CSS: absolute and relative.

Absolute Units: The Unchanging Ruler

Absolute length units are fixed and will appear as the same size regardless of the context. They are not relative to anything else in the document.

The most common absolute unit on the web is the pixel (px).

h1 {
  font-size: 32px;
}

p {
  font-size: 16px;
}

Pros of px:

  • Predictable: A pixel is a pixel (mostly). It provides pixel-perfect control, which can be appealing for designers who want their vision executed precisely.
  • Easy to Understand: The concept is straightforward for beginners.

Cons of px:

  • Accessibility Issues: This is the big one. If a user sets a larger default font size in their browser settings (e.g., due to a visual impairment), text set in px will not scale accordingly. This forces users to rely on full-page zoom, which can be a clunky experience. It disrespects the user's preferences.
  • Poor Scalability: When building a responsive design, using pixels for everything (font-size, padding, margin) makes it difficult to scale components up or down uniformly. You end up writing complex media queries to adjust dozens of pixel values.

While pixels have their place (we'll touch on that later), they are generally not the right choice for typography and layout spacing.

Relative Units: The Chameleon

Relative units, as the name suggests, get their value from something else. Their size is relative to another length property, which makes them incredibly flexible and powerful for building fluid, accessible designs.

Key relative units include:

  • % (percentage): Relative to the same property in the parent element.
  • em: Relative to the font-size of the parent element.
  • rem (root em): Relative to the font-size of the root (<html>) element.
  • vw / vh: Relative to 1% of the viewport's width or height.

For the rest of this guide, we'll focus on em and rem, the two workhorses of modern CSS typography.

The em Unit: The Parent's Child

The em unit is defined by the font-size of its nearest parent element. If the parent doesn't have an explicit font-size set, it inherits it from its parent, and so on, up the DOM tree.

Let's look at a simple example. By default, most browsers set the base font size to 16px.

<div class="parent">
  <p>This is some text.</p>
</div>
.parent {
  /* 1.25 * 16px (default) = 20px */
  font-size: 1.25em; 
}

.parent p {
  /* 0.8 * 20px (from .parent) = 16px */
  font-size: 0.8em;
}

In this case, the p tag's font size is relative to its parent, .parent. This makes em units excellent for creating components that should scale proportionally.

A key insight: The em unit is not just for font-size. When used for other properties like padding, margin, or width, it is still relative to the element's own calculated font-size.

.button {
  font-size: 18px;
  /* padding will be 0.5 * 18px = 9px */
  padding: 0.5em 1em; 
}

.button--large {
  font-size: 24px;
  /* padding automatically scales! 0.5 * 24px = 12px */
}

This is powerful! By changing just the font-size on .button--large, the padding scales perfectly along with it. This makes creating component variations a breeze.

The em Compounding Problem

This parent-relative nature, however, is also the em unit's greatest weakness. In deeply nested structures, em units can have a compounding effect that is difficult to predict and manage.

Consider a nested list for comments or navigation:

<article>
  <h2>Comments</h2>
  <ul>
    <li>Level 1 Comment
      <ul>
        <li>Level 2 Reply
          <ul>
            <li>Level 3 Reply - Oh no!</li>
          </ul>
        </li>
      </ul>
    </li>
  </ul>
</article>
article {
  font-size: 16px; 
}

/* Let's try to make nested list items smaller */
ul {
  /* Each nested ul will be 90% of its parent's font-size */
  font-size: 0.9em;
}

Let's calculate the font sizes:

  • article: 16px (our base)
  • Level 1 li: Inherits 16px from article.
  • Level 2 ul: 0.9em of its parent li's font-size = 0.9 * 16px = 14.4px.
  • Level 2 li: Inherits 14.4px.
  • Level 3 ul: 0.9em of its parent li's font-size = 0.9 * 14.4px = 12.96px.
  • Level 3 li: Inherits 12.96px.

The text gets progressively and uncontrollably smaller with each level of nesting. This compounding effect makes it nearly impossible to maintain a consistent typographic scale across a complex application.

The rem Unit: The Root's Rule

Enter the rem unit, which stands for "Root EM". It was created specifically to solve the compounding problem of ems.

The rem unit is not relative to the parent element. Instead, it is always relative to the font-size of the root element, which is the <html> tag.

html {
  /* This is the base for all rem units on the page */
  /* Most browsers default to 16px */
  font-size: 100%; /* or 16px */
}

h1 {
  /* 2 * 16px = 32px */
  font-size: 2rem;
}

p {
  /* 1 * 16px = 16px */
  font-size: 1rem;
}

.sidebar {
  /* 0.875 * 16px = 14px */
  font-size: 0.875rem;
}

Let's revisit our nested list example, but this time using rem:

<article>
  <ul>
    <li>Level 1 Comment</li>
    <li>Level 1 Comment
      <ul>
        <li>Level 2 Reply</li>
        <li>Level 2 Reply
          <ul>
            <li>Level 3 Reply</li>
          </ul>
        </li>
      </ul>
    </li>
  </ul>
</article>
html {
  font-size: 16px;
}

/* Set a consistent font-size for all list items, regardless of nesting */
li {
  font-size: 1rem; /* Always calculates to 16px */
}

/* Or maybe we want a specific size for a component */
.comment-thread li {
  font-size: 0.9rem; /* Always calculates to 14.4px, no compounding! */
}

With rem, the font-size of every element is predictable. It only depends on one value: the root font-size. This brings back the predictability of px while retaining the scalability and accessibility benefits of relative units. If a user changes their browser's default font size, the <html> element's font-size changes, and your entire rem-based layout and typography scale up or down perfectly.

The 62.5% Trick: A Modern Best Practice or an Anti-Pattern?

For years, a popular technique circulated in the development community to make rem calculations easier. It's known as the "62.5% trick."

The logic is this: The default browser font size is 16px. If we set the root font size to 62.5% of that, we get:

16px * 0.625 = 10px

By setting html { font-size: 62.5%; }, you make 1rem equal to 10px. This simplifies the math significantly:

  • 1.6rem = 16px
  • 2.4rem = 24px
  • 0.8rem = 8px

It seems like a clever solution. However, this is now widely considered an anti-pattern and a bad practice for accessibility.

Why? Imagine a user with low vision has gone into their browser settings and set their default font size to 20px to make text more readable. If your CSS contains html { font-size: 62.5%; }, you are overriding their explicit preference. You've now forced their base font size down to 20px * 0.625 = 12.5px, making the site less accessible for them, not more.

The Modern, Accessible Alternative:

Respect the user's settings. Leave the root font size at its default.

html {
  /* Let the browser and user control the base font size. */
  /* 100% is the default, so you don't even need this declaration. */
  font-size: 100%;
}

"But the math is harder!" you might say. True, dividing by 16 isn't as easy as dividing by 10. But we have tools to solve this:

  1. Use Comments: Simply document the pixel equivalent in your code.
    p {
      /* 18px / 16px = 1.125rem */
      font-size: 1.125rem; 
    }
    
  2. Use CSS calc(): Let the browser do the math.
    p {
      font-size: calc(18 / 16 * 1rem);
    }
    
  3. Use a Sass/Less Function: If you're using a preprocessor, this is the cleanest solution. Create a function to handle the conversion.
    // Sass (SCSS) function
    @function rem($pixels, $context: 16) {
      @return ($pixels / $context) * 1rem;
    }
    
    p {
      font-size: rem(18);
    }
    
    h1 {
      font-size: rem(48);
    }
    

These modern approaches give you the best of both worlds: developer convenience and, most importantly, a fully accessible and user-respecting experience.

Practical Strategy: A Hybrid Approach for When to Use What

So, the ultimate question is: which unit should you use? The best strategy isn't to pick one and use it for everything. It's to use each unit for what it does best.

Here is a practical, modern rulebook:

1. Use rem for Global Sizing and Spacing

Use rem for any property that should scale with the root font size, creating a globally consistent and accessible UI.

  • Typography: Set your base font-size on the body in rem, and define your entire typographic scale (h1, h2, p, etc.) using rem.
  • Layout Spacing: Use rem for margin and padding on major layout components like headers, footers, sections, and grids. This ensures that the whitespace in your layout scales with the text size.
  • Component Size: Define the width, height, max-width of major components in rem.
body {
  font-size: 1rem; /* 16px base */
  line-height: 1.5;
}

h1 {
  font-size: 3.052rem; /* ~49px */
  margin-bottom: 2rem;
}

.main-content {
  padding: 2rem;
}

2. Use em for Component-Internal Sizing

Use em for properties inside a component that should scale relative to that component's own font-size.

  • Button Padding: As shown earlier, padding in em allows a button to grow or shrink based on its font-size.
  • Icon Sizing: Size an icon within a text element using em so it always matches the surrounding text.
  • Element-Specific Line Height: If a specific heading needs a tighter line-height than the body, setting it in em (line-height: 1.2em;) makes it relative to the heading's own font-size.
.card {
  font-size: 0.9rem; /* Set the card's base font size */
  padding: 1.5em; /* This padding is relative to the card's 0.9rem font-size */
}

.card__title {
  font-size: 1.2em; /* This is relative to the card's font-size, NOT the root */
  margin-bottom: 0.5em; /* This margin is relative to the title's font-size */
}

In this card example, if we decide to make the entire card bigger (.card { font-size: 1.1rem; }), the padding and title size will scale up proportionally and automatically!

3. Use px for Values That Should Never Scale

Is there ever a place for px? Yes, for tiny, specific values that you want to remain constant regardless of user settings.

  • Borders: border: 1px solid black; is a perfect use case. You almost always want a border to be a crisp, single pixel line.
  • Box Shadows: The offset and blur values in box-shadow are often best defined in px for fine-tuned control.

Beyond em and rem: A Glimpse at Fluid Typography

Once you've mastered rem and em, you can explore even more advanced techniques. Modern CSS offers viewport units (vw, vh) and functions like clamp() that allow for truly fluid typography.

Fluid typography scales smoothly with the viewport size, not just in steps at media query breakpoints.

Here's a quick look at the clamp() function:

font-size: clamp(MINIMUM_VALUE, PREFERRED_VALUE, MAXIMUM_VALUE);

  • MINIMUM_VALUE: The smallest the font size can be.
  • PREFERRED_VALUE: A flexible value, usually involving a viewport unit like vw.
  • MAXIMUM_VALUE: The largest the font size can be.
h1 {
  /* 
    Min size: 2rem (~32px) 
    Scalable size: 4vw (4% of viewport width)
    Max size: 4rem (~64px)
  */
  font-size: clamp(2rem, 1rem + 4vw, 4rem);
}

This h1 will have a base size of 2rem on small screens. As the screen gets wider, it will grow proportionally with the viewport width (1rem + 4vw). It will stop growing once it reaches 4rem. This creates a perfectly fluid heading without complex media queries. Notice how it combines a rem unit (for accessibility) with a vw unit (for fluidity).

Conclusion: Choose Your Units with Purpose

The journey from px to em and rem is a journey toward becoming a more thoughtful, professional front-end developer. It's about moving from a rigid, fixed-width mindset to a flexible, user-centric one.

Let's recap the strategy:

  • rem is your default: Use it for typography, global spacing, and most layout dimensions. It provides consistency and respects user accessibility settings.
  • em is for component-specific scaling: Use it for properties like padding or margin inside a component that should be relative to the component's font-size, not the global one.
  • px is for fixed details: Use it sparingly for things like borders and shadows that should never scale.

By mastering these units and applying them with purpose, you'll build websites that are not only beautiful and responsive but also more accessible and far easier to maintain in the long run. You're not just styling a website; you're crafting a resilient and adaptable user experience.