Stop treating 16px as sacred
Most browsers ship with a 16px root. Teams treat the number as a law. Your project already changed html { font-size } for a compact admin panel, a large-type accessibility mode, or a design system step. Convert against the root you ship, not the textbook default.
Open DevTools. Inspect html. Read the computed font-size. Put the number into the root field above. Then convert.
Figma handed you 24px. Now what?
A design file marks body copy at 16, a subheading at 24, a page title at 40. Those are screen pixels on a mock canvas. Drop the same numbers into CSS as px and users who enlarge default text in the browser get stuck. Rem rewrites each mark relative to the root:
- 24 ÷ 16 = 1.5rem
- 40 ÷ 16 = 2.5rem
- 12 ÷ 16 = 0.75rem for tight captions
Change the root to 18px and those rem values still describe the same ratios. The absolute pixel size moves with the root. Ratios stay put.
How the conversion runs
Formula: rem = pixels ÷ root font size. Reverse: pixels = rem × root font size. Both fields above stay linked. Edit either side. The formula strip, live scale, and table refresh together.
Fractional rem values are normal. A 15px label on a 16px root becomes 0.9375rem. Prefer four decimal places for precision, then trim trailing zeros in production if your linter prefers shorter tokens. Browsers accept the full fraction.
Everything here runs in your browser. No upload. No account. The numbers never leave the page.
Rem vs em vs px for real layouts
| Unit | Relative to | Pick when | Skip when |
|---|---|---|---|
| rem | Root (html) | Global type scale, spacing tokens, media-query breakpoints tied to type | A component must scale only with its own parent font-size |
| em | Parent element | Padding or icon size inside a button that should grow with the button label | Deep nesting, where each level multiplies the last |
| px | Absolute CSS pixels | Hairline borders, 1px rules, shadow offsets, device-pixel-snapped chrome | Body copy and vertical rhythm users expect to resize |
We recommend rem for the type scale and most spacing tokens. Keep borders in px. Reach for em only inside a self-contained control where the parent font-size is the intentional scale driver. For parent-relative work, the PX to EM Converter is the better match. To reverse a rem value back to pixels while debugging, use the REM to PX Converter.
Where this converter falls short
- No media-query math. A fluid clamp() scale needs min, preferred, and max values. Convert each stop separately, then assemble clamp yourself.
- Root percent tricks. Setting
html { font-size: 62.5% }so 1rem ≈ 10px changes the base. Enter 10 in the root field when you use the 62.5% trick, or the rem output will be wrong. - Zoom is not root. Browser page zoom scales everything. User font-size preference changes the root. Rem responds to the second. Px ignores both for type size.
- Subpixel rounding. 0.0625rem steps look clean in code. Paint still snaps to device pixels, so a long stack of fractional rem margins sometimes lands a pixel off a Figma guide.
Mistakes we see every week
- Converting against 16 while the stylesheet sets root to 15 or 18.
- Using rem for a border width, then wondering why a hairline thickens when the user enlarges text.
- Nesting rem inside a container with a transformed font-size and expecting the rem to follow the container. Rem ignores the container. Em does not.
- Copying rem from a 16px root project into a 62.5% root project without recalculating.
If a value looks “almost right,” check the root first. The arithmetic is rarely the bug.
