px → rem When the Root Font Size Is Not 16

(Updated: July 16, 2026 ) CSS rem px accessibility typography px-rem

A design spec says 24px. The stylesheet has html { font-size: 10px; } from an old bootstrap habit. Someone writes 1.5rem believing the root is 16px and ships 15px type. Another teammate uses a converter that assumes 16 and double-locks the bug. rem is not a synonym for “px÷16.” It is “px ÷ root font-size.”

Define the root before you convert

Root strategyComputed root (typical)24px in rem
Browser default16px1.5rem
62.5% on html10px (if default 16)2.4rem
Explicit 10px10px2.4rem
User default 20px, no author root20px1.2rem

Always ask: what is getComputedStyle(document.documentElement).fontSize in the environment you care about? Converters should take a root px input. A px–rem calculator that lets you set the root avoids the folklore spreadsheet.

Accessibility angle

Users enlarge default font size because body text is hard to read. Layouts built entirely in px ignore that preference for spacing and type (zoom still helps, but rem is the courteous unit for UI chrome tied to text). Conversely, locking the root to absolute 10px can fight user preferences — prefer percentage-based strategies thoughtfully, and test with default font set to Large in the OS or browser.

When px is still correct

  • 1px borders and hairlines
  • Shadows tuned to device pixels (sometimes)
  • Overlays matching raster assets of fixed size
  • Media features that compare to viewport in px (as the platform defines)

Mixing is normal. Dogmatic “never px” produces thicker borders on large-root setups; dogmatic “always px” produces inaccessible type. rem for type and spacing, px for precision chrome, is a calm default.

Component libraries and nested rem myths

rem ignores parent font-size; em does not. If a button sets font-size: 0.875rem and an icon uses 1em, the icon tracks the button. If the icon uses 1rem, it tracks the root. Both are valid — document which you mean in the design system so contributors stop “fixing” each other’s icons.

Migration tips

  1. Record the official root font-size in the design tokens README.
  2. Convert spacing scales in bulk with the same root assumption.
  3. Spot-check pages with browser default 12 / 16 / 20.
  4. Avoid reintroducing magic ÷16 comments that contradict html CSS.
  5. When you change root strategy, re-audit shadows and borders that accidentally became rem.

Tailwind and utility pitfalls

Utility frameworks often ship spacing in rem already. Converting a mock’s 16px padding to p-4 assumes their scale and their root. If you customized html font-size, the visual no longer matches the docs’ “16px” commentary. Either keep root at default and embrace the scale, or document the px equivalent of each token under your root.

Designers working in Figma pixels should label frames with the agreed root. Handing off “everything in px” without stating root makes every converter disagree — including two humans with calculators.

Subpixel rounding and testing

Browsers round differently when many rem values sum to a container. Prefer consistent scales (4px/8px grids expressed in rem) over arbitrary 13px leftovers converted to 0.8125rem everywhere. A token scale reduces drift.

Check at least:

  • Default font 16px
  • Default font 20px
  • Page zoom 110%
  • Root overridden to 62.5% (if you use that pattern)

If any matrix cell collapses text or overflows chips, your conversion assumptions are too rigid.

Pixels are absolute in the mock. rem is a contract with the root. Honor the contract, and the converter becomes trivial arithmetic instead of a source of off-by-33% typography bugs.