Design

px vs rem vs em: Which One to Use, and What Each One Breaks

They all resolve to the same kind of length. The difference is who controls the number they get multiplied by.

Use rem for font sizes, spacing and media queries. Use px for things that must not change size — a one-pixel border, a fixed icon, a hairline divider. Use em when a value should follow the font size of the element it sits on, like the padding inside a button. All three end up as the same thing, a length in CSS pixels; the only difference is what number they get multiplied by, and who controls that number.

UnitMultiplied byControlled by
pxNothing. 1px is 1px.You, and nobody else.
remThe computed font size of the html element.The reader's browser setting — unless you overwrite it.
emThe computed font size of the element itself. On the font-size property, the parent's.Wherever the element happens to sit in the cascade.

Is a CSS pixel a real pixel?

No. CSS pins one inch to exactly 96 px, which locks every absolute unit to every other one: 1pt is exactly four thirds of a pixel, 1cm is 96 divided by 2.54, about 37.8 px. Those ratios hold on every device. What they do not describe is anything physical. A display at twice the pixel density paints each CSS pixel with four hardware pixels, and a phone applies a further scale on top so text stays readable at arm's length. px is stable against the rest of your layout and means nothing at all against glass.

Why rem is the default for text

Because 16 px is not a constant, it is a default, and defaults get changed. Chrome, Firefox and Edge all put a text size control in their ordinary settings panel, no accessibility menu required. Set it to 20 and every rem on the page grows by a quarter — 1rem stops meaning 16 px and starts meaning 20. Everything you sized in px on that same page does not move.

That is the whole argument, and it is worth being honest about how big it is. The common claim that px text is inaccessible is overstated. The W3C's own Understanding document for success criterion 1.4.4 — the one requiring text to survive being resized to 200% without breaking — treats content as passing if any text-scaling mechanism the browser offers does the job, and full-page zoom scales px along with everything else. On that reading, px body copy does not fail the checkbox.

What it does fail is the reader who never touches zoom. Zoom enlarges images, layout and line lengths too, and plenty of people only want bigger words. They set the browser's text size once, years ago, then land on a page whose body copy is font-size: 14px and nothing happens. Firefox's "Zoom text only" switch is the same group of people asking for the same thing. rem answers them, and it costs you nothing.

When px is still the right answer

Sizing everything in rem is a real mistake, just a less common one. Use px where scaling would make the value wrong rather than merely different:

Mixing units inside a single rule is normal, not a smell. padding: 1rem; border-bottom: 1px solid is exactly right: the space follows the text, the line does not.

What em is actually for

em refers to the font size of the element itself — with one exception that causes most of the confusion. On the font-size property, em resolves against the parent's font size, because the element's own size is the thing being calculated. Everywhere else, padding included, it resolves against the element's own already-computed size.

That exception is why a nested list styled at 0.9em shrinks at every level: 0.9, then 0.81, then 0.729, and four levels down a 16 px base has become about 10.5 px. It is also what makes em genuinely useful. Set one font size on a button, express its padding, gap and border radius in em, and the whole component scales as a unit from a single number. Change that number and the button grows correctly without touching four other declarations.

The rule of thumb: em when the value belongs to the component, rem when it belongs to the page. Button padding is the component's business. The gap between two sections is the page's.

In media queries, em and rem are the same thing

The specification is explicit that relative units inside a media query resolve against the initial font size, never against the result of a declaration. So @media (min-width: 40em) and @media (min-width: 40rem) produce an identical breakpoint, and neither one is affected by html { font-size: 62.5% }. Both still follow the reader's default text size, which is the point: someone reading at 20 px reaches the wider layout later, when their larger text actually needs the room. A breakpoint in px ignores that entirely.

What do you divide by to get rem?

The root font size. At the default 16 that gives 24 px = 1.5rem, 12 px = 0.75rem, 14 px = 0.875rem, 18 px = 1.125rem. The round ones you memorise in a week. The odd ones you look up forever.

Doing that across a whole stylesheet is where the afternoon goes, so it is worth keeping a px to rem converter open beside the editor. Its "read from this window" button fills in the root font size your browser has actually computed, which is the fastest way to find out that something in your own CSS already overwrote it.

Is the 62.5% trick worth it?

html { font-size: 62.5% } makes 1rem equal 10 px for a reader on the default, so 1.6rem can be read straight off the page as "16px" and the division disappears. The root is still a percentage of the reader's setting, so their preference survives and this is not an accessibility failure. The bill arrives through inheritance. Every unstyled element now starts from 10 px, so the body needs resetting immediately, and anything you drop in that was authored in rem — a date picker, a component library, an embedded widget — renders 37.5% smaller than its author intended. Fine on a codebase you control end to end. Not fine inside someone else's design system.

The four mistakes worth knowing about

Setting html { font-size: 16px }. This is the one that quietly cancels everything above. It replaces the reader's preference with your number, and now your carefully rem-based stylesheet behaves exactly like a px-based one. If you need to adjust the root, use a percentage or leave it alone.

Putting a unit on line-height. A unitless line-height: 1.5 inherits as a ratio, so each child recalculates it from its own font size. line-height: 1.5rem inherits as a fixed length, and a heading that inherits it gets 24 px of line box around 32 px text. Unitless, always.

Using em for page-level spacing. The margin between two sections should not change because one of them happens to contain smaller text. That is a rem value.

Trusting an em or % conversion you did not check. px, rem, pt and the physical units are exact arithmetic. em and % are not: their real value depends on the computed font size of an element you have to go and look up, and no converter can read your cascade for you. Type the wrong parent size in and you get a confidently wrong answer out.

So what should you actually do

Leave the root font size alone. Write font sizes, vertical spacing and breakpoints in rem. Write borders, hairlines and fixed graphics in px. Reach for em inside a component when you want one font size to drive the rest of it, and nowhere else. Keep line-height unitless.

That covers close to everything. The remaining units are narrower: pt, cm and mm only mean something in a print stylesheet; % means a different thing on almost every property it appears on; ch and ex depend on the metrics of the specific font file, which is why no converter can compute them for you. And if a page is set in rem and still hard to read, the units were never the problem — the contrast ratio your text needs is the other half of legibility, and it is the half people skip.

The arithmetic is trivial and doing it forty times is not, which is the entire case for keeping the CSS unit converter in a tab. It shows one value in eleven units at once, and each row says which of your four assumptions — root font size, element font size, viewport width, viewport height — produced that number. The em and % rows are only ever as right as the font size you typed in, and ch and ex are absent on purpose, because glyph metrics cannot be derived by arithmetic.

If you are tidying up a stylesheet's units, the colour values are usually the next thing you find. HEX, RGB and HSL compared asks the same question this post does — which notation you can actually edit by hand — about colour instead of length.

Frequently asked questions

Should I use px or rem in CSS?

Use rem for anything that should follow the reader’s text size: font sizes, vertical spacing and breakpoints. Use px for anything that would be wrong rather than merely different at a larger size, such as a hairline border, a fixed-resolution icon or a small shadow offset. Both belong in the same rule regularly — padding in rem with a 1px border under it is correct, not a smell.

What is the difference between em and rem?

rem is measured against the font size of the root html element, so one rem is the same length everywhere on the page. em is measured against the font size of the element it appears on — except on the font-size property itself, where it uses the parent’s, because the element’s own size is what is being calculated. That exception is why em compounds through nested elements and rem never does.

Is it bad to use px for font size?

It does not fail WCAG success criterion 1.4.4, which is satisfied if any text-scaling mechanism the browser offers works, and full-page zoom enlarges px text along with the rest of the page. What px ignores is the reader who raised the browser’s default text size instead of zooming; that person gets nothing from your page. Since rem costs nothing extra, there is little reason to set body copy in px.

Does html { font-size: 62.5% } cause accessibility problems?

Not by itself. The root stays a percentage of whatever text size the reader has chosen, so their preference still scales the whole page. The real cost is inheritance: every element now starts from 10px instead of 16, so the body needs an explicit reset, and any third-party CSS authored in rem renders 37.5% smaller than intended.

Should breakpoints be in px, em or rem?

em and rem behave identically in media queries: the CSS specification says relative units there resolve against the initial font size and ignore any font-size set on the html element. Either one makes breakpoints follow the reader’s text size preference, so someone reading larger text reaches the wider layout when their text actually needs the room. A px breakpoint ignores that preference entirely.

Why does my nested list keep getting smaller?

Because a font-size in em multiplies against the parent’s font size, so it compounds once per level of nesting. A rule of 0.9em is 0.81 of the original two levels down and 0.729 three levels down — starting from 16px, that is about 11.7px. Set that rule in rem instead, or scope it with a child selector so it only applies to the first level.

Last updated September 19, 2026