</>
DevToolHub
Laptop computer showing code editor with lines of programming code

Why Use rem Instead of px? (The Accessibility Case)

·10 min read
Quick answer: rem scales with the user's browser font size preference. px ignores it. When someone sets their browser to "Large" text (20px default instead of 16px), 1rem becomes 20px while 16px stays at 16px. For the 3-5% of users who change their default font size --- many of whom have low vision --- this is the difference between reading your content and not reading it.

Three years ago I audited a client's e-commerce site for accessibility. The entire CSS was in px. Every font size, every margin, every padding --- hardcoded pixels. The site looked sharp on my 27-inch monitor. On a browser set to 150% default font size (common for users over 60), the text was still the same hardcoded size. Navigation links at 14px stayed at 14px. Product descriptions at 15px stayed at 15px. The only way to make text larger was browser zoom, which blew up the entire layout and made the checkout form overflow its container.

The fix took two days. Replace px with rem on font sizes and spacing. The site now responds to user preferences without breaking. Here's why this matters and exactly how it works.

What Actually Happens When You Use px

A pixel is absolute. font-size: 16px renders at 16 CSS pixels regardless of what the user configured in their browser settings.

Browsers have a font size preference (Chrome: Settings > Appearance > Font size). The options are typically Very Small (12px), Small (14px), Medium (16px, the default), Large (20px), and Very Large (24px). This setting exists specifically for users who need larger text --- people with low vision, older users, or anyone using a high-DPI screen from a distance.

When you write font-size: 16px, you're telling the browser: "I know better than the user. Display this at 16px no matter what they've configured." The browser obeys. The user's preference is overridden.

This isn't a theoretical problem. The UK Government Digital Service tested their GOV.UK platform and found that approximately 4% of visitors had changed their default browser font size. WebAIM's 2024 screen reader user survey found that 27% of respondents used browser zoom or font size adjustments as an accommodation. These are real people on real websites.

What Actually Happens When You Use rem

rem is relative to the root element's (<html>) font size. The root font size defaults to whatever the user's browser preference is --- typically 16px, but potentially 12px, 20px, 24px, or any custom value.

/* If user's browser default is 16px (standard) */
h1 { font-size: 2rem; }     /* 2 x 16 = 32px */
p  { font-size: 1rem; }     /* 1 x 16 = 16px */

/* If user's browser default is 20px (set by user with low vision) */
h1 { font-size: 2rem; }     /* 2 x 20 = 40px */
p  { font-size: 1rem; }     /* 1 x 20 = 20px */

The proportions are identical. The heading is always 2x the body text. But the absolute size scales to match what the user asked for. The design stays coherent while respecting the user's needs.

This is the entire argument for rem. One unit. One behavior change. It turns your CSS from "ignore user preferences" to "respect user preferences." The CSS unit converter handles the px-to-rem math if you're converting an existing codebase.

Zoom Behavior: px vs rem Side by Side

Here's where it gets concrete. This table shows how text renders across different browser font size settings and zoom levels:

Browser Font SettingZoom Level16px Renders As1rem Renders AsDifference
Medium (16px)100%16px16pxNone
Medium (16px)125%20px20pxNone
Medium (16px)150%24px24pxNone
Large (20px)100%16px20pxrem is 25% larger
Large (20px)125%20px25pxrem is 25% larger
Large (20px)150%24px30pxrem is 25% larger
Very Large (24px)100%16px24pxrem is 50% larger
Very Large (24px)125%20px30pxrem is 50% larger
Very Large (24px)150%24px36pxrem is 50% larger
Notice the pattern: both units respond identically to zoom. The divergence happens with the browser font setting. At "Large" (20px), rem faithfully delivers 25% larger text. px ignores the setting entirely and only grows with zoom.

A user who set their browser to "Very Large" (24px) is telling every website: "I need text at 150% of default." With px, they get nothing. With rem, they get exactly what they asked for. The layout scales proportionally because spacing (also in rem) grows along with the text.

The WCAG Connection

WCAG 2.2 Success Criterion 1.4.4 (Resize Text) requires that text can be resized up to 200% without assistive technology and without loss of content or functionality. The conformance level is AA --- the standard most organizations target for legal compliance (ADA, Section 508, EAA in Europe).

Using px for font sizes doesn't automatically violate SC 1.4.4, because browser zoom (Ctrl/Cmd + Plus) still works regardless of the unit. But it does violate the spirit of SC 1.4.4 and creates a worse experience for users who prefer the browser font setting over full-page zoom.

The practical difference between zoom and font size adjustment:

BehaviorBrowser Zoom (Ctrl+)Browser Font Size Setting
What scalesEverything: text, images, layout, whitespaceOnly text and rem-based values
Layout impactEntire viewport shrinks (less content visible)Layout stays the same width, text gets larger
User controlCoarse (125%, 150%, 200% steps)Fine (any px value, typically 12-24px)
PersistencePer-tab, per-site, sometimes lost on restartGlobal, permanent across all sites
MobilePinch-to-zoom (often disabled by sites with user-scalable=no)Respected if rem is used
Font size settings give users fine-grained control without distorting the layout. Zoom is a blunt instrument that works but isn't the preferred method for users who need consistent text scaling.

Many accessibility consultants, including those at Deque Systems (makers of axe DevTools), explicitly recommend rem for font sizes as a best practice. It's not a WCAG requirement --- it's a UX best practice that makes WCAG compliance easier.

The Responsive Design Argument

Beyond accessibility, rem makes responsive design more predictable. Consider media queries:

/* px-based media query */
@media (min-width: 768px) {
  .sidebar { display: block; }
}

/* rem-based media query (recommended) */
@media (min-width: 48rem) {
  .sidebar { display: block; }
}

At default browser settings (16px root), 48rem equals 768px. Identical behavior. But if a user has set their browser font to 20px, 48rem becomes 960px. The sidebar only appears when there's genuinely enough space for both the content and the sidebar text at the user's preferred size. The px version shows the sidebar at 768px regardless, potentially cramming oversized text into a too-narrow container.

This is the subtle power of rem in media queries: your breakpoints adapt to the user's content size, not just the viewport size. The layout responds to actual content needs rather than arbitrary pixel thresholds.

Safari caveat: Safari handles rem in media queries inconsistently in some older versions. The widely accepted workaround is to use em for media queries specifically (not rem), since em in a media query context always references the browser default font size. @media (min-width: 48em) behaves identically to @media (min-width: 48rem) in all browsers except Safari, where em is more reliable.

What Should Stay in px

Not everything benefits from scaling. Some values should be absolute:

Borders. A 1px border should stay 1px. A 1rem border at 24px root font size becomes a 1.5px border --- the browser rounds to 2px, giving you a visibly thicker border than intended. Borders are visual accents, not content. They don't need to scale with font preferences.

.card {
  border: 1px solid #e5e7eb;      /* px: stays 1px always */
  border-radius: 0.5rem;          /* rem: scales proportionally */
  padding: 1.5rem;                /* rem: scales with font size */
  box-shadow: 0 2px 4px rgba(0,0,0,0.1); /* px: shadow stays fixed */
}

Box shadows. Shadow offsets and blur radii are visual effects. A shadow that scales with font size looks wrong --- larger text doesn't need a proportionally larger shadow. Keep shadows in px.

Outlines and focus indicators. Accessibility focus indicators need to be consistently visible. A 2px outline is the WCAG-recommended minimum. Scaling it with rem could make it too thin at small root sizes or too thick at large ones.

Fine decorative details. Thin lines, dividers, underline offsets, small icon strokes --- anything where sub-pixel precision matters.

Here's a quick decision table:

Property TypeUnitExample
Font sizesremfont-size: 1.125rem
Spacing (padding, margin, gap)rempadding: 1.5rem
Max-width for text containersch or remmax-width: 65ch
Border widthpxborder: 1px solid
Border radiusrem or pxborder-radius: 0.5rem
Box shadowpxbox-shadow: 0 2px 8px
Outlinepxoutline: 2px solid
Media queriesem@media (min-width: 48em)
Line heightUnitlessline-height: 1.5
Letter spacingemletter-spacing: 0.02em
The border radius generator outputs values in both rem and px, so you can pick whichever convention your project uses.

Migration Strategy: px to rem

If you have an existing codebase in px, here's the migration path I've used on three production sites:

Step 1: Don't touch the root font size. Leave html at the browser default (100% or 16px). Setting html { font-size: 62.5% } to make 1rem = 10px breaks third-party components that expect 1rem = 16px. It's a shortcut that creates more problems than it solves.

Step 2: Convert font sizes first. Font sizes have the biggest accessibility impact. Divide each px value by 16 to get rem. 14px = 0.875rem. 18px = 1.125rem. 24px = 1.5rem. Use the CSS unit converter for bulk conversion.

Step 3: Convert spacing (padding, margin, gap). Same formula: divide by 16. 8px = 0.5rem. 16px = 1rem. 24px = 1.5rem. 32px = 2rem.

Step 4: Leave borders, shadows, and outlines in px. These don't need to scale.

Step 5: Test at 150% and 200% browser font size. Set Chrome to "Very Large" (24px default) and check that nothing overflows, overlaps, or becomes unreadable. The most common breakage: fixed-width containers that can't accommodate larger text.

Common px-to-rem conversions (assuming 16px root):

pxremCommon Use
10px0.625remSmall labels, captions
12px0.75remSecondary text, footnotes
14px0.875remBody text (smaller), form labels
16px1remDefault body text
18px1.125remLarge body text, sub-headings
20px1.25remH4, card titles
24px1.5remH3, section headings
30px1.875remH2, major headings
36px2.25remH1, page titles
48px3remHero text, display headings

FAQ

Do CSS frameworks like Tailwind already use rem?

Yes. Tailwind CSS uses rem for all spacing and font size utilities by default. text-base is 1rem (16px). p-4 is 1rem (16px) of padding. text-lg is 1.125rem (18px). If you're using Tailwind, you're already getting the accessibility benefits of rem without thinking about it. The only exception is border widths --- Tailwind's border is 1px, which is correct.

What if my designer gives me a Figma file with all px values?

Convert them. Figma works in pixels because it's a fixed-canvas design tool. The designer's intent is the visual proportion, not the literal pixel value. A heading at 32px in Figma becomes 2rem in CSS --- visually identical at default settings, but responsive to user preferences. Most design handoff tools (Figma's Dev Mode, Zeplin) can be configured to show rem values.

Does rem affect performance?

No. The browser resolves rem to a computed pixel value during the layout phase, the same phase where it resolves px. The resolved values are cached per element. There is zero runtime performance difference between 1rem and 16px --- they produce the same computed output. The unit choice is purely about maintainability and user experience.

Should I use rem for everything, including width and height?

For most widths, percentage (%) or viewport units (vw) are better than rem because widths typically relate to the viewport, not to font size. For max-width on text containers, ch (character width) or rem both work well. For height, avoid fixed values entirely when possible --- let content determine height. Use rem for min-height when you need a minimum vertical space.

Next Steps