Every CSS value you write in pixels is a decision to ignore user accessibility preferences. That sounds harsh, but it's technically accurate.
When a user sets their browser's default font size to 20px instead of the standard 16px, they're telling every website: "I need larger text." Rem units respect this preference. Pixel units do not.
The conversion
1rem = the root element's font size (usually 16px by default).
To convert: rem = px / 16.
That's it. Here's a reference table for the most common values:
10px = 0.625rem
12px = 0.75rem
14px = 0.875rem
16px = 1rem
18px = 1.125rem
20px = 1.25rem
24px = 1.5rem
28px = 1.75rem
32px = 2rem
36px = 2.25rem
40px = 2.5rem
48px = 3rem
64px = 4rem
Where to use rem, where to keep pixels
Use rem for:
- Font sizes (always)
- Spacing and padding on text-adjacent elements
- Max-widths for readable text containers
- Media query breakpoints (though these always use the browser default regardless)
Keep pixels for:
- Borders (a 1px border should stay 1px)
- Box shadows
- Very small decorative elements
- Outlines
The reasoning: borders and shadows are visual decorations that don't need to scale with text size. A 1px border at 200% zoom is still perfectly visible. But a 14px font size at 200% zoom is unreadably small for the user who needed that zoom.
CSS custom properties make this easier
Instead of memorizing rem values, define a spacing scale with custom properties:
:root {
--space-xs: 0.25rem; /* 4px */
--space-sm: 0.5rem; /* 8px */
--space-md: 1rem; /* 16px */
--space-lg: 1.5rem; /* 24px */
--space-xl: 2rem; /* 32px */
--space-2xl: 3rem; /* 48px */
}
.card {
padding: var(--space-md) var(--space-lg);
margin-bottom: var(--space-lg);
font-size: 1rem;
}
Now you're thinking in a design system rather than individual pixel values, and every value automatically scales with user preferences.
PostCSS and Sass functions
If you're using a build tool, you can automate the conversion:
@function rem($px) {
@return #{$px / 16}rem;
}
.heading {
font-size: rem(24); // 1.5rem
margin-bottom: rem(16); // 1rem
}
Or with PostCSS using the postcss-pxtorem plugin, which automatically converts px values in your compiled CSS to rem based on a configurable root value.
Testing your rem implementation
The simplest test: open your browser settings, change the default font size to 24px, and reload your site. Everything using rem should scale up proportionally. Everything using pixels will stay fixed. If your navigation text grows but your nav container doesn't, you've found a pixel/rem inconsistency.
I built a quick converter at zovo.one/free-tools/px-to-rem that handles single values and batch conversions with any base size. Keep it open in a tab while writing CSS. It saves more time than you'd expect.
I'm Michael Lip. I build free developer tools at zovo.one. 500+ tools, all private, all free.
Top comments (0)