16px is 1rem at the default root, which is 16px in every major browser: 16 ÷ 16 = 1. It’s also 1em under a 16px parent, 100% as a font-size percentage, and 12pt, since CSS fixes 1pt at 4/3 px.
At other root sizes
root 16px (default) 16px = 1rem
root 10px (62.5%) 16px = 1.6rem
root 18px (112.5%) 16px = 0.8889rem
1rem isn’t always 16px
1rem is whatever the root’s font size is. If your CSS sets none, that’s the reader’s default: 16px out of the box, more if they raised it in browser settings. At a 20px default, 1rem is 20px while 16px stays 16px. That’s the reason to use rem; zoom scales both units alike.
Media queries are the exception. rem and em in @media use the initial font size, not the font-size you set on html (Media Queries Level 4), so 48rem means 768px at the default even under a 10px root. Tailwind CSS v4’s breakpoints are rem-based (md is 48rem) and work the same way.
Form fields on iOS
Safari on iOS zooms the page when a form field whose text is smaller than 16px gets focus. font-size: 1rem on input, select and textarea avoids that at the default size.
In Tailwind, Sass and CSS
Tailwind CSS v4’s text-base is 1rem and sets line-height: calc(1.5 / 1), 24px. On the spacing scale (--spacing: 0.25rem), p-4 is 1rem.
A Sass helper saves writing the division everywhere:
@use 'sass:math';
@function rem($px, $root: 16px) {
@return math.div($px, $root) * 1rem;
}
body { font-size: rem(16px); } // 1rem
In plain CSS, calc(16rem / 16) is 1rem; just write 1rem.