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
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.
font-size1.5em16px24.00px36.00px54.00px81.00px121.50px1.5⁵ = 7.59× baseA 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.)
- 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-sizeshould drive the internal scale
- Global type scale on nestable components —
emcompounds through nesting - Sizes that must stay stable regardless of where the component is placed
- Text that mustn't shrink because of a deep parent —
remis 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
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.
font-size16px2rem heading32px1rem body16px0.75rem small12px16px fixed16pxMove 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.
font-sizefor 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-*
- Internal component proportions that should follow the component's own font-size
- Anywhere you'd want
paddingto grow with a button's text — useeminstead
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?
font-size16pxfont-size: 1.25em16px20px25px31pxfont-size: 1.25rem16px20px20px20pxSlide 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 only if the team commits to
remfor 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:
1remhere is not the browser default
- If the team reads
remaspxand mixespxalongside 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:
- Open the page in a browser.
- Increase the browser default font-size (e.g. from
16pxto20pxor24px). - Reload.
- Check whether body text, controls, labels, cards, and modals all grew.
- Look for clipping, overlap, hidden buttons, horizontal scroll.
- 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 variant — rch, 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.
1emfont-size, stable across fonts1lhline-height as a length1capcap-height (top of capital letters)1exx-height (top of lowercase x)1chadvance width of the "0" glyphEvery 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
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);
}
max-width: 65ch, three fontsmax-width65chTypography on the web is a negotiation between author intent, the user's reading environment, and the constraints of the medium. Line length matters.
Typography on the web is a negotiation between author intent, the user's reading environment, and the constraints of the medium. Line length matters.
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.
- 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
- General-purpose spacing —
2chties the size to the font's0width, 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
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.
- 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
- Icons that should match the full line — use
1lh - Icons that should match capital letters — use
1cap - Layout spacing —
exvaries unpredictably between fonts;em/remare steadier
cap
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.
- 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
- Body-text alignment —
capmatches capitals, so it mismatches lowercase-dominant text (ex/lhfit better) - Icons that should just be "roughly text-sized" —
1emneeds no font-metric support - Layouts targeting older browsers without an
@supportsfallback
ic
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.”
- Column widths in CJK layouts
- Input fields sized for an expected number of CJK characters
- Vertical writing-mode CJK typography
- Where
chwould give a Latin proxy but you need a CJK proxy
- Latin or Cyrillic interfaces —
1icsilently falls back to another font - Any context without CJK characters
lh
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.
1lh × 1lhfont-size18pxline-height1.5Read more about typography on the web
The icon scales with the text's line height automatically
27px≡ font-size × line-height 18 × 1.5 = 27The icon's width and height are both 1lh. As the line-height grows, the icon grows with it — without needing JavaScript or media queries.
- 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.5lhfor line-aware spacing
- 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
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.
- 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
- Internal component proportions that should follow the component's own font
- Default spacing scale — a plain
remmultiple is enough; the root metric adds no benefit here - Iconography inside a heading — use the local
1capso 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.