Three nested levels of 1.2em
- Input
- 16 × 1,2 × 1,2 × 1,2
- Expected output
- 27,648px
em compounds: two levels give 23.04px and three reach 27.648px.
px vs rem vs em: when to use each
The difference between the three units is their calculation base: px is fixed, rem starts from the root font-size and em starts from the element context. That base decides what happens when the person changes the browser font size or when elements nest. The tool below calculates all three side by side.
em compounds: two levels give 23.04px and three reach 27.648px.
rem always starts from the root, so nesting does not change the result.
Outside font-size, em uses the element own font: the padding follows the text.
Text in 16px would stay at 16px, ignoring the preference.
Divide the pixels by the root font-size. With a 16px base: 24px ÷ 16 = 1.5rem, 14px ÷ 16 = 0.875rem and 10px ÷ 16 = 0.625rem. To go the other way, multiply: 1.25rem × 16 = 20px.
No. px is the right choice for hairline borders, shadows and any detail that should not scale with the text. The point is not to lock in px the font sizes the reader might want to adjust.
Yes. According to the Media Queries specification, relative units there start from the initial value, never from declarations in the page. An html with a different font-size does not change the breakpoints.
Yes, zoom enlarges the whole page, including what is in px. What px does not follow is the default font-size preference, which only affects relative units such as rem and em.
Here, % means a percentage of the reference font-size (as in font-size: 150%). In width, padding and other properties, % refers to something else, so this conversion does not apply to them.
To rem: 24 ÷ 16 = 1.5 rem
Paste a CSS block and swap every value in one unit for another, leaving everything else alone. Comments, strings, url() and numbers inside names (classes, variables, colors) are left untouched.
Paste a CSS block above to convert. The result shows up here instantly.
Quick reference with a root font-size of 16px. Click a rem value to copy it. The Tailwind column shows the step n where n × 0.25rem gives the same size.
| px | rem | Tailwind (n) |
|---|---|---|
| 4px | 1 | |
| 6px | 1.5 | |
| 8px | 2 | |
| 10px | 2.5 | |
| 12px | 3 | |
| 14px | 3.5 | |
| 16px | 4 | |
| 18px | - | |
| 20px | 5 | |
| 24px | 6 | |
| 28px | 7 | |
| 32px | 8 | |
| 36px | 9 | |
| 40px | 10 | |
| 48px | 12 | |
| 56px | 14 | |
| 64px | 16 | |
| 72px | - | |
| 80px | 20 | |
| 96px | 24 | |
| 112px | 28 | |
| 128px | 32 |
| n | rem | px |
|---|---|---|
| 0 | 0 | 0 |
| 0.5 | 0.125 | 2 |
| 1 | 0.25 | 4 |
| 1.5 | 0.375 | 6 |
| 2 | 0.5 | 8 |
| 2.5 | 0.625 | 10 |
| 3 | 0.75 | 12 |
| 3.5 | 0.875 | 14 |
| 4 | 1 | 16 |
| 5 | 1.25 | 20 |
| 6 | 1.5 | 24 |
| 7 | 1.75 | 28 |
| 8 | 2 | 32 |
| 9 | 2.25 | 36 |
| 10 | 2.5 | 40 |
| 11 | 2.75 | 44 |
| 12 | 3 | 48 |
| 14 | 3.5 | 56 |
| 16 | 4 | 64 |
| 20 | 5 | 80 |
| 24 | 6 | 96 |
| 28 | 7 | 112 |
| 32 | 8 | 128 |
| 36 | 9 | 144 |
| 40 | 10 | 160 |
| 44 | 11 | 176 |
| 48 | 12 | 192 |
| 52 | 13 | 208 |
| 56 | 14 | 224 |
| 60 | 15 | 240 |
| 64 | 16 | 256 |
| 72 | 18 | 288 |
| 80 | 20 | 320 |
| 96 | 24 | 384 |
In Tailwind, spacing is n × 0.25rem (the --spacing variable defaults to 0.25rem), so p-4 is 1rem. The px column follows the root font-size set above. The listed steps are the classic scale; the table on the left shows - when the size does not land on one of them.
Generate a clamp() that grows the size linearly between two viewports and locks at the ends. The output uses rem + vw, with the formula shown step by step.
The maximum size is less than twice the minimum. Adrian Roselli notes that with vw or a clamped size the user may be unable to reach 200% (WCAG 1.4.4, level AA), and suggests never letting the maximum fall below twice the base size. This does not guarantee conformance either way: test at 200% and 300% zoom.
1) Slope = (24 - 16) ÷ (1280 - 320) = 0.008333 font px per screen px
2) In vw: 0.008333 × 100 = 0.8333vw
3) Fixed term = 16 - 0.008333 × 320 = 13.3333px = 0.8333rem (÷ 16)
4) Preferred value = 0.8333rem + 0.8333vw, locked between minimum and maximum by clamp()
Pure vw ignores the user font-size preference and can break text zoom. That is why the fixed term is in rem: it follows the preference, and only the part that grows with the screen uses vw.
| Viewport | px | rem |
|---|---|---|
| 320px | 16 | 1 |
| 375px | 16.46 | 1.029 |
| 414px | 16.78 | 1.049 |
| 768px | 19.73 | 1.233 |
| 1024px | 21.87 | 1.367 |
| 1280px | 24 | 1.5 |
| 1440px | 24 | 1.5 |
| 1920px | 24 | 1.5 |
Fluid typography changes size with the screen
Size now: 20px (1.25rem)
The box uses the same formula with cqw (container unit) in place of vw, so the browser resolves the clamp() for real. On narrow screens the box cannot exceed the screen width.
Each step is the base size multiplied by the ratio raised to the step number. Pick a ratio, see the scale in rem and px, and export it as CSS variables or Tailwind.
Samples stop at 72px to fit the screen; the table values are the real ones.
:root {
--text-7xl: 5.9605rem;
--text-6xl: 4.7684rem;
--text-5xl: 3.8147rem;
--text-4xl: 3.0518rem;
--text-3xl: 2.4414rem;
--text-2xl: 1.9531rem;
--text-xl: 1.5625rem;
--text-lg: 1.25rem;
--text-base: 1rem;
--text-sm: 0.8rem;
--text-xs: 0.64rem;
}Convert breakpoints from px to em and generate the media queries. Paste a list (one per line, or comma separated) in px, em or rem.
| px | em | Condition | Copy |
|---|---|---|---|
| 640 | 40 | (min-width: 40em) | |
| 768 | 48 | (min-width: 48em) | |
| 1024 | 64 | (min-width: 64em) | |
| 1280 | 80 | (min-width: 80em) | |
| 1536 | 96 | (min-width: 96em) |
@media (min-width: 40em) {
/* ... */
}
@media (min-width: 48em) {
/* ... */
}
@media (min-width: 64em) {
/* ... */
}
@media (min-width: 80em) {
/* ... */
}
@media (min-width: 96em) {
/* ... */
}The Media Queries specification says relative units in media queries are based on the initial value, never on declarations in the page. In HTML, em is the initial font-size defined by the browser or the user preferences, not what your CSS declares.
Practical consequence: em and rem are the same inside a media query, and an html with font-size: 62.5% does not change the breakpoints. If the user raises the default font size, em breakpoints rise with it and the layout switches earlier, which is usually what you want for larger text.
Range syntax (width >= 48em, width < 48em) removes the need for the 0.02px trick, because the upper bound is exclusive. It only works in recent browsers; check support before using it in production.
rem is a multiple of the root element font-size, which by default is the font size set in the browser (16px in the default setting). If the person raises that size in settings, everything in rem grows with it. A px value stays fixed, whatever the preference. Pick the browser default below and compare.
Does the text adapt to the reader?
font-size: 16px results in 16px, always
Does the text adapt to the reader?
font-size: 1rem results in 16px
Browser zoom (Ctrl and +) enlarges the whole page, including what is in px. The font-size preference, available in browsers like Chrome, Edge and Firefox, only affects what is relative (rem, em, %).