Command Palette

Search for a command to run...

Hectal
PHASE 3Beginner ~27 min· topic 2 of 7

Topic 3.2

Relative Units

In one line

Relative units measure against something else: em against the font size, rem against the root font size, % against a property-specific reference, and ch/ex against the font's own letters; choosing the right reference makes designs scale with the reader.

Think of it like this

Telling a friend "cut the cake into slices half as wide as your hand". The slice size depends on whose hand it is. A relative unit works the same way: 2em means "twice the current text size", so it gets bigger or smaller when the text does.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Relative unit
A unit whose real size depends on something else, like the text size or the parent's width.
em
One times the font size. On font-size it uses the parent's size; on other properties, the element's own size.
rem
"Root em": one times the font size of the <html> element, 16px by default.
Root element
The outermost element of the page, <html>. CSS can select it as html or :root.
Compounding
When sizes multiply as you nest elements, so 1.2em inside 1.2em becomes 1.44 times the start.
Containing block
The box a percentage is measured against, usually the content area of the parent element.
ch
The width of the character 0 in the current font. Handy for line lengths.
x-height (ex)
The height of lowercase letters like x in the current font.
Computed value
The final value the browser works out for a property, often turned into pixels.

Step by step

01rem: one reference for the whole page

Set sizes in rem and they all hang off one number, the root font size. If the reader sets their browser's default text to 20px, 1rem becomes 20px, 1.5rem becomes 30px, and the whole design grows in proportion.

That is why you should not set html { font-size: 16px; }: it would replace the reader's choice with a fixed number. Leave the root alone, or set it as a percentage like 100%.

style.csswhole filecss
h1 { font-size: 2rem; }      /* 32px by default */
p  { font-size: 1rem; }      /* 16px by default */
.small { font-size: 0.875rem; } /* 14px by default */
section { padding: 1.5rem; }    /* 24px by default */

02em: grows with its own component

Give a button padding: 0.5em 1em. A normal button with 16px text gets 8px by 16px padding. Make a large button with font-size: 1.5rem (24px) and its padding becomes 12px by 24px automatically, so it keeps the same shape.

button.csswhole filecss
.btn {
  font-size: 1rem;
  padding: 0.5em 1em;   /* scales with this button's own text */
  border-radius: 0.4em;
}
.btn.large { font-size: 1.5rem; } /* padding and radius follow */

03Watch em compound when you nest

em on font-size multiplies the parent's size. A list item at 1.2em inside another at 1.2em inside another ends up at 16 × 1.2 × 1.2 × 1.2 = 27.648px. With rem every level stays at 19.2px.

Watch em compound when you nestdiagram
Rendering diagram…

04% depends on the property

In a parent that is 400px wide and 200px tall, a child with width: 50% is 200px wide. height: 50% is 100px, because the parent's height is fixed. But padding-top: 10% is 40px: vertical padding and margin percentages are measured against the width, not the height. That surprise is behind the classic "padding-top: 56.25% for a 16:9 box" trick, which aspect-ratio replaces today (CSS Phase 9).

05ch and ex: measure with the font itself

max-width: 65ch limits a paragraph to about 65 characters per line, which is easy to read. Because ch comes from the font, the limit stays right when the font or size changes.

ex is rare, but useful for nudging small things relative to lowercase letters, like an icon's vertical position next to text.

article.csswhole filecss
article p { max-width: 65ch; }
input.zip { width: 7ch; font-family: monospace; }  /* fits 7 characters */

Try it yourself

  1. 1

    Predict the fourth level

    In the first example, add a fourth nested <li> inside each list (em level 4, rem level 4). Predict both sizes before running. (Hint: multiply 27.648 by 1.2.)

  2. 2

    Change the root

    In the second example, add html { font-size: 125%; } to the CSS. Predict the new numbers, then run. Everything grows by 1.25 because every size hangs off rem. This is what happens for a reader who sets a 20px default font.

  3. 3

    Give the parent an auto height

    In the third example, delete height: 200px; from .parent. Predict what height: 50% becomes now. Run it: the percentage has nothing fixed to measure against, so it behaves like auto and the console shows the child's natural height.

Code & diagrams

em compounds, rem does not New tab

Each em level multiplies by 1.2 again; each rem level stays at 1.2 × 16px.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

em level 1 19.2px
em level 2 23.04px
em level 3 27.648px
rem level 1 19.2px
rem level 2 19.2px
rem level 3 19.2px
Buttons that keep their shape with em padding New tab

Only font-size differs between the buttons. The padding follows each button's own text size.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

Normal: font 16px padding 8px 16px
Large: font 24px padding 12px 24px
Small: font 12px padding 6px 12px
What does 50% mean? It depends on the property New tab

padding-top: 10% is 10% of the parent's WIDTH (400px), not its height.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

width 50% = 200px
height 50% = 100px
padding-top 10% = 40px
Readable lines with ch New tab

Change the font-size of .measure to 1.5rem: the box gets wider too, because ch grows with the font.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Nested em font sizes run away

Give li { font-size: 1.2em; } and build a menu with three levels of nested lists.

terminal
$ DevTools → Computed → font-size
── what you'll see ──
The innermost items are 27.648px, much larger than the top level (19.2px), and the menu looks lopsided.

Break #2

height: 100% does nothing

Set .panel { height: 100%; } inside a <div> that has no height set.

terminal
$ DevTools → Elements → hover .panel
── what you'll see ──
The panel is only as tall as its text; the 100% has no visible effect.

Myth vs fact

Myth

1rem is always 16px.

Fact

It is 16px only while the root font size is the browser default and the reader hasn't changed it. A reader with a 20px default gets 1rem = 20px, which is the whole point.

Myth

em always means the parent's font size.

Fact

Only on the font-size property. On padding, margin, width and the rest, em uses the element's own computed font size.

Myth

padding-top: 10% is 10% of the parent's height.

Fact

Vertical padding and margin percentages use the containing block's width. Only height, top and bottom use its height.

Myth

Setting html { font-size: 62.5% } is required to use rem.

Fact

It is an old trick to make 1rem = 10px for easy maths. It still respects the reader's setting, but it forces you to set every font size again. Thinking in 16ths (0.875rem = 14px) works fine.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    Inside media queries, em and rem refer to the browser's initial font size (usually 16px), not to anything you set on html, because media queries are evaluated before your styles. Breakpoints in em (for example @media (min-width: 40em)) also respond to the reader's default text size in most browsers.

  • ▸

    line-height percentages and em values are computed to a pixel length on the element and that pixel value is inherited, which causes cramped lines in children with bigger text. A unitless line-height: 1.5 is inherited as the ratio instead, which is why it is the recommended form (CSS Phase 4).

  • ▸

    Newer font-relative units exist: lh (the element's line height) and rlh (the root's), cap (capital letter height), ic (width of the CJK water ideograph 水). They are useful for vertical rhythm and East Asian text; check support before relying on them.

  • ▸

    ch uses the advance width of 0; in proportional fonts most letters are narrower, so 65ch holds a few more than 65 average characters. If the font hasn't loaded yet, ch is measured with the fallback font and changes when the web font arrives, which can shift layout (see CSS Phase 4 — Web Fonts).

Remember this

  1. 1

    A relative unit is a unit whose size comes from something else: a font size, the parent element's width, or the size of a letter. The browser works out the real pixel size for every element. This final pixel number is the computed value (Topic 0.4 showed it in DevTools' Computed pane).

  2. 2

    **em** is relative to a font size. On the font-size property itself, 1em means the parent's font size, so font-size: 1.5em is "one and a half times my parent's text". On every other property (padding, margin, width, border-radius…), 1em means the element's own font size. Because each element's em font size builds on its parent's, nested elements compound: 1.2em inside 1.2em inside 1.2em is 1.728 times bigger.

  3. 3

    **rem (root em) is always relative to the font size of the root element**, <html>. Browsers default it to 16px, but readers can change that default in their browser settings. So 1rem is usually 16px, 1.5rem is 24px, and every rem grows when a reader asks for bigger text. rem never compounds, which makes it the safest default for font sizes and for spacing.

  4. 4

    **%** means "a percentage of some reference", and the reference depends on the property. For font-size, it is the parent's font size (like em). For width, and for padding and margin on all four sides, it is the width of the containing block (usually the parent's content box). For height, it is the containing block's height, but only if that height is fixed; otherwise height: 50% acts like auto. For transform: translate(), it is the element's own size.

  5. 5

    **ch** is the width of the digit 0 in the element's current font, and **ex is the font's x-height** (roughly the height of a lowercase x). They are tied to the actual font. max-width: 65ch keeps paragraphs to a comfortable reading line of about 60–75 characters. In a monospace (fixed-width) font, 1ch is exactly one character, so width: 20ch fits 20 characters.

  6. 6

    Which to choose: rem for font sizes and the page's spacing scale; em for things that should grow with their own component's text (button padding, icon size next to text, a heading's margin); % for fractions of the parent's width; ch for line length and inputs sized to their content; px for borders and hairlines. Topic 3.3 adds viewport units and Topic 3.7 mixes them all with clamp().

Explain it without notes

01

Explain the difference between em and rem, including what em refers to on font-size versus on padding.

02

Why are rem font sizes better for accessibility than px font sizes?

03

What does a percentage refer to for width, height, padding-top and font-size?

04

When would you choose em over rem on purpose?

Practice

01

Write a type scale in rem for h1 (40px), h2 (28px), p (16px) and .caption (13px) at the default root size.

02

Make an .icon span sit next to text and always be the same height as that text, whatever the text size.

03

Make a paragraph never wider than 60 characters and center it on the page.

Trade-offs

  • ↔

    rem is predictable and accessible but ignores the component's context; em adapts to context but compounds when nested.

  • ↔

    % widths adapt to the parent but depend on knowing what the containing block is; ch adapts to the font but changes when the font swaps.

Done when you can

  • I can convert a pixel size to rem at the default root size.

  • I can explain why nested em font sizes compound and how to avoid it.

  • I can say what % refers to for width, height, padding and font-size.

  • I can size a line of text with ch.

  • I can choose between px, rem, em, % and ch for a given job.