PX vs REM vs EM: How to Choose CSS Units Without Breaking Accessibility
Every CSS file quietly contains a units strategy, even when nobody chose one. Font sizes in pixels, padding in rems, breakpoints in ems: each choice decides whether your layout respects the reader's own settings or silently overrides them. Get it wrong and the people who most need larger text get a site that fights their browser.
This guide explains the three units designers actually choose between, px, em, and rem, with worked conversions you can check against the px-to-rem converter. It covers which unit to use for type, spacing, components, and media queries, the accessibility rule the choice hangs on, and the handful of mistakes that break zoom and user font settings on otherwise well-made sites.
The Short Answer
- Font sizes: set them in rem. One rem equals the root element's font size, which the reader's browser settings control, so your sizes scale with their preference.
- Spacing inside a component: em is the specialist. It scales with the component's own text size, so a button's padding grows when its label does.
- Borders, hairlines, and pixel-exact nudges: px is fine. A 1px border does not need to respect anyone's font settings.
- Media queries: em (or rem, which browsers treat identically there). The breakpoint then responds to the reader's font size as well as the window width.
Everything below is why, plus the arithmetic.
What a Pixel Actually Is in CSS
A CSS pixel is not a screen pixel. Since the high-density display era, CSS defines px as an abstract unit anchored to a reference viewing distance, and the browser maps it to however many physical pixels make text readable at that size. On a standard display the mapping is 1 to 1; on a retina phone it is often 1 to 3. This is why px sizes look consistent across devices even though the hardware differs wildly.
The practical consequence: px in CSS is already a relative unit, relative to the device. What it is not relative to is the user. Set font-size: 16px on the html element and you have told every reader that their browser's text-size preference no longer applies to your page. That single declaration is the most common accessibility bug in hand-written CSS.
The fix is structural: never set the root font size in px. Leave it unset and the browser default (16px in every mainstream browser unless the user changes it) flows through, or set it as a percentage (100 percent, or a deliberate scale) which multiplies the user's setting instead of replacing it. Your rems then convert at whatever root size the reader actually has.
REM: Relative to the Root
A rem ("root em") always refers to the font size of the html element, wherever it is used in the document. Set the root at its untouched default and the conversions at default settings are simple multiples of 16px:
| Design value (at default root) | In rem |
|---|---|
| 12px | 0.75rem |
| 14px | 0.875rem |
| 16px | 1rem |
| 18px | 1.125rem |
| 20px | 1.25rem |
| 24px | 1.5rem |
| 32px | 2rem |
| 48px | 3rem |
The right mental model is "desired size divided by root size," and the fastest way to sanity-check a conversion is the px-to-rem converter, which also handles the case where your project sets a different root size. Two properties make rem the right unit for type across a whole site. First, predictability: unlike em, a rem never compounds, so 1.25rem is the same size in the footer as in the hero. Second, respect: when a reader raises their browser's default font size, every rem on your page rises proportionally, which is exactly what they asked their browser to do.
The same table serves spacing. Page-level margins, paddings between sections, and gaps in layout grids take rem for the same reason type does, so the whole page breathes at the reader's scale:
| Spacing value (at default root) | In rem |
|---|---|
| 4px | 0.25rem |
| 8px | 0.5rem |
| 16px | 1rem |
| 24px | 1.5rem |
| 32px | 2rem |
| 48px | 3rem |
| 64px | 4rem |
Pick steps on a consistent rhythm (powers of two and simple halves keep a system legible to the next person), name them as tokens, and never type a raw spacing number anywhere again.
The famous 62.5 percent trick, setting the root to 10px at default settings so 1.6rem reads as 16px, trades that respect for mental arithmetic. It still respects user settings (it is a percentage of the user's default), but it surprises every developer who inherits the file and any component library pasted into it. Most teams are better served by plain conversions and a converter at hand.
EM: Relative to Context
An em refers to a font size that depends on where the em is used. In the font-size property itself, 1em means the parent element's computed font size. In every other property, padding, margin, width, 1em means the element's own computed font size. That dual meaning is the source of both em's power and its one real hazard.
The power: component-relative spacing. A button with font-size: 1rem and padding: 0.6em 1.2em keeps its internal proportions at every size; grow the label and the padding grows with it. Alerts, badges, form inputs, and cards all benefit from spacing that tracks their own type rather than the root.
The hazard is compounding. Nested elements that each set font-size in em multiply: a 1.2em list inside a 1.2em section inside 1.2em body copy renders its text at 1.728 times the root size. Nothing warns you; the text is just quietly larger than the spec said. The discipline that avoids it is simple: font-size in rem, spacing in em. Then compounding can never happen, because em used for spacing refers to a font size that was itself set in rem.
Percent in font-size behaves like em (150 percent equals 1.5em) and compounds the same way. It is the right choice in exactly one common place: the root element, where 100 percent defers to the reader.
The Accessibility Rule Behind All of This
WCAG 2.1 Success Criterion 1.4.4, "Resize Text" (Level AA), requires that, except for captions and images of text, "text can be resized without assistive technology up to 200 percent without loss of content or functionality." Two clarifications from the WCAG documentation matter for unit choices. The intent is that users with low vision can enlarge text; the author is not responsible for scaling that only a user agent provides if the page then breaks, but the page must not block or defeat the scaling the browser offers. And scaling done entirely by the browser, page zoom, or a conforming alternate version each can satisfy the criterion when content and function survive.
Units are how a page defeats scaling by accident. Text set in px inside a fixed-height container overflows or clips at 200 percent. Text set in rem on an untouched root, in containers that grow, scales the way the reader asked. The WCAG compliance guide covers the full checklist this criterion belongs to; the unit rules here are the piece that lives in your stylesheet.
Breakpoints belong to this story too. A media query written in em or rem is evaluated against the reader's root font size, so when a user raises their default text size, your "narrow layout" breakpoints arrive earlier, giving larger text the narrower, more readable measure. A breakpoint in px responds only to the window. That is why experienced responsive code writes breakpoints in em: 48em at default settings is 768px, and it becomes a genuinely adaptive threshold rather than a magic number. The typography scale system guide shows how a rem-based scale and em breakpoints work together across a full design system.
A Worked Example: One Component, All Three Units
Consider a notification banner, starting from a spec written in pixels: 18px headline, 16px body, 16px padding, 24px vertical margin between banners, 1px border, on a root font size of 16px.
Converted sensibly:
- Headline font-size: 1.125rem (18 divided by 16). It will scale with the reader's root setting.
- Body font-size: 1rem. Paragraph spacing in rem, 1rem, keeps the vertical rhythm predictable across the page.
- Padding: 1em. It tracks the banner's own text; if a compact variant drops the body to 0.875rem, the padding tightens by itself.
- Margin between banners: 1.5rem. Page-level rhythm belongs to the root scale, not to one component's type.
- Border: 1px. A hairline's job is to be a hairline at every setting.
Check any of the conversions instantly in the px-to-rem converter by entering the pixel value and your root size. The habit worth building is not memorizing the table; it is never writing a font-size the reader cannot override.
Breakpoints, Converted
The classic responsive widths, expressed the way your media queries should be once the root does the talking. Values assume the untouched 16px default; a reader at 20px shifts every threshold wider by the same proportion, which is precisely the behavior you want.
| Window width (at default root) | Media query |
|---|---|
| 480px | 30em |
| 640px | 40em |
| 768px | 48em |
| 1024px | 64em |
| 1280px | 80em |
| 1536px | 96em |
Tokens and Scales: Where the Units Live in a Design System
A team usually meets this question at the design-token layer, where spacing and type decisions get names. Keep the unit rules in the token values themselves: type tokens in rem (t-body 1rem, t-h2 1.5rem, t-display 3rem), space tokens in rem for page rhythm (space-4 1rem, space-8 2rem), and let components translate space into em with their own local font size when the padding should breathe with the label. The token names then stay stable forever while the units quietly route each value to the right behavior.
Two scale habits pair well with this. Set line-height as a unitless multiplier (1.5, not 1.5em or 24px) so it rescales on every element it lands on. And name breakpoints after what the layout does rather than a device: mq-stack 30em, mq-two-col 48em, mq-wide 80em. Devices change yearly; layout intentions do not.
Three Production Bugs This Prevents
- The iOS input zoom. iOS Safari zooms the page when a focused form field's text is smaller than about 16px, a deliberate readability feature that feels like a bug to everyone who meets it unprepared. Form text at 1rem on an untouched root scales from the reader's own setting and sidesteps the trigger; form text shrunk in px walks into it.
- The fixed-height clip. A card given an exact pixel height swallows rem-sized text whole at 200 percent resize, the precise failure the resize criterion describes. Minimum heights, or none at all, let the box grow with its content.
- The compounded sidebar. A navigation component that sets its font size in em, nested inside two ancestors that did the same, drifts from every mockup and gets "fixed" with a hard-coded pixel value that then cannot scale. Font sizes in rem make the drift impossible in the first place.
Unit by Unit: The Decision Table
| Job | First choice | Why |
|---|---|---|
| Body and heading font sizes | rem | Scales with user preference, never compounds |
| Line height | unitless number | Multiplies the font size; stays proportional on every element |
| Component padding and gaps | em | Tracks the component's own type size |
| Page layout spacing | rem | Consistent rhythm tied to the root scale |
| Container widths for text | rem or ch | Measure follows type, not device pixels |
| Media query breakpoints | em | Respond to both window width and user font size |
| Borders and hairlines | px | Exact, tiny, and not a legibility feature |
| Icon sizes inline with text | em | Icons join the text's own scale |
Making Fluid Type Safe: Min, Max, and the Middle
Viewport units enter this conversation because designers want headings that grow with big screens. Used alone, a font-size in vw ignores the reader's root size entirely, the same defect as px with extra steps. The clamp() pattern fixes it by pairing a rem floor and ceiling with a viewport-based middle:
font-size: clamp(1.125rem, 1rem + 1vw, 2rem);
Read it as: never smaller than 1.125rem, never larger than 2rem, and between those, grow with the viewport. Set the two rem bounds first, from your type scale, and the middle term only chooses where between them the current window lands. A heading built this way can flex across layouts without ever dropping below the size the reader's settings would have given them, and without ballooning on a cinema display. Body text should not flex this way at all; 1rem is already the reader's chosen size, and the correct fluidity for paragraphs is a fixed rem size with a comfortable measure.
Two Units Worth Knowing by Name
ch is the width of the zero glyph in the current font. Text columns constrained with max-width in ch (a measure near 60 to 75ch is the classic readable range) follow the font, so a reader's larger text gets a proportionally wider column rather than the same narrow slot with bigger letters. Because the metric comes from the font itself, the measure stays honest when families change.
Percent on the root is the quiet hero of this whole guide. html set to 100 percent simply adopts the reader's default; any lower percentage scales the whole site relative to their choice. The root is the one place where a percent, and effectively an em mindset, is not a compounding hazard but the entire point.
Print stylesheets follow the same rules with one wrinkle: in print CSS, physical units anchor px to 1/96 of an inch, and rem and em resolve exactly as on screen relative to the root. A rem-based screen scale prints sensibly without a separate print typography system, which is one more inheritance benefit of the approach.
None of this requires a framework or a build step. It is a dozen decisions made once in a stylesheet's foundations, inherited by every component that follows, which is why unit discipline is cheap to adopt at the start of a project and tedious to retrofit. Fixing a mature stylesheet is still worthwhile: most of the damage concentrates in a small number of base styles, and converting those converts everything downstream of them.
Before You Ship: Units Checklist
- Root untouched or percentage-based: the html font size is never set in px, so reader settings flow through.
- No px font sizes below the root: every text size is in rem (or inherits), including small print and captions.
- Spacing units split on purpose: em inside components, rem for page rhythm, by convention not accident.
- Breakpoints in em: media queries respond to user font size as well as width.
- 200 percent resize tested: text scales to double size with nothing clipped, overlapped, or cut off, per WCAG 1.4.4.
- Containers grow: no fixed pixel heights on text blocks whose content must reflow at large sizes.
FAQ
Is 1rem always 16px?
Only when the reader's root font size is the untouched default. 1rem always equals the html element's computed font size, which browsers default to 16px and users can raise or lower in settings. That variability is the feature, not a bug; it is how rem-based sites honor reader preferences.
Should I set html font-size to 62.5 percent to make the math easy?
You can, and it does respect user settings because it is a percentage of their default. The cost is surprise: every pasted component, library, and new teammate expects 1rem to relate to a 16px default. Plain conversions, a quick converter check, and a documented type scale age better.
Why do my nested lists keep getting bigger?
Their font sizes are set in em or percent, which multiply down the tree. Set font-size in rem throughout, and keep em for spacing only, and compounding disappears while component-relative padding still works.
Are pixels ever the right unit for text?
For a truly fixed artifact, a canvas game or an image-replacement graphic, px can be defensible, with the understanding that the reader's text setting will not move it. For text that forms part of an HTML document, rem is the unit that meets the resize requirement without extra work.
What about vw and vh for type?
Viewport units size text to the window, which is a different job. They are useful inside a clamp() that also sets rem-based minimums and maximums, so text can flex with layout without ever dropping below what the reader's root size would give them. Viewport units alone also defeat browser text scaling, so never let them stand by themselves for body or heading sizes.
Do em breakpoints change my layout for users with large default text?
Yes, and that is the intent. A reader whose default is 20px gets your narrower layouts at wider windows than a 16px reader does, because their larger text needs the narrower measure sooner. The layout adapts to the reader instead of interrogating the device.
What to Read Next
- PX to REM converter - convert values both ways at any root size, while you are editing the stylesheet.
- Typography scale system guide - building a rem-based modular scale for the whole site.
- WCAG compliance and accessibility guide - the full checklist that the resize rule belongs to.
- Accessible color systems - sizing's partner discipline: color that survives every reader too.
This guide is maintained by the Lucky Graphics editorial team and reflects WCAG 2.1 Success Criterion 1.4.4 and standard CSS sizing behavior as documented by browser vendors.