Understanding CSS Units: px, rem, em & More
Summary: Choosing the right CSS unit is one of the most impactful decisions you make for accessibility, responsiveness, and maintainability. This guide covers every CSS unit from px and rem to the new dynamic viewport and container query units, with a definitive px vs rem comparison, fluid typography patterns, and a conversion cheat sheet you can reference daily.
📑 Table of Contents
- 1. Absolute Units (px and Friends)
- 2. The rem Unit
- 3. The em Unit
- 4. px vs rem vs em — The Definitive Comparison
- 5. Percentage Units
- 6. Viewport Units (vw, vh, vmin, vmax)
- 7. Dynamic Viewport Units (dvh, svh, lvh)
- 8. Character & Line Units (ch, ex, lh)
- 9. Container Query Units (cqi, cqb)
- 10. Fluid Typography with clamp()
- 11. Responsive Design with Units
- 12. Units & Accessibility
- 13. Conversion Cheat Sheet
- 14. Common Unit Mistakes
- 15. FAQ
1. Absolute Units (px and Friends)
Absolute units have a fixed size regardless of parent elements, root font size, or viewport dimensions. The most important is px — but CSS actually defines several absolute units.
/* Absolute units */ width: 300px; /* Pixels — the most common absolute unit */ width: 1in; /* Inches: 1in = 96px */ width: 2.54cm; /* Centimetres: 1cm = 37.8px */ width: 25.4mm; /* Millimetres: 1mm = 3.78px */ width: 6pc; /* Picas: 1pc = 16px */ width: 72pt; /* Points: 1pt = 1.333px */
Understanding CSS Pixels
A CSS pixel (px) is not a device pixel. On a 2x Retina display, one CSS pixel maps to four device pixels (2×2). On a 3x display, it maps to nine. The browser handles this scaling transparently through the devicePixelRatio. This is why a 300px box looks the same physical size on a 1x laptop screen and a 3x phone screen — the browser renders it at 300, 600, or 900 device pixels respectively.
The other absolute units (in, cm, mm, pt, pc) are defined in terms of px on screens and don't correspond to physical measurements. A 1in element on screen is always 96px, not a literal inch. These units are primarily useful for print stylesheets (@media print) where they do map to physical sizes. For screen design, stick to px or — better — relative units.
For a deeper exploration of how CSS pixels relate to device pixels, the MDN CSS length reference provides excellent technical detail.
2. The rem Unit
rem stands for "root em" — it's relative to the font size of the <html> element. By default, browsers set the root font size to 16px, so 1rem = 16px, 0.5rem = 8px, 2rem = 32px.
/* rem is always relative to the root (html) font size */
html { font-size: 16px; } /* browser default */
h1 { font-size: 2rem; } /* 32px */
p { font-size: 1rem; } /* 16px */
small { font-size: 0.875rem; } /* 14px */
/* Spacing with rem */
.card {
padding: 1.5rem; /* 24px */
margin-bottom: 2rem; /* 32px */
border-radius: 0.75rem; /* 12px */
}
/* The 62.5% trick (controversial) */
html { font-size: 62.5%; } /* Makes 1rem = 10px */
h1 { font-size: 3.2rem; } /* 32px — easier math */
p { font-size: 1.6rem; } /* 16px */Why rem is the Modern Default
The critical advantage of rem is accessibility. Users with low vision often increase their browser's default font size from 16px to 20px, 24px, or higher. When your text is set in rem, it scales proportionally with this preference. When it's set in px, it stays fixed — ignoring the user's explicit accessibility preference.
The 62.5% trick (setting html { font-size: 62.5% } to make 1rem = 10px) simplifies mental math but has downsides: it breaks third-party components that assume a 16px root, and if you forget to explicitly size every text element, unstyled text will be 10px (unreadably small). Most modern projects avoid this trick and use the default 16px root.
Convert between px and rem instantly using Hue's unit converter tool.
3. The em Unit
em is relative to the font size of the current element (for font-size itself, it's relative to the parent's font size). This makes em contextual — its computed value changes depending on where in the DOM tree it's used.
/* em for font-size: relative to PARENT's font-size */
.parent { font-size: 20px; }
.child { font-size: 0.8em; } /* 16px (20 × 0.8) */
/* em for other properties: relative to ELEMENT's own font-size */
.button {
font-size: 1rem; /* 16px */
padding: 0.5em 1em; /* 8px 16px — scales with font-size */
}
.button-large {
font-size: 1.25rem; /* 20px */
padding: 0.5em 1em; /* 10px 20px — automatically larger */
}
/* The compounding problem */
.level-1 { font-size: 0.9em; } /* 14.4px */
.level-2 { font-size: 0.9em; } /* 12.96px (0.9 × 0.9 × 16) */
.level-3 { font-size: 0.9em; } /* 11.66px (0.9 × 0.9 × 0.9 × 16) */When em Shines
The em unit is ideal for properties that should scale proportionally with the element's font size. Padding and margin on buttons, icons sized relative to surrounding text, and media query breakpoints all benefit from em. A button with padding: 0.5em 1em maintains its proportions whether the font size is 14px or 24px — the padding scales automatically.
The Compounding Problem
The biggest footgun with em is compounding. Since font-size: em is relative to the parent, nesting elements with em-based font sizes creates a cascade where each level multiplies the previous one. Three levels of 0.9em doesn't give you 90% — it gives you 72.9% (0.9 × 0.9 × 0.9). This is the primary reason rem replaced em for font sizing — rem always references the root, so there's no compounding.
4. px vs rem vs em — The Definitive Comparison
This is the question every developer asks. Here's the definitive answer, broken down by use case.
| Property | Recommended | Why |
|---|---|---|
| font-size | rem | Respects user preferences, no compounding |
| padding / margin | rem or em | rem for consistency, em if it should scale with text |
| border / outline | px | Borders should stay crisp and fixed |
| border-radius | rem or px | rem if card should scale, px for fixed roundness |
| box-shadow | px | Shadow blur/offset is visual, shouldn't scale with text |
| width / max-width | rem, %, or ch | rem for containers, ch for text blocks, % for fluid |
| line-height | unitless | Unitless multiplier (1.5) avoids compounding |
| media queries | em | em-based breakpoints respect user zoom |
| icons next to text | em | Icon scales with the text it accompanies |
The Simple Rule
Use rem for most things. Use em when you want something to scale with its local font size (button padding, icon size). Use px for things that should stay fixed regardless of font size (borders, box shadows, thin lines). Use unitless values for line-height. Use % or viewport units for fluid layouts.
This isn't dogma — it's pragmatism. The WCAG 2.2 resize text criterion requires text to be resizable to 200% without loss of content. Using rem for font sizes and em for related spacing is the simplest way to pass this requirement without any extra work.
5. Percentage Units
Percentages are relative to the parent element's corresponding property. width: 50% is 50% of the parent's width. font-size: 120% is 120% of the parent's font size. The reference property changes depending on what you're setting.
/* Width/height: relative to parent's content box */
.container { width: 80%; } /* 80% of parent width */
.sidebar { width: 25%; }
.main { width: 75%; }
/* Font-size: relative to parent's font-size */
.large-text { font-size: 150%; } /* 1.5× parent font size */
/* Padding/margin: relative to parent's WIDTH (even vertical!) */
.card { padding: 5%; } /* 5% of parent width for ALL sides */
/* Transform: relative to element's own dimensions */
.centered {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%); /* 50% of own width/height */
}The most surprising behaviour: vertical padding and margin percentages are relative to the parent's width, not its height. This is actually useful — it's the basis for the classic aspect-ratio hack (padding-top: 56.25% for 16:9), though the modern aspect-ratio property has largely replaced this technique.
Percentages are foundational for fluid layouts and are used extensively in responsive design. Combined with max-width in rem or px, they create layouts that flex within bounds: width: 100%; max-width: 64rem;.
6. Viewport Units (vw, vh, vmin, vmax)
Viewport units are relative to the browser viewport (the visible area of the page). They're essential for full-screen sections, fluid typography, and viewport-relative sizing.
/* 1vw = 1% of viewport width */
/* 1vh = 1% of viewport height */
/* 1vmin = 1% of smaller dimension */
/* 1vmax = 1% of larger dimension */
/* Full-viewport hero section */
.hero {
height: 100vh;
width: 100vw;
}
/* Fluid heading that scales with viewport */
h1 {
font-size: 5vw; /* Scales from tiny to huge — needs clamping! */
}
/* Responsive square (same width and height) */
.square {
width: 50vmin;
height: 50vmin;
}
/* Safe fluid font-size with clamp */
h1 {
font-size: clamp(2rem, 5vw, 4rem);
}vmin and vmax
vmin uses the smaller of width/height, vmax uses the larger. On a 1200×800 viewport, 1vmin = 8px (1% of 800) and 1vmax = 12px (1% of 1200). These are useful for elements that should maintain proportions regardless of screen orientation — squares, circles, and proportional spacing.
The 100vh Mobile Problem
On mobile browsers, 100vh includes the area behind the address bar and navigation chrome. When the browser UI is visible, your "full height" section overflows and requires scrolling. This has been a notorious issue since mobile browsers first appeared. The solution is the new dynamic viewport units covered in the next section — or the classic workaround: height: 100%; min-height: -webkit-fill-available;.
7. Dynamic Viewport Units (dvh, svh, lvh)
The new viewport unit variants solve the mobile 100vh problem by accounting for the browser's dynamic UI (address bar, toolbar). These have excellent browser support as of 2025.
/* dvh — Dynamic Viewport Height */
/* Changes as the browser UI expands/collapses */
.hero { height: 100dvh; }
/* svh — Small Viewport Height */
/* The smallest possible viewport (browser UI fully visible) */
.safe-section { min-height: 100svh; }
/* lvh — Large Viewport Height */
/* The largest possible viewport (browser UI hidden) */
.max-section { height: 100lvh; }
/* Corresponding width variants also exist */
/* dvw, svw, lvw — same concept for width */
/* Dynamic viewport inline/block (writing-mode aware) */
/* dvi, svi, lvi — inline axis */
/* dvb, svb, lvb — block axis */Which One to Use?
- 100dvh — Best for hero sections and full-viewport layouts. It updates live as the browser chrome appears/disappears, so the section always fills the visible area exactly.
- 100svh — Best as a
min-heightvalue. It guarantees the section fills at least the smallest viewport, and content can extend beyond when the browser UI hides. - 100lvh — Use sparingly. It matches the viewport with all browser UI hidden, which means content may be clipped when the UI is visible. Useful for background elements that should fill the maximum possible space.
For responsive design patterns involving these units, the web.dev viewport units guide provides excellent visual explanations of how each variant behaves across devices. Test how your layout responds to different viewport sizes with tools like Clarity SEO, which flags mobile viewport issues alongside SEO recommendations.
8. Character & Line Units (ch, ex, lh)
These units are relative to typographic properties of the current font, making them ideal for text-centric layouts.
/* ch — width of the "0" character in the current font */
.prose {
max-width: 65ch; /* Ideal line length for reading */
}
.code-block {
max-width: 80ch; /* Standard terminal width */
}
/* ex — height of the lowercase "x" in the current font */
.icon-inline {
width: 1ex;
height: 1ex; /* Icon matches x-height of surrounding text */
}
/* lh — line-height of the element */
.gap-one-line {
margin-bottom: 1lh; /* Exactly one line of space */
}
/* cap — height of capital letters (newer) */
.drop-cap {
font-size: 3lh;
float: left;
}The Magic of ch for Line Length
The ch unit is the best way to constrain line length for readability. Research consistently shows that 45-75 characters per line is optimal for reading comprehension. max-width: 65ch creates this constraint automatically, and it adapts to the current font — a wider font will produce a wider container, maintaining the character count. This is why ch is superior to a fixed max-width in px or rem for text containers.
Note that ch measures the width of "0", not the average character width. For proportional fonts, the actual character count at 65ch will be higher (roughly 75-80 characters for typical body fonts). Adjust accordingly. Explore how different fonts affect line length with Hue's typography tool.
9. Container Query Units (cqi, cqb)
Container query units are the newest addition to CSS units, and they're a game changer for component-based design. Instead of being relative to the viewport, they're relative to the size of a container element.
/* Define a container */
.card-wrapper {
container-type: inline-size;
container-name: card;
}
/* Container query units */
/* 1cqi = 1% of container's inline size (width) */
/* 1cqb = 1% of container's block size (height) */
/* 1cqw = 1% of container's width */
/* 1cqh = 1% of container's height */
/* 1cqmin = smaller of cqi/cqb */
/* 1cqmax = larger of cqi/cqb */
/* Fluid typography relative to container */
.card-title {
font-size: clamp(1rem, 4cqi, 2rem);
}
/* Container query with @container */
@container card (min-width: 400px) {
.card-body {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2cqi;
}
}
@container card (min-width: 600px) {
.card-title {
font-size: 5cqi;
}
}Container query units solve a fundamental problem with viewport units: a component in a narrow sidebar and the same component in a wide main content area get the same vw value. With cqi, the sidebar instance uses its narrow container width and the main-content instance uses its wide container width. The component adapts to its context, not the page.
Browser support is excellent as of 2025 — Chrome 105+, Firefox 110+, Safari 16+. This covers 95%+ of users. Container queries are one of the most significant CSS features in years, enabling truly portable, responsive components. Use them alongside Hue's spacing tool to visualise how spacing scales within containers.
10. Fluid Typography with clamp()
Fluid typography scales text smoothly between a minimum and maximum size based on the viewport width — no media queries needed. The clamp() function is the modern way to achieve this.
/* clamp(minimum, preferred, maximum) */
/* Body text: 1rem minimum, scales with viewport, 1.25rem max */
body {
font-size: clamp(1rem, 0.95rem + 0.25vw, 1.25rem);
}
/* H1: scales from 2rem to 4rem */
h1 {
font-size: clamp(2rem, 1rem + 3vw, 4rem);
}
/* H2: scales from 1.5rem to 2.5rem */
h2 {
font-size: clamp(1.5rem, 1rem + 2vw, 2.5rem);
}
/* Fluid spacing too */
.section {
padding: clamp(1.5rem, 4vw, 4rem);
}
/* Full fluid type scale */
:root {
--text-xs: clamp(0.75rem, 0.7rem + 0.2vw, 0.875rem);
--text-sm: clamp(0.875rem, 0.8rem + 0.25vw, 1rem);
--text-base: clamp(1rem, 0.95rem + 0.25vw, 1.125rem);
--text-lg: clamp(1.125rem, 1rem + 0.5vw, 1.5rem);
--text-xl: clamp(1.5rem, 1.2rem + 1vw, 2rem);
--text-2xl: clamp(2rem, 1.5rem + 2vw, 3rem);
--text-3xl: clamp(2.5rem, 1.5rem + 3vw, 4rem);
}The formula inside clamp() typically follows the pattern rem + vw. The rem component provides a stable base that respects user font preferences, and the vw component adds viewport-relative scaling. The Utopia fluid type calculator generates these values for you — input your min/max viewport and font sizes, and it outputs the clamp formulas.
Accessibility note: Always use rem (not px) for the minimum and maximum values. If the minimum is in px, it won't scale when a user increases their browser font size, violating WCAG resize requirements. The minimum value in clamp() is not the same as a CSS min — it's the mathematical floor of the function. If the user's rem makes the preferred value larger than the max, the max wins. But if the min is in rem, at least the floor scales with user preferences.
11. Responsive Design with Units
Choosing the right units is central to responsive design. Here's how different units fit into a responsive strategy.
Layout Units
/* Fluid container with max-width */
.container {
width: 100%;
max-width: 72rem; /* 1152px — stops growing on large screens */
margin: 0 auto;
padding: 0 clamp(1rem, 3vw, 3rem); /* fluid horizontal padding */
}
/* CSS Grid with fluid columns */
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(min(100%, 20rem), 1fr));
gap: clamp(1rem, 2vw, 2rem);
}
/* Sidebar layout with clamp */
.sidebar {
width: clamp(200px, 25%, 350px);
}Breakpoints in em
Media query breakpoints should use em, not px. When a user zooms the page (which scales the effective rem/em size), em-based breakpoints trigger at the right visual threshold. Pixel-based breakpoints don't account for zoom, meaning a zoomed-in user might get a desktop layout crammed into a phone-width view.
/* Breakpoints in em */
@media (min-width: 30em) { /* 480px at default zoom */ }
@media (min-width: 48em) { /* 768px at default zoom */ }
@media (min-width: 64em) { /* 1024px at default zoom */ }
@media (min-width: 80em) { /* 1280px at default zoom */ }
@media (min-width: 96em) { /* 1536px at default zoom */ }
/* These scale with user zoom — at 200% zoom:
64em triggers at 2048 device px instead of 1024,
so the mobile layout appears when appropriate */This is one of the most overlooked responsive design best practices. Frameworks like Tailwind use px-based breakpoints by default, which is a pragmatic trade-off (easier to reason about). But for maximum accessibility, em-based breakpoints are technically superior.
12. Units & Accessibility
Unit choice has a direct impact on accessibility. WCAG 2.2 requires that text can be resized to 200% without loss of content or functionality (Success Criterion 1.4.4). Your unit choices determine whether your site passes or fails this criterion.
How Users Resize Text
Users resize text in two ways: browser zoom (Ctrl/Cmd + Plus) and default font size (browser settings → font size). Browser zoom scales everything proportionally — px, rem, em all scale. But changing the default font size only affects rem and em. If your text is in px, users who change their default font size to 24px still see 16px text on your site.
The Accessibility Impact of px vs rem
/* ❌ Bad: ignores user font-size preferences */
body { font-size: 16px; }
h1 { font-size: 32px; }
/* ✅ Good: respects user preferences */
body { font-size: 1rem; }
h1 { font-size: 2rem; }
/* ✅ Better: fluid with accessibility floor */
h1 { font-size: clamp(1.75rem, 1rem + 3vw, 3rem); }
/* ❌ Bad: fixed container clips text when enlarged */
.card { width: 300px; overflow: hidden; }
/* ✅ Good: container grows with text */
.card { max-width: 20rem; overflow: visible; }About 3-5% of users modify their default browser font size. That sounds small, but it represents millions of people — many with low vision who depend on this setting to use the web. Using rem for font sizes costs you nothing and serves these users correctly. It's the single highest-impact accessibility improvement you can make with units.
Test your site by changing your browser's default font size to 24px and navigating normally. If text overlaps, containers clip, or layouts break, your unit choices need work. Hue's unit converter can help you convert px-based designs to rem values.
13. Conversion Cheat Sheet
Reference table for converting between common CSS units. Based on the default root font size of 16px.
| px | rem | em (at 16px parent) | % (of 16px) |
|---|---|---|---|
| 8px | 0.5rem | 0.5em | 50% |
| 10px | 0.625rem | 0.625em | 62.5% |
| 12px | 0.75rem | 0.75em | 75% |
| 14px | 0.875rem | 0.875em | 87.5% |
| 16px | 1rem | 1em | 100% |
| 18px | 1.125rem | 1.125em | 112.5% |
| 20px | 1.25rem | 1.25em | 125% |
| 24px | 1.5rem | 1.5em | 150% |
| 28px | 1.75rem | 1.75em | 175% |
| 32px | 2rem | 2em | 200% |
| 40px | 2.5rem | 2.5em | 250% |
| 48px | 3rem | 3em | 300% |
| 64px | 4rem | 4em | 400% |
Quick Formulas
px to rem: px ÷ 16 = rem (e.g., 24 ÷ 16 = 1.5rem) rem to px: rem × 16 = px (e.g., 1.5 × 16 = 24px) px to em: px ÷ parent-px = em (e.g., 24 ÷ 20 = 1.2em) vw to px: vw × viewport ÷ 100 (e.g., 5vw × 1440 ÷ 100 = 72px)
Bookmark this table or use Hue's unit converter tool for instant conversions between any CSS units, including custom root font sizes.
14. Common Unit Mistakes
❌ Using px for all font sizes
This ignores user font-size preferences and fails WCAG 1.4.4. Switch to rem for font sizes. It's the single most important unit change you can make.
❌ Using em for font-size in nested components
Em compounds through nesting. A 0.875em font-size inside another 0.875em gives you 0.766em (76.6%), not 87.5%. Use rem for font sizes and em only for properties that should scale with the local font size (padding, margins).
❌ Using 100vh for full-height sections on mobile
100vh doesn't account for mobile browser chrome, causing overflow. Use 100dvh for dynamic full-height, or min-height: 100svh for safe minimum full-height.
❌ Using vw for font-size without clamp()
font-size: 5vw creates text that's unreadably small on phones and absurdly large on 4K monitors. Always clamp viewport-based font sizes: font-size: clamp(1rem, 3vw, 3rem).
❌ Using line-height with units
line-height: 24px or line-height: 1.5rem creates a fixed line height that doesn't adapt to font-size changes. Use the unitless value: line-height: 1.5. The unitless multiplier is inherited as a ratio, not a computed value.
❌ Mixing unit systems inconsistently
Using px for some spacing, rem for others, and em for a few more creates unpredictable layouts when users zoom or change font sizes. Pick a primary unit system (rem-based is best) and use it consistently. Reserve px for fixed-size elements like borders and shadows.
15. Frequently Asked Questions
Should I use px or rem for font sizes?
Use rem for font sizes. Rem units respect the user's browser font-size preference, which is critical for accessibility. When a user increases their default font size from 16px to 20px, rem-based text scales up while px-based text stays fixed, making it unreadable for low-vision users.
What is the difference between em and rem?
rem is relative to the root element's font size (<html>), which defaults to 16px. em is relative to the parent element's font size, which means it compounds — an em inside an em inside an em creates increasingly large or small values. Rem is predictable; em cascades. Use rem for font sizes, em for properties that should scale with local text (button padding, icon sizes).
When should I use viewport units (vw, vh)?
Use vw for fluid typography (inside clamp()) and full-width layouts. Use dvh (not vh) for full-viewport-height sections — 100vh doesn't account for mobile browser chrome. Use vmin for elements that should maintain aspect ratio across orientations.
What is clamp() and how does it work?
clamp(min, preferred, max) returns the preferred value, clamped between min and max. For fluid typography, font-size: clamp(1rem, 2.5vw, 2rem) means the text scales with viewport width but never goes below 1rem or above 2rem. Use the Utopia calculator to generate clamp values.
What are container query units (cqi, cqb)?
Container query units are relative to a container element's size, not the viewport. 1cqi equals 1% of the container's inline size (width in horizontal writing modes). They enable component-level responsive design where elements adapt to their container context, not the page. Supported in Chrome 105+, Firefox 110+, Safari 16+.
Why does 100vh not work properly on mobile?
On mobile browsers, 100vh equals the maximum viewport height — including the area behind the address bar. When the address bar is visible, content overflows. Use 100dvh (dynamic viewport height) which accounts for the actual visible area, or 100svh for the smallest viewport height as a safe minimum.