Font-relative · Chapter 2 of 7 · 12 units

Font-relative Units

Set sizes as ratios, and the page keeps its shape when the reader changes the rules.

em and rem are the two units through which CSS shows its true nature: a system of coordinates and a system of inheritance. In px, a size is a number. In em and rem, a size becomes a ratio — to the local typography, or to the document’s root.

The short version:

em  = multiply by the element's font-size
rem = multiply by the root element's font-size

But the most important nuance is this:

em on font-size resolves against the INHERITED font-size;
em on other properties resolves against the COMPUTED font-size of the element itself.

This is one of the most important facts in the whole article. It explains both the power of em and the famous cascade trap.


em

Formula
1em = element's computed font-size (or inherited, when set on font-size itself)
Relative to
The element's own font-size
Support
Universal

em is a local font-relative unit. It measures length in terms of the element’s font-size.

If an element has computed font-size: 20px, then 1em = 20px, 2em = 40px, and 0.5em = 10px.

The mental model:

em = multiplier of the current font-size

Historically em came from typography: in print, an em was the size of the current type body. On the web it became a very practical idea: don’t pin a size to absolute pixels; say how much bigger or smaller it should be than the surrounding text.

For most properties:

.button {
  font-size: 20px;
  padding: 0.5em 1em;
}

resolves as:

font-size      = 20px
padding-block  = 0.5em = 10px
padding-inline = 1em   = 20px

On the font-size property itself, the reference is necessarily different, and this falls out of how computed values work, not from a special rule baked into em:

.child {
  font-size: 1.5em;
}

Here 1.5em can’t be computed from the element’s own font-size, which is the very value being computed. So it resolves against the inherited font-size, i.e. the parent’s computed value:

child font-size = parent computed font-size × 1.5

If the parent is 16px, the child becomes 24px.

The cascade trap

The trouble with em shows up when the same multiplier repeats through nesting:

.item { font-size: 1.5em; }
<div class="item">
  Level 1
  <div class="item">
    Level 2
    <div class="item">
      Level 3
    </div>
  </div>
</div>

With a base of 16px:

Level 1: 16 × 1.5  = 24px
Level 2: 24 × 1.5  = 36px
Level 3: 36 × 1.5  = 54px

At five levels deep: 1.5⁵ = 7.59×, and 16 × 7.59 ≈ 121.5px.

That’s the cascade trap: a small multiplier looks innocent, but inheritance turns it into an exponent.

Demo — em cascade: small multipliers, exponential growth
Each nested level: font-size1.5em
Base: 16px
Level 1 · 24.00px
Level 2 · 36.00px
Level 3 · 54.00px
Level 4 · 81.00px
Level 5 · 121.50px
Level 5 scale1.5⁵ = 7.59× base

A small multiplier looks harmless. But because em onfont-size compounds through inheritance, five levels become exponential — 1.5⁵ ≈ 7.6×.

em itself isn’t the problem. The trap only triggers when em is used for font-size on components that nest.

em shines inside a component, where sizes should follow the local type. The canonical example is a button:

.button { padding: 0.625em 1em; border-radius: 0.5em; }

Padding and border-radius are tied to the button’s text: set the button’s font-size once, and everything inside it follows. Bigger text → bigger internal whitespace. The component scales as a single typographic body. What that one font-size should itself be written in is a separate question, and em vs rem answers it.

Or an inline icon next to text:

.label     { display: inline-flex; align-items: center; gap: 0.5em; }
.label svg { width: 1em; height: 1em; }

The icon scales with the text instead of staying fixed at 16px, which is useful when the component ships in several sizes. (A fixed px icon is fine too, especially for sprite assets tuned to an exact pixel size.)

Use when
  • Padding/gap/border-radius you want to scale with the component's own text
  • Inline icons that should match the text size: width: 1em; height: 1em
  • Any local component where one font-size should drive the internal scale
Avoid when
  • Global type scale on nestable components — em compounds through nesting
  • Sizes that must stay stable regardless of where the component is placed
  • Text that mustn't shrink because of a deep parent — rem is safer (note: portaled tooltips usually escape this anyway)

Pitfall: line-height: 1.5em vs line-height: 1.5

For line-height, a unitless number is almost always better than a length:

body { line-height: 1.5; }   /* good */
body { line-height: 1.5em; } /* avoid */

The difference is inheritance. With line-height: 1.5, the number is inherited. Every element multiplies that number by its own font-size:

body { font-size: 16px; line-height: 1.5; }
h1   { font-size: 32px; }
body line-height = 16 × 1.5 = 24px
h1   line-height = 32 × 1.5 = 48px

With line-height: 1.5em, the computed value becomes a length and is inherited as a fixed length:

body { font-size: 16px; line-height: 1.5em; /* computed: 24px */ }
h1   { font-size: 32px; }

Now h1 inherits a 24px line-height, which is too tight for 32px text.


rem

Formula
1rem = root element's computed font-size
Relative to
The root element (typically <html>)
Support
Universal

rem stands for “root em.” It’s a font-relative unit that always resolves against the font-size of the root element (typically <html>).rem is young: browsers only picked it up around 2010. Before that, keeping nested em arithmetic in your head was simply part of the job.

The mental model:

rem = multiplier of the root font-size

If the root is 16px, then 1rem = 16px, 2rem = 32px, 0.75rem = 12px. If the user (or the site) sets the root to 20px, then 1rem = 20px, 2rem = 40px, 0.75rem = 15px.

An important accessibility fact: the typical browser default is 16px, but users can change it in browser settings. So while 1rem is often 16px, you shouldn’t treat that as guaranteed. A better phrasing:

1rem = the root font-size
the root font-size is often 16px by default
but a user may have made it larger or smaller

Why rem became the standard

rem solves the main pain of em: compounding.

.item { font-size: 1.5rem; }
<div class="item">
  Level 1
  <div class="item">
    Level 2
    <div class="item">
      Level 3
    </div>
  </div>
</div>

With a root of 16px, every level is 24px. No 24 → 36 → 54; nesting stops mattering.

That’s why rem became the obvious base for design systems:

:root {
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-6: 1.5rem;
  --space-8: 2rem;
}

This scale is stable anywhere in the DOM, scales with the root font-size, respects user preferences better than px, and is easier to read than nested em.

Demo — rem responds to root, px does not
Simulated root font-size16px
2rem heading
Heading
32px
1rem body
Body text
16px
0.75rem small
small caption
12px
16px fixed
Fixed in px
16px

Move the slider — the three rem-based elements scale together. The 16px-fixed element doesn't budge. That's why remrespects the user's root font-size; px doesn't.

Use when
  • font-size for body text and headings
  • Global spacing scales and layout gaps
  • Margins between sections, max-width on text containers
  • Component sizes that must be stable across contexts
  • Design tokens: --space-*, --text-*, --radius-*
Avoid when
  • Internal component proportions that should follow the component's own font-size
  • Anywhere you'd want padding to grow with a button's text — use em instead

Pitfall: rem is not “px, but nicer”

A common mistake is to read rem as if it were px:

.title {
  font-size: 2rem; /* I'm thinking "32px" */
}

The correct mental model is:

2rem = 2 × root font-size

If the user changed their default font-size, 2rem changes too. That’s not a bug — that’s the point of rem.

A related smell is hardcoding the root in px:

html { font-size: 16px; }

This makes 1rem predictable for the designer but undermines the user’s browser default. Prefer one of:

html { font-size: 100%; }

or simply don’t set a root font-size unless you have a specific reason.

The root of this site, measured

This guide has a specific reason, so it’s fair to hold it to its own standard. Its root is fluid:

html { font-size: clamp(1rem, 0.9rem + 0.5vw, 1.25rem); }

One declaration scales the whole system with the window. The reading measure, the vertical rhythm, the header and every demo are sized in rem, so all of them follow, with no breakpoints involved. (The rail on this page shows what 1rem currently resolves to on your screen.)

What that preserves is the thing the advice above is really about. Every term is in rem, and the floor is exactly 1rem, so the page never renders smaller than the reader’s own default, and it grows when they raise it:

Browser default This site’s root
16px 20px
20px 25px
24px 28.8px

A px root would have pinned all three rows to one number.

The cost shows up under page zoom. Zoom scales rem but not vw, because magnifying the page shrinks the layout viewport by the same factor, so the 0.5vw term contributes nothing to growth. Measured on a 1440px window with a 16px default:

Page zoom Root, CSS px Rendered Growth
100% 20 20 100%
200% 18 36 180%
225% 17.6 39.6 198%
250% 17.3 43.2 216%

At 200% zoom the text is 180% of its original size, and a reader who wants it twice as large gets there at about 225%. Whether that trade is acceptable is a judgment call. That it exists is not.

The mechanism is the clamp() branch rather than the units. At 1440px the preferred term computes to 21.6px, above the ceiling, so the rendered value is a flat 1.25rem, which would double perfectly on its own. Zoom knocks the value off that ceiling into the fluid band, where the vw term dilutes the growth. The clamp() playground shows the same effect on a headline.

The generalization is worth having, because the obvious fix isn’t one:

/* Looks safer. Isn't. */
html { font-size: 100%; }
@media (min-width: 60rem) { html { font-size: 125%; } }

At 200% zoom the layout viewport drops below the breakpoint, the rule stops matching, and the root falls from 20px to 16px: 160% growth, with a visible jump on the way. Any root that depends on the viewport pays this surcharge; only a root that ignores the viewport doubles exactly.

Which leaves a choice rather than a rule. If you want the system to breathe with the window, accept that zoom grows text by less than its nominal factor, and measure where that lands. If you want zoom to be exact, keep the root viewport-independent and make individual elements fluid instead.


em vs rem — when to use which

The core distinction:

em  = local scale
rem = global scale

em answers: how big should this be relative to the text right here? rem answers: how big should this be relative to the page’s base size?

Demo — em compounds, rem stays locked to root
Outer container font-size16px
font-size: 1.25em
Outer · 16px
Nested · 20px
Nested · 25px
Nested · 31px
font-size: 1.25rem
Outer · 16px
Nested · 20px
Nested · 20px
Nested · 20px

Slide the outer container's font-size. The em column grows with the parent and compounds through nesting. The rem column stays locked to the page root — every level renders at the same size.

A practical table:

Task Usually better Why
Body text size rem Respects root / user preference; immune to nesting
Headings rem or clamp(...rem...) You want a shared typographic scale
Button padding em Should grow with the button’s own text
Gap between icon and text em Icon and text live at one local scale
Inline SVG icon size em width: 1em matches the text
Page layout spacing rem Spacing should be stable across the site
Margin between sections rem This is the page’s vertical rhythm
Component sized via font-size em inside One font-size controls everything internal
Tooltip / popover text rem Must not shrink because of a deep parent
Nested comments / tree UI rem for text Avoid compounding in deep nesting
line-height unitless Inherits as a multiplier, not a fixed length
Media query breakpoints em or rem Breakpoints can be tied to the user’s text scale

The best CSS often uses both at once:

.button         { font-size: 1rem;     padding: 0.625em 1em; border-radius: 0.5em; }
.button--small  { font-size: 0.875rem; }
.button--large  { font-size: 1.25rem;  }
rem sets the component's size in the page's system;
em sets the component's internal proportions.

“em vs rem” is often posed as an either/or. In practice the best answer is both, each doing its own job.


The 62.5% trick

A pattern you’ll see in older codebases and tutorials:Popularized around 2004 for em sizing, back when Internet Explorer could resize only text, never pages. A surprising share of ’00s font-size lore is archaeology from that one limitation.

html { font-size: 62.5%; }

If the browser default is 16px, then 16 × 0.625 = 10px, which makes rem convenient for mental arithmetic:

1rem    = 10px
1.4rem  = 14px
1.6rem  = 16px
2.4rem  = 24px

It’s not a technical necessity. It’s a hack for math.

An honest assessment

html { font-size: 62.5%; } does not automatically break user preferences.

Why: 62.5% is a percentage of the user’s default. If the user raised their default from 16px to 20px, the root becomes 12.5px, not 10px. If everything downstream is in rem, the system continues to scale:

body { font-size: 1.6rem; }

At default 16px: root 10px, body 16px. At default 20px: root 12.5px, body 20px.

User preferences still flow through.

Where it breaks

The problem starts when 62.5% gets mixed with hardcoded px:

html { font-size: 62.5%; }
body { font-size: 16px; }

Now body is locked at 16px. The user’s preference is silently ignored for body text. Worse, when half the components use rem and half use px, the interface stops being consistent: some text scales, some doesn’t.

Use when
  • Use only if the team commits to rem for all sizes that should scale
  • Document explicitly: 1.6rem ≈ 16px at default 16px
  • Test the site with the browser default font-size changed
  • Make the rule clear: 1rem here is not the browser default
Avoid when
  • If the team reads rem as px and mixes px alongside it
  • If you depend on an external design system that assumes 1rem = browser default
  • If component libraries expect the standard rem scale — some may render unexpectedly small

Verdict

The 62.5% trick is acceptable, but no longer especially useful. Modern CSS has calc(), design tokens, editor hints, and well-established rem scales. The cost is a team-wide convention everyone has to remember.

A safer neutral default:

html { font-size: 100%; }
body { font-size: 1rem; line-height: 1.5; }

If your project does use 62.5%, make sure the team understands:

This is not a way to make rem "right."
It's a way to make px-to-rem conversion easier.

Accessibility and user font preferences

Users really do change the browser’s default font-size. It’s not a theoretical possibility.

Browsers offer font-size settings, minimum font size, page zoom, text-only zoom (in some environments), system display-scaling, and accessibility-specific overrides. They don’t all work the same way, but the principle is shared: users may need text larger than the designer planned.

Browser default font-size

The typical default is 16px, but it’s not a law of nature.

default root font-size is often 16px
but the user may change it in browser settings

If your CSS says:

body { font-size: 1rem; }

then body follows the root.

If your CSS says:

body { font-size: 16px; }

then body is locked to 16px as a CSS length. Page zoom will still scale the page visually, but the user’s default-font-size setting will not change that pinned text.

What about zoom?

It’s important to distinguish:

browser default font-size — changes the base text size
page zoom              — scales the entire page
text-only zoom         — scales text without changing layout (in some browsers)
minimum font size      — clamps text below a threshold

WCAG 1.4.4 requires that text can be resized up to 200% without losing content or functionality. It’s not about only using rem — it’s about the result: the user enlarges text, and the interface doesn’t clip, overlap, or break.

Why font-size: 16px can still be a problem

The claim that fixed px font-sizes “break user preferences” needs nuance.

font-size: 16px does not break browser zoom: if the user zooms to 200%, the page scales visually.

But font-size: 16px does ignore the user’s default-font-size setting. If someone set their default to 20px because they have trouble reading at smaller sizes, your author-specified 16px stays 16px.

.a { font-size: 16px; }
.b { font-size: 1rem; }

At user default 16px: both are 16px. At user default 20px: .a is still 16px, .b is 20px.

That’s why rem is the better default for text.

How many users change font-size?

There’s no single honest percentage for the whole web; it depends on audience, device, browser, age, vision, and context. Some useful reference points:

  • Evan Minto’s Internet Archive analysis (via Nicolas Hoizey’s write-up): ~3.08% of users had a changed root font-size.
  • WebAIM Survey of Users with Low Vision #2, among low-vision respondents:
    • 8% had increased text size above the default. That figure is script-detected, so it counts style and browser-setting changes only, not zoom or magnification.
    • 36.7% said they often use browser text-sizing controls.
    • 44.0% often use browser zoom.
    • 48.4% use screen magnification or system settings.
Even if it's "only a few percent," it isn't an edge case.
For a large product, 2–3% of users is a huge audience.
For accessibility, it's exactly the audience you must not cut off.

How to test

A minimal manual test:

  1. Open the page in a browser.
  2. Increase the browser default font-size (e.g. from 16px to 20px or 24px).
  3. Reload.
  4. Check whether body text, controls, labels, cards, and modals all grew.
  5. Look for clipping, overlap, hidden buttons, horizontal scroll.
  6. Repeat at page zoom 200%.

What should be true:

Text is bigger.
Containers grew with it.
Buttons didn't clip their labels.
Modals didn't lose their action buttons.
Navigation didn't cover content.

You can simulate it in DevTools in one line. Temporarily set:

html { font-size: 125%; }

or:

html { font-size: 150%; }

This doesn’t replace real browser-setting testing, but it instantly reveals which parts of the UI are written in rem/em versus pinned to px.


Beyond em and rem — the font-metric family

em and rem resolve against the font’s size. CSS has five more font-relative units that resolve against finer properties of the font itself:

ch  = advance width of the "0" glyph
ex  = x-height (height of lowercase x)
cap = cap height (height of capital letters)
ic  = advance width of the "水" CJK ideograph
lh  = computed line-height as a length

Each also has a root variantrch, rex, rcap, ric, rlh — measured against the root element’s font rather than the local one.

These units are quieter than em and rem. Most CSS doesn’t need them. But when you need to align an icon to cap-height, limit a column to “about 65 characters,” or size something to exactly one line of text — nothing else does the job.

Demo — Font metrics, rendered with the units that describe them
Font
Hxyg012
1em
font-size, stable across fonts
1lh
line-height as a length
1cap
cap-height (top of capital letters)
1ex
x-height (top of lowercase x)
1ch
advance width of the "0" glyph

Every bar above is sized in the unit that names it (e.g. height: 1cap). Switch fonts to see how cap-height, x-height, and especially 1ch shift — that's what makes these units font-relative rather than universal.


ch

Formula
1ch = advance width of the '0' glyph in the current font
Relative to
The element's own font
Support
Universal

ch is the advance measure, or width, of the 0 (digit zero) glyph in the current font.

The mental model:

1ch ≈ the width of one character

But “approximately” is doing real work here. ch doesn’t compute an average glyph width. It looks at one specific glyph: U+0030 DIGIT ZERO. In a monospaced font, 1ch is close to the width of every character. In a proportional font, 0, i, m, the space character, and Cyrillic letters all have different advance widths.

If the browser can’t determine the 0 glyph’s advance, the spec says to assume 0.5em wide by 1em tall. In practice 1ch falls back to 0.5em in normal horizontal text, and to 1em only when the text is typeset upright — a vertical writing mode (vertical-rl/vertical-lr) with text-orientation: upright.

The canonical use:

.article {
  max-width: 65ch;
}

This is one of the more elegant patterns in CSS: instead of max-width: 720px, we ask for “roughly 65 characters per line.” For prose, a range of ~45–75 characters is the classic typographic guideline, with ~66 often cited as a comfortable target.

A more defensive variant:

.article {
  inline-size: min(100%, 65ch);
}

Or with fluid scaling:

.article {
  max-inline-size: clamp(40ch, 70vw, 70ch);
}
Demo — Same max-width: 65ch, three fonts
max-width65ch
System sans-serif

Typography on the web is a negotiation between author intent, the user's reading environment, and the constraints of the medium. Line length matters.

Monospace

Typography on the web is a negotiation between author intent, the user's reading environment, and the constraints of the medium. Line length matters.

Georgia (serif)

Typography on the web is a negotiation between author intent, the user's reading environment, and the constraints of the medium. Line length matters.

The same 65ch renders at different visual widths because1ch equals the width of the 0 glyph in the current font. Monospaced fonts produce the widest result; many serifs end up the narrowest.

Use when
  • Reading column widths: max-inline-size: 65ch
  • Input fields with an expected character count
  • Code fields and monospaced inputs
  • OTP / verification fields: inline-size: 6ch
  • Tables with character-based values; terminal-style interfaces
Avoid when
  • General-purpose spacing — 2ch ties the size to the font's 0 width, rarely what you mean
  • When pixel-exact width must be identical across fonts
  • As a stand-in for character count — different fonts give different widths

ex

Formula
1ex = x-height of the current font
Relative to
The element's own font
Support
Universal

ex is the x-height of the current font: the height of the lowercase letter x, i.e. the height of lowercase letters without ascenders or descenders.

X-height has a big effect on perceived size. Two fonts at the same font-size: 16px can look very different, because one has a taller x-height.

A common approximation:

1ex ≈ 0.5em

But it’s only an approximation. A typeface with a large x-height (modern sans-serifs like Inter) gives a bigger 1ex than a small-x-height face (some classical serifs). That’s the whole point of ex — it reflects a real typographic property, not an arbitrary half-em.

Use when
  • Aligning a small inline icon to the lowercase body of the text
  • Decorative markers that should sit at lowercase height
  • Thin elements that should match x-height (underlines, highlights)
  • Typographic experiments where lowercase metrics matter
Avoid when
  • Icons that should match the full line — use 1lh
  • Icons that should match capital letters — use 1cap
  • Layout spacing — ex varies unpredictably between fonts; em/rem are steadier

cap

Formula
1cap = cap height of the current font
Relative to
The element's own font
Support
Modern browsersall engines since Dec 2023

cap is cap-height: the height of capital letters in the current font. Where ex looks at lowercase, cap looks at uppercase.

It’s useful because icons placed near a label often need to match the height of capital letters, not the full 1em. Capitals fill only part of the em square (roughly 0.7em in most fonts), which also reserves room for descenders and diacritics.

cap is typically smaller than 1em. (When a font doesn’t expose a cap-height metric, the spec falls back to the font’s ascent.) The classic use is icons in headings:

.heading-with-icon {
  display: inline-flex;
  align-items: baseline;
  gap: 0.25em;
}

.heading-with-icon svg {
  inline-size: 1cap;
  block-size: 1cap;
}

If the heading font changes, the icon adapts to the new cap-height automatically.

Use when
  • Icons placed next to a heading or uppercase label
  • Icons inside button labels (when buttons use uppercase or title-case)
  • Badges aligned with capitalized text
  • Decorative typographic bars sized to cap-height
Avoid when
  • Body-text alignment — cap matches capitals, so it mismatches lowercase-dominant text (ex/lh fit better)
  • Icons that should just be "roughly text-sized" — 1em needs no font-metric support
  • Layouts targeting older browsers without an @supports fallback

ic

Formula
1ic = advance width of the '水' (water) CJK ideograph
Relative to
The element's own font
Support
Modern browsersall engines since Sep 2022

ic is a font-relative unit built for CJK typography. It equals the advance measure of , the CJK water ideograph (U+6C34).水 is the test glyph because ideographs are square by design: one 水 is one cell of the grid. A paragraph of CJK text is, typographically, graph paper.

1ic ≈ the width of one full-width CJK character

For Latin-script interfaces this is exotic. For Chinese, Japanese, and Korean text, it’s a much more meaningful unit than ch (which is anchored to a Latin digit).

.cjk-article {
  max-inline-size: 32ic;
  line-height: 1.7;
}

Reads as: “limit the column to about 32 CJK characters.”

Use when
  • Column widths in CJK layouts
  • Input fields sized for an expected number of CJK characters
  • Vertical writing-mode CJK typography
  • Where ch would give a Latin proxy but you need a CJK proxy
Avoid when
  • Latin or Cyrillic interfaces — 1ic silently falls back to another font
  • Any context without CJK characters

lh

Formula
1lh = the element's computed line-height as a length
Relative to
The element's own line-height
Support
Modern browsersall engines since Nov 2023

lh resolves to the computed line-height of the element, expressed as a length.

1lh = the height of one line

If font-size: 16px and line-height: 1.5, then 1lh = 24px. If font-size: 20px and line-height: 1.4, then 1lh = 28px. Critically, lh depends on both font-size and line-height, not just the type size.

The killer use case is icons inside a single line of text:

.label svg {
  inline-size: 1lh;
  block-size: 1lh;
}

When line-height changes, the icon follows. Strictly, 1lh is the computed line-height — the size of an ideal empty line — so a line box whose content is taller can still exceed it.

Demo — Icon sized at 1lh × 1lh
font-size18px
line-height1.5

Read more about typography on the web

The icon scales with the text's line height automatically

Icon size 27px≡ font-size × line-height 18 × 1.5 = 27

The icon's width and height are both 1lh. As the line-height grows, the icon grows with it — without needing JavaScript or media queries.

Use when
  • Inline icons that should fill a line: width: 1lh; height: 1lh
  • Line-height-aware controls and indicators
  • Skeleton placeholders sized to one line
  • Vertical rhythm where measurements should be multiples of a line
  • padding-block: 0.5lh for line-aware spacing
Avoid when
  • When the element should match the type size only — use 1em
  • When it should match capital letters — use 1cap
  • As a replacement for unitless line-height — they're different things

lh vs unitless line-height

It’s easy to confuse these two:

line-height: 1.5;  /* unitless multiplier — for the line-height property */
height: 1lh;       /* length — for any other property */

line-height: 1.5 inherits as a number; descendants multiply by their own font-size. 1lh is the resolved length once computed, usable as a length in other properties. They’re complements, not alternatives.

The well-formed pattern:

.text { line-height: 1.5; }
.text .icon { block-size: 1lh; }

Body text uses the multiplier (so nested elements get proportional line-heights). The icon uses the resolved length (so it fits one line of the parent).


Root variants — rch, rex, rcap, ric, rlh

Formula
r* = the same metric, measured on the root element's font
Relative to
The root element (typically <html>)
Support
Modern browsersall engines since Jan 2026

Each font-metric unit has a root variant. They follow the same idea as rem:

rch  = ch of the root font
rex  = ex of the root font
rcap = cap of the root font
ric  = ic of the root font
rlh  = computed line-height of the root element

Where ch, ex, cap, ic, lh are local — they shift if the element changes font-family or font-size — the root variants stay locked to whatever <html> is using.

html {
  font-family: Inter, sans-serif;
  font-size: 16px;
  line-height: 1.5;
}

.card {
  font-family: Georgia, serif;
  max-inline-size: 65rch;
}

65rch is measured against Inter’s 0, not Georgia’s, even inside a Georgia component.

Use when
  • A global text measure that ignores any local font changes
  • Vertical rhythm via rlh: margin-block: 2rlh
  • Layout tokens tied specifically to root font metrics
  • Documentation / editor interfaces where the root defines the typographic grid
Avoid when
  • Internal component proportions that should follow the component's own font
  • Default spacing scale — a plain rem multiple is enough; the root metric adds no benefit here
  • Iconography inside a heading — use the local 1cap so it adapts to that heading's font

In practice, rem handles 90% of “I want something tied to the root” cases. Root metric units are reserved for the times when you specifically need that root metric — root character width, root cap-height, root line-height — rather than just a multiple of root font-size.

The support story finally closed

This family took the longest of any unit group in this guide to become usable everywhere. rlh had all three engines by November 2023, but rch, rex, rcap, and ric sat in a two-engine limbo for years: Chromium shipped them in 2023 (Chrome 111, then 118 for rcap), Safari in 17.2 (December 2023), and Firefox only in version 147, January 2026.Which makes these the newest CSS units in this guide — younger than container queries, and by some margin the last to arrive.

So the practical guidance flipped recently:

before 2026: rch/rex/rcap/ric needed an @supports fallback
now:         all three engines ship them — only older installs need one

If you support browsers released before 2026, keep a fallback in rem:

.measure { max-inline-size: 34rem; }          /* fallback */
@supports (width: 1rch) {
  .measure { max-inline-size: 70rch; }        /* the metric you actually meant */
}

Support checked against MDN browser-compat data ·


Compact reference

Unit Formula Best for Watch out
em element’s computed font-size (inherited, when set on font-size) component-internal proportions compounds through nesting
rem root element’s font-size type scale, spacing tokens it’s not a fancy px alias
ch advance of 0 in the current font max-inline-size: 65ch font-dependent; ≠ exactly N characters
ex x-height of the current font lowercase-aware alignment varies strongly between fonts
cap cap height of the current font icons next to headings all engines only since Dec 2023
ic advance of in the current font CJK layouts rarely useful in Latin / Cyrillic UI
lh computed line-height, as a length icons sized to one line not a substitute for unitless line-height
rch rex rcap ric rlh same metrics, on the root system measures that ignore local fonts rem is simpler in 90% of cases

Two rules worth keeping next to this table: for line-height, prefer the unitless form (1.5) so it inherits as a multiplier, not a frozen length. And the 62.5% trick is acceptable only if every size downstream uses rem: mixing px alongside doesn’t change what 1rem means, but it does leave half the interface scaling with the reader’s settings and half of it ignoring them.


Sources