PX to REM: The Developer's Guide to Scalable Typography
Learn why rem units improve accessibility, how to calculate them, and best practices for responsive typography in CSS.
The choice between px and rem in CSS is not just a stylistic preference — it has direct consequences for accessibility, user control, and responsive design. Millions of users set a custom font size in their browser preferences, and a stylesheet written entirely in px ignores that setting completely.
This guide explains the mechanics of each unit, the formula for converting px to rem, and the practical rules for when to use each in a real-world CSS codebase.
Understanding CSS units (px, rem, em, vh)
CSS offers multiple length units for different use cases. px (pixels) is an absolute unit — 1px on a 96dpi screen corresponds to a physical pixel, but on high-DPI (Retina) displays it maps to multiple physical pixels. rem (root em) is a relative unit based on the font-size of the root element (<html>). em is a relative unit based on the font-size of the current element or its nearest ancestor with an explicit font-size. vh and vw are viewport-relative: 1vh = 1% of the viewport height. Each unit has appropriate and inappropriate use cases.
Free Tool PX to REM Converter Instantly convert pixel values to rem with any base sizeWhy px is problematic for accessibility
Browsers allow users to set a default font size in preferences (typically 16px by default, but users may set it to 20px, 24px, or larger). When you set font-size: 16px in CSS, this overrides the user's preference — the text will always be 16px regardless of what the user has configured. This violates WCAG 2.1 Success Criterion 1.4.4 (Resize Text), which requires that text can be resized up to 200% without loss of content or functionality. Rem-based typography respects the user's root font size and scales accordingly.
How rem works (root font-size)
The rem unit is always relative to the font-size of the <html> element. By default, browsers set this to 16px. So 1rem = 16px, 1.5rem = 24px, 0.875rem = 14px. If a user increases their browser font size to 20px, then 1rem = 20px — all rem-based values scale proportionally. Never set font-size on <html> in pixels (e.g., html { font-size: 16px }) because this defeats the purpose — it resets the base to a fixed value regardless of user preferences. If you need to change the base, use a percentage: html { font-size: 112.5% } sets the base to 18px on a default 16px browser.
Free Tool Color Contrast Checker Check WCAG 2.1 contrast ratios alongside font size accessibilityPX to REM formula
The formula is simple: rem = px / base-font-size. With the default 16px base: 12px = 0.75rem, 14px = 0.875rem, 16px = 1rem, 18px = 1.125rem, 24px = 1.5rem, 32px = 2rem. In CSS preprocessors (Sass, Less), you can define a function. In modern CSS, custom properties and calc() can automate this without a preprocessor.
/* Sass function */
@function rem($px) {
@return #{$px / 16}rem;
}
/* Usage */
font-size: rem(18); /* → 1.125rem */
/* Pure CSS alternative */
:root {
--base: 16;
}
.text {
/* Manual calc — not dynamic */
font-size: calc(18 / var(--base) * 1rem);
} When to use rem vs em vs px
Use rem for global typography (body text, headings, labels) and for spacing that should scale with the user's font size preference. Use em for component-level spacing that should be proportional to the component's own font size (padding inside a button, for example — so the button scales uniformly when its font-size changes). Use px for borders, shadows, and layout elements that should not scale with text (1px borders should stay 1px regardless of font size). Use viewport units (vh, vw, vmin, vmax) for layout elements that should fill or relate to the viewport, not typography.
CSS custom properties for rem base
A common pattern is to set a CSS custom property for the base font size to enable easy global adjustment. Set font-size as a percentage on <html> to respect user preferences, then use rem throughout. The clamp() function creates fluid typography that scales between minimum and maximum values based on viewport width, without media query breakpoints. This combines the accessibility benefits of rem with fluid responsive behavior.
/* Accessible base — respects browser preference */
html {
font-size: 100%; /* = browser default (usually 16px) */
}
body {
font-size: 1rem; /* 16px at default */
}
h1 {
font-size: 2rem; /* 32px at default, scales with user prefs */
}
/* Fluid typography with clamp */
.hero-title {
font-size: clamp(1.5rem, 4vw, 3rem);
/* min 24px, max 48px, fluid between */
} Free Tool Color Palette Generator Build accessible color palettes alongside your typographic system