Suggest an editImprove this articleRefine the answer for “CSS units: px, rem, em”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**CSS units px, rem, and em** differ in their reference point. `px` is fixed at an absolute size. `em` multiplies the parent element's font-size and compounds on nesting. `rem` multiplies the root `html` font-size and stays consistent anywhere in the document. ```css html { font-size: 16px; } .text { font-size: 1.5rem; } /* 24px, always */ .box { font-size: 1.5em; } /* depends on parent */ ``` **Key rule:** `rem` for typography and spacing, `px` for borders.Shown above the full answer for quick recall.Answer (EN)Image**CSS units px, rem, and em** define what a size value actually references: a fixed pixel, the parent font-size, or the root font-size. ## Theory ### TL;DR - `px` is a brick: fixed size, unchanged regardless of parent or root - `em` is relative to the parent element's font-size and compounds on nesting - `rem` is relative to the root `html` font-size and stays consistent anywhere - Core problem with em: three levels of 1.2em = 1.728em (1.2³), not 1.2em - Decision rule: `rem` for almost everything, `px` for borders and shadows, `em` when padding should scale with the component's own font-size ### Quick example ```css html { font-size: 16px; } /* rem base */ .parent { font-size: 2rem; } /* 32px */ .child-em { font-size: 1.5em; } /* 48px - multiplied by parent's 32px */ .child-rem { font-size: 1.5rem; } /* 24px - multiplied by root's 16px */ .child-px { font-size: 20px; } /* 20px - always 20px */ ``` `.parent` has 2rem (32px). Its `child-em` at 1.5em resolves to 48px, not 24px. `rem` ignores the parent entirely and reads the root. ### Key difference The reference point is everything. `px` has none - it is always the same number. `em` multiplies its parent's computed font-size, so deep nesting creates compound growth. `rem` always reads the `html` element's computed font-size, so 1.5rem is 24px whether the element sits at the top of the page or inside five nested components. ### When to use - **Typography and spacing (most cases)**: `rem` scales with browser and OS font preferences - **Borders, box-shadow, outlines**: `px` keeps edges crisp and does not benefit from scaling - **Padding or margin proportional to component font-size**: `em`, like `padding: 0.75em` on a button that grows and shrinks with its own text - **Deeply nested menus or components**: `rem` over `em` to avoid compound sizing math - **1px lines and small icons**: `px` only ### Comparison table | Aspect | px | em | rem | |---|---|---|---| | Reference | Fixed (1/96 inch) | Parent font-size | Root (`html`) font-size | | Nesting effect | None | Compounds (1.2em x 1.2em = 1.44em) | Consistent | | User zoom | Fixed | Scales with parent | Scales from root | | Accessibility | Ignores font prefs | Tracks parent chain | Respects root prefs | | Best for | Borders, shadows | Component-local ratios | Typography, spacing, layout | ### How the browser handles this Browsers resolve units during the cascade. `html` font-size is computed first (default 16px unless overridden). `rem` reads that root value directly. `em` walks up the style tree to find the parent's computed font-size, then multiplies. Both convert to absolute pixels before layout begins. `px` skips all of it. ### Common mistakes **Using em for site-wide spacing** ```css /* breaks in nested structures */ .menu { font-size: 1.2em; } .menu li { font-size: 1.2em; } /* 1.44em already */ .menu li a { font-size: 1.2em; } /* 1.728em - unintended */ /* fix */ .menu li a { font-size: 1rem; } ``` Three levels of 1.2em gives 1.728 times the base. That breaks layouts in ways that are hard to trace back. **Using px for body text** ```css /* wrong: user font preferences are ignored */ body { font-size: 16px; } /* right: scales with user and OS settings */ body { font-size: 1rem; } ``` WCAG requires text to scale with user font preferences. `px` blocks that. It is an accessibility issue, not just a style preference. **Using em for borders** ```css /* fine */ button { border: 1px solid #333; font-size: 1rem; } /* looks off at large font sizes */ button { border: 0.1em solid #333; } ``` Borders need a fixed, crisp edge. `em` on a border makes it thicker or thinner as the component font-size changes, which is rarely the intent. ### Real-world usage - Tailwind CSS: `p-4` = 1rem, border classes use `px` - Bootstrap 5: `$spacer: 1rem` base, fixed elements in `px` - Material-UI (MUI): theme typography with `h1: { fontSize: '2rem' }` - Chakra UI: rem-based spacing tokens across the board ### Follow-up questions **Q:** What is the browser default root font-size? **A:** 16px. Users can change it in browser settings. You can check the actual computed value with `getComputedStyle(document.documentElement).fontSize`. **Q:** Does viewport meta affect rem? **A:** Indirectly. `width=device-width` sets the logical pixel base on mobile. rem still resolves from `html` font-size, but the overall layout scales to fit the viewport. **Q:** Why use px for box-shadow instead of rem? **A:** Shadows in `rem` can look blurry at non-standard zoom levels. `px` keeps them sharp because shadow rendering happens at the pixel layer, not the font layer. **Q:** In CSS-in-JS (Emotion, Styled Components), how do you handle rem with a dynamic root? **A:** Read the root value at runtime with `getComputedStyle(document.documentElement).fontSize`, then convert px to rem programmatically. Most theme utilities in those libraries handle this automatically. **Q:** Browser zoom scales px too. So what is the actual accessibility difference? **A:** Browser zoom scales everything, including `px`. But OS-level font size settings (not zoom) only affect relative units like `rem`. A user who sets their system font to 20px benefits from `rem`, not from `px`. ## Examples ### em compounding breaks nested nav ```css :root { font-size: 16px; } .nav { font-size: 1.125rem; } /* 18px */ .nav > li { font-size: 1.2em; } /* 21.6px */ .nav > li > a { font-size: 1.1em; /* 23.76px - ballooned unintentionally */ padding: 0.5em; /* 11.88px - also unpredictable */ } /* fix: use rem for inner elements */ .nav > li > a { font-size: 1rem; /* 16px - predictable */ padding: 0.5rem; /* 8px - predictable */ } ``` Two levels of em nesting pushed the font past 23px. Switching inner elements to `rem` keeps everything tied to the root, not to whoever sits above in the DOM tree. ### rem-based button that respects user preferences ```css :root { font-size: 16px; } .btn { font-size: 1rem; /* 16px, scales with root */ padding: 0.75rem 1.5rem; /* 12px top/bottom, 24px sides */ border-radius: 4px; /* px - sharp corners */ border: 1px solid #333; /* px - crisp 1px line */ } ``` If a user sets their browser base to 20px, the button font and padding scale automatically. The border stays at 1px, which is exactly right for a thin dividing line. I have seen codebases where everything was in `px`, and the team spent days debugging why accessibility users with large-font settings got a broken layout. Switching typography to `rem` fixed most of it in one line.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.