📏

px, rem, em

px, rem, and em are CSS measurement units for defining font sizes, spacing, and dimensions. px is absolute (screen pixels), rem is relative to the root font size, and em is relative to the parent element's font size.

px (pixels) is the most common absolute unit. font-size: 16px means exactly 16 CSS pixels, regardless of context. It's predictable and straightforward, but ignores user preferences (like text zoom) and hinders responsive design. Using it exclusively breaks accessibility for people with low vision who increase browser font size.

rem (root em) is relative to the font-size of the root element (html). If root is 16px (browser default), 1rem = 16px, 2rem = 32px. Changing root font-size scales the entire design proportionally. This respects accessibility preferences and facilitates theming. It's the recommended unit for typography and spacing in modern design systems.

em is relative to the direct parent element's font-size. If a div has font-size: 20px and a child uses padding: 1em, padding will be 20px. Ems multiply in cascade: if a div has font-size: 1.2em inside another div with 1.5em, result is 1.2 × 1.5 = 1.8em from ancestor. This can be useful for modular components but confusing without discipline.

Most browsers have root font-size of 16px by default. You can change it with html { font-size: 18px; } or use percentages: font-size: 112.5%; (18px if default is 16px). Design systems often define root in rem to scale everything: html { font-size: 62.5%; } makes 1rem = 10px, easing math (1.6rem = 16px).

Mental conversion: if root is 16px, divide px by 16 to get rem. 24px = 1.5rem, 32px = 2rem, 12px = 0.75rem. With root at 10px (62.5%), conversion is direct: 18px = 1.8rem. For em, depends on parent: if parent is 20px, 1em = 20px, 0.5em = 10px.

In media queries, always use em or rem, never px. Browsers interpret em in media queries relative to the default 16px (not altered root). Example: @media (min-width: 48em) equals 768px if 16px is base. This respects browser zoom: if a user zooms 200%, a px breakpoint doesn't scale, but an em one does.

Use rem for: base font sizes, layout spacing (margins, paddings), component sizes that should scale with user preferences. Example: h1 { font-size: 2.5rem; margin-bottom: 1rem; }. This ensures if someone increases text zoom, entire design responds.

Use em for: component internal spacing that should be proportional to component's text. Example: buttons with padding: 0.5em 1em; automatically adjust if button has font-size: 1.2rem or 0.875rem. Also useful in media queries and line-height (line-height: 1.5em or better unitless: 1.5).

Use px for: borders (border: 1px solid), small shadows, details that shouldn't scale (fixed icons, dividers). Also in cases where you need pixel-perfect control, like grid or image alignment. Avoid px for typography and main spacing: breaks accessibility.

Rule of thumb: rem by default, em for modular components, px only when absolute is required. Systems like Tailwind CSS use rem for almost everything: text-base = 1rem, p-4 = 1rem padding.

Mistake 1: Using px for everything. This ignores that users may have different accessibility settings. A pure-px design fails WCAG 2.1 (criterion 1.4.4 Resize Text). Migrate to rem/em to meet standards and improve UX.

Mistake 2: Nesting em without control. If you have 5 levels of font-size: 1.2em nested, text at the last level will be huge (1.2^5 ≈ 2.5x). Use rem to avoid cascade multiplication, or em only at one depth level.

Mistake 3: Not defining root explicitly. Assuming root is 16px works until a user or framework changes it. Declare your intent: html { font-size: 100%; } or the specific value you need. Document the base in your design system.

Good practice: use CSS variables for scales. --font-size-base: 1rem; --spacing-unit: 0.5rem; This way you change entire scale by editing one variable. Tools like clamp() combine well with rem for fluid typography: font-size: clamp(1rem, 2vw, 1.5rem); scales between 16-24px based on viewport, respecting rem.

Examples

  • html { font-size: 16px; } → body { font-size: 1rem; } = 16px
  • html { font-size: 10px; } → h1 { font-size: 3.2rem; } = 32px
  • div { font-size: 20px; } → span { padding: 0.5em; } = 10px padding
  • button { font-size: 1rem; padding: 0.5em 1em; } = 16px font, 8px 16px padding (if root is 16px)
  • @media (min-width: 48em) = breakpoint at 768px (if base is 16px)

FAQ

Why not use px if it's easier?

px ignores user accessibility preferences. If someone configures their browser for larger text (common in people with low vision or older adults), a pure-px site doesn't respond. Additionally, responsive design is simpler with rem: changing a breakpoint or typography scale is editing one variable, not finding and replacing hundreds of px values.

rem or em for media queries?

Both work, but em is more common because in media queries it calculates relative to browser default (16px), not root. Using em ensures breakpoints scale with browser zoom. Example: <code>@media (min-width: 64em)</code> is 1024px but if user zooms 150%, breakpoint also scales. px in media queries doesn't scale with zoom.

How do I migrate a project from px to rem without breaking everything?

Start by defining <code>html { font-size: 62.5%; }</code> (1rem = 10px) for easy conversion. Use find and replace with regex for common values: 16px → 1.6rem, 24px → 2.4rem. Prioritize typography and layout spacing, leave borders and details in px for now. Test with browser zoom at 200% and 50% to validate everything scales. Tools like PostCSS have automatic conversion plugins.