px, rem ve em: Hangi CSS Birimini Ne Zaman Kullanmalı
px vs rem vs em: When to Use Which CSS Unit
Choosing between px, rem, and em for CSS sizing — particularly font sizes and spacing — is one of the most debated decisions in front-end development. Each unit has a distinct behavior, a distinct relationship to its context, and a distinct impact on accessibility. Getting this right isn't just about convention; it has measurable effects on whether users who rely on browser zoom and font size settings can use your site comfortably.
This guide covers what each unit actually does, when each shines, and gives you a set of clear rules to follow.
px: Absolute, Predictable, and Sometimes Wrong
The px unit in CSS is an absolute unit — 1px is always 1px, regardless of any parent element or browser setting. It maps to a physical device pixel (or a logical pixel on high-DPI displays).
/* px: predictable, context-independent */
.box {
font-size: 16px;
padding: 12px 16px;
border-radius: 6px;
border: 1px solid #ccc;
}
h1 {
font-size: 48px;
line-height: 56px;
letter-spacing: -0.5px;
}
When px is Right
Borders and dividers. A 1px border should always be 1px, regardless of font size or zoom level. Using em or rem for borders would make them grow when the user zooms in — which is wrong behavior.
/* Correct: px for borders */
.card {
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 8px;
}
/* Correct: px for very small, precise decorative values */
.separator {
height: 1px;
background-color: #e5e7eb;
}
Box shadows. Shadow values that should remain crisp and stable regardless of text size:
/* Correct: px for box shadows */
.elevated {
box-shadow:
0 1px 3px rgba(0, 0, 0, 0.12),
0 1px 2px rgba(0, 0, 0, 0.24);
}
Fixed/absolute positioning offsets. When you need an element to be exactly N pixels from the edge of its positioned ancestor:
/* Correct: px for fixed positioning */
.tooltip {
position: absolute;
top: 4px;
left: 0;
}
Icon sizes in SVG or image contexts. A 16x16 icon should be 16x16:
.icon {
width: 16px;
height: 16px;
}
When px is Wrong: The Accessibility Problem
The fundamental problem with px for font sizes is that it overrides the user's browser default font size.
Most browsers allow users to set a default font size larger than 16px. Users with low vision, or those simply who prefer larger text, set this to 20px or 24px. When they visit a site where font sizes are set in px, their preference is ignored entirely.
/* Bad: px ignores user's browser default font size */
html { font-size: 16px; }
body { font-size: 16px; }
p { font-size: 16px; }
No matter how the user has configured their browser, these elements will be 16px. This violates WCAG 2.1 Success Criterion 1.4.4 (Resize Text).
/* Bad: px for font sizes throughout */
h1 { font-size: 32px; }
h2 { font-size: 24px; }
p { font-size: 16px; }
/* When user has their browser set to 20px default:
h1 = still 32px (should be 40px)
p = still 16px (should be 20px) */
The rule is clear: do not use px for font sizes or spacing that should scale with user preferences.
rem: Relative to Root, Best for Font Sizes
The rem unit (root em) is relative to the font size of the html element — the root of the document. If html has a font size of 16px (the browser default), then 1rem = 16px, 1.5rem = 24px, 0.875rem = 14px.
/* rem: relative to html font size */
h1 { font-size: 3rem; } /* 48px if html is 16px */
h2 { font-size: 2rem; } /* 32px */
h3 { font-size: 1.5rem; } /* 24px */
p { font-size: 1rem; } /* 16px */
.small { font-size: 0.875rem; } /* 14px */
.xs { font-size: 0.75rem; } /* 12px */
The Correct Pattern for Respecting User Preferences
/* Correct: do NOT set html font-size in px */
/* Let the browser use its default (typically 16px) */
/* WRONG */
html { font-size: 16px; } /* Freezes the scale — disables user preference */
/* ALSO WRONG — same effect */
html { font-size: 100%; font-size: 16px; } /* The second declaration wins */
/* CORRECT: set nothing on html, or set a percentage */
/* The browser default is used, and user can override it */
/* If you want to adjust the base, use a percentage */
html {
font-size: 112.5%; /* 18px if browser default is 16px, but scales with user preference */
}
The key insight: never set html { font-size: Npx; }. This defeats the rem system entirely by anchoring everything to a fixed pixel value. The point of rem is that it inherits from the user's browser setting.
rem for Spacing
rem is also excellent for spacing (padding, margin, gap) that should scale proportionally with the site's typographic scale:
/* Spacing in rem — scales with the type system */
.section {
padding: 4rem 2rem;
}
.card {
padding: 1.5rem;
margin-bottom: 1.5rem;
border-radius: 0.5rem;
}
h2 {
font-size: 2rem;
margin-top: 3rem;
margin-bottom: 1rem;
}
When a user has their browser set to a larger font size, these spacing values also grow, keeping the layout proportional. This is the right behavior.
em: Relative to Parent, Best for Component Spacing
The em unit is relative to the font-size of the element it's applied to (or, for font-size itself, relative to the parent element's font-size).
/* em behavior */
.parent {
font-size: 20px;
}
.child {
font-size: 0.8em; /* 16px — 80% of parent's 20px */
padding: 0.5em; /* 8px — 50% of child's own 16px */
margin-bottom: 1em; /* 16px — 100% of child's own 16px */
}
This context-sensitivity is both em's greatest strength and its biggest source of confusion. The compound nesting behavior is why em is generally not recommended for font sizes in large stylesheets — it becomes very hard to predict.
/* Compounding problem */
.level-1 { font-size: 0.8em; } /* 12.8px (80% of 16px) */
.level-2 { font-size: 0.8em; } /* 10.24px (80% of 12.8px) */
.level-3 { font-size: 0.8em; } /* 8.19px (80% of 10.24px) */
Each nested level compounds, producing unintended results. This doesn't happen with rem, which always refers back to the root.
Where em Excels: Component-Internal Spacing
em is ideal when spacing should scale proportionally with the element's own font size. This is perfect for components that may be used at different sizes:
/* Button component: spacing scales with button font size */
.btn {
display: inline-flex;
align-items: center;
padding: 0.6em 1.2em; /* Scales with the button's text size */
font-size: 1rem; /* Set the base size in rem */
border-radius: 0.3em; /* Scales with font size */
gap: 0.4em;
}
/* Small variant: just change font-size, all em values scale */
.btn--sm { font-size: 0.875rem; } /* All padding/radius/gap scale down */
.btn--lg { font-size: 1.125rem; } /* All padding/radius/gap scale up */
This pattern is extremely powerful for component systems. Define spacing in em, set the base font-size in rem, and resizing the component is a single-property change.
/* Another example: badge/chip component */
.badge {
font-size: 0.75rem; /* rem: respects user preferences */
padding: 0.25em 0.75em; /* em: proportional to badge's own font-size */
border-radius: 1em; /* em: always perfectly rounded */
line-height: 1.5;
}
/* Input fields */
.input {
font-size: 1rem; /* rem: body text size */
padding: 0.5em 0.75em; /* em: scales with input's font-size */
border-radius: 0.25em;
}
em for Media Queries
One often-overlooked best practice: use em for media query breakpoints, not px.
/* px breakpoints — don't respect browser zoom */
@media (min-width: 768px) { ... }
/* em breakpoints — scale with user's zoom level */
@media (min-width: 48em) { ... } /* 768px at default zoom */
@media (min-width: 64em) { ... } /* 1024px at default zoom */
When a user has zoomed in to 200%, px breakpoints fire at the same viewport size as if there were no zoom. em breakpoints take the zoom level into account, so the layout switches to a narrower mode at the zoomed viewport — which is often exactly what you want.
When to Use Which: Clear Rules
Here is a definitive set of rules to follow. Following these consistently eliminates most sizing decisions and produces accessible, maintainable CSS.
Use rem for:
/* Font sizes — always rem */
h1 { font-size: 2.5rem; }
h2 { font-size: 2rem; }
h3 { font-size: 1.5rem; }
p { font-size: 1rem; }
/* Spacing that should scale with the type system */
.section { padding: 4rem 2rem; }
.card { padding: 1.5rem; }
.stack > * { margin-bottom: 1rem; }
/* Gap in grid/flex layouts */
.grid { gap: 1.5rem; }
/* Max-width limits */
.content { max-width: 70rem; }
/* Breakpoints in media queries */
@media (min-width: 48rem) { ... }
Use em for:
/* Component-internal spacing (where scaling with component size matters) */
.btn {
padding: 0.6em 1.2em;
border-radius: 0.3em;
gap: 0.5em;
}
/* Icon size relative to text */
.icon {
width: 1em;
height: 1em;
}
/* Letter-spacing (should scale with font-size) */
.label {
letter-spacing: 0.08em;
text-indent: -0.5em;
}
/* word-spacing */
p {
word-spacing: 0.05em;
}
Use px for:
/* Borders */
.card { border: 1px solid rgba(0, 0, 0, 0.12); }
/* Box shadows */
.elevated { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); }
/* Outline (focus rings) */
:focus-visible { outline: 2px solid blue; outline-offset: 2px; }
/* Fixed/precise decorative values */
.divider { height: 1px; }
.avatar { width: 40px; height: 40px; }
/* SVG dimensions */
svg { width: 24px; height: 24px; }
/* Tiny pixel-perfect adjustments */
.icon { transform: translateY(-1px); }
Decision flowchart
- Is it a font size? → rem
- Is it spacing that should be proportional to the current element's text size (component padding, border-radius)? → em
- Is it spacing that should be part of the global layout scale (page margins, section padding, grid gaps)? → rem
- Is it a border, shadow, or fixed structural dimension? → px
- Is it a media query breakpoint? → em
Accessibility: Why rem Matters for Zoom
WCAG 2.1 Success Criterion 1.4.4 requires that text can be resized up to 200% without loss of content or functionality. There are two mechanisms users have:
Browser zoom (Ctrl/Cmd + scroll): Zooms the entire page, scaling everything proportionally. All unit types scale with this because the browser multiplies the viewport. This works regardless of which units you use.
Browser default font size (Settings → Appearance → Font size): Allows users to increase just the base font size. This is what rem and em respond to, and what px ignores.
/* Scenario: user has set browser font size to 24px (150% of default 16px) */
/* px: font size stays 16px — user preference ignored */
p { font-size: 16px; }
/* → 16px regardless of user preference */
/* rem: font size scales to 24px — user preference respected */
p { font-size: 1rem; }
/* → 24px because 1rem = user's chosen base of 24px */
Testing Accessibility
To test whether your site respects user font size preferences:
1. In Chrome: go to Settings → Appearance → Customize fonts → Font size
2. Change it from "Medium (16px)" to "Very Large (24px)"
3. Reload your site — text should be noticeably larger everywhere
4. If nothing changes, you likely have html { font-size: px } or are using px for font sizes
/* Accessible pattern: no px on html, rem everywhere */
html {
/* No font-size set — browser default (user's setting) is used */
box-sizing: border-box;
}
*, *::before, *::after {
box-sizing: inherit;
}
body {
font-family: 'Inter', system-ui, sans-serif;
font-size: 1rem; /* Inherits from html → respects user preference */
line-height: 1.6;
color: #1a1a2e;
}
The Myth: 62.5% Root Font Size
A common pattern sets html { font-size: 62.5%; } so that 1rem = 10px, making calculations easier:
/* The 62.5% trick — do not use */
html { font-size: 62.5%; } /* "1rem = 10px" */
h1 { font-size: 3.2rem; } /* "32px" */
p { font-size: 1.6rem; } /* "16px" */
This is technically accessible because it scales with user preferences (it's a percentage, not a px value), but it creates confusion: 1rem no longer means "one body text unit" — it means 10px at default zoom. The mental model breaks. More importantly, a user who has set their browser to 20px now gets 1rem = 12.5px instead of the expected 1rem = 20px. The math doesn't hold.
The better approach: keep html without a font-size declaration, set body to 1rem, and use your design tool's px-to-rem conversion (or a Sass function) if you need to convert design specs:
// Sass helper
@function rem($px) {
@return #{$px / 16}rem;
}
h1 { font-size: rem(32); } // → 2rem
p { font-size: rem(16); } // → 1rem
The px vs rem vs em question has clear answers once you understand what each unit is relative to. The single most impactful decision is to stop using px for font sizes and layout spacing — and to start using rem as your default sizing unit for typography. Everything else flows from there.