Published 2026-02-10 · 2 min read
By Akash Yadav · Frontend Engineer
Best CSS Units for Responsive Design: Decision Matrix
Pick units with intent: REM for tokenized scale, % for liquid tracks, vw/vh for viewport-bound effects, container units for components, px for optical precision.
Decision framework
The best CSS unit for a given property depends on four questions:
- What is the value relative to? The root (rem), the parent (%, em), the viewport (vw, vh), the container (cqw), or nothing (px, pt)?
- Should it scale with user font-size preferences? If yes, avoid px.
- Should it track the viewport? If yes, vw/vh/dvh family.
- Should it track a component container? If yes, cqw/cqh with @container.
Answer these four questions for any property, and the right unit becomes obvious.
Quick decision matrix
| Property concern | Best unit(s) | Rationale |
|---|---|---|
| Body text, headings | rem | Respects root and user prefs |
| Component padding/gap | rem | Token-consistent, scales with type |
| Inline component spacing (badge, chip) | em | Tracks component font-size |
| Full-bleed section width | 100% or 100vw | % for layout tracks, vw for truly full-bleed |
| Full-screen overlays, modals | 100dvh | Avoids mobile chrome clip |
| Fluid hero sizing | clamp() with vw | Smooth interpolation |
| Component-internal sizing | cqw / cqh | Tracks container, not viewport |
| Grid track sizing | fr, minmax() | Intrinsic layout distribution |
| Borders, focus rings, hairlines | px | Sub-pixel precision |
| Print stylesheets | pt, mm, cm | Physical paper dimensions |
| Media query breakpoints | rem | Scales with user font preferences |
Hop to the canonical CSS unit converter hub for all conversion shortcuts in one place.
Typography: REM always
For font sizes, line-heights, and letter-spacing, REM is the correct default in all but a few edge cases.
/* Design system typography tokens */
:root {
--text-xs: 0.75rem; /* 12px */
--text-sm: 0.875rem; /* 14px */
--text-base: 1rem; /* 16px */
--text-lg: 1.25rem; /* 20px */
--text-xl: 1.5rem; /* 24px */
--text-2xl: 2rem; /* 32px */
--text-3xl: 3rem; /* 48px */
}
body { font-size: var(--text-base); line-height: 1.6; }
h1 { font-size: var(--text-3xl); line-height: 1.15; }
h2 { font-size: var(--text-2xl); line-height: 1.25; }
.card-title { font-size: var(--text-lg); line-height: 1.4; }
For fluid headings, wrap in clamp() using the fluid typography generator.
Layout: %, fr, and auto
Tracks, columns, and width constraints should express proportional relationships, not absolute measurements.
/* Sidebar + main: sidebar is fixed, main is flexible */
.page {
display: grid;
grid-template-columns: 260px 1fr;
}
/* Card grid: fill available width, min 260px per card */
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 1.5rem;
}
/* Constrained content: max-width with % margin for breathing room */
.container {
width: min(100% - 2rem, 72rem);
margin-inline: auto;
}
The min(100% - 2rem, 72rem) pattern is a one-liner for a responsive container: never wider than 72rem (1152px), always with at least 1rem padding on each side.
Viewport units: vw, vh, dvh
Viewport units should be reserved for elements whose size should directly match the browser canvas — heroes, overlays, sticky headers, and full-bleed sections.
/* Full-screen hero — use dvh to avoid iOS chrome issues */
.hero {
min-height: 100vh; /* fallback for older browsers */
min-height: 100dvh;
}
/* Full-bleed decorative section */
.full-bleed {
width: 100vw;
margin-inline: calc(50% - 50vw);
}
/* Fluid container padding — scales smoothly from mobile to wide */
.section {
padding-inline: clamp(1rem, 5vw, 4rem);
}
Avoid bare vw or vh for font sizes without clamp() bounds — text can become illegible at extreme viewport sizes.
Component units: cqw, cqh
Container units solve the long-standing problem of components that need to respond to their parent container rather than the viewport.
/* Define the container context */
.card-container {
container-type: inline-size;
}
/* Size internals relative to the card */
.card-title {
font-size: clamp(1rem, 4cqw, 1.5rem);
}
.card-image {
height: 50cqh;
object-fit: cover;
}
/* Apply @container queries for major layout changes */
@container (min-width: 400px) {
.card-layout {
display: grid;
grid-template-columns: 40% 1fr;
}
}
Use the PX to CQW converter to express a pixel measurement as a percentage of a known container width.
Worked examples
Full-bleed section title (fluid via clamp):
.section-title {
font-size: clamp(1.5rem, 1rem + 3vw, 3rem);
max-width: 20ch; /* readable line length */
}
Sidebar widget padding (REM tokens):
.widget {
padding: 1rem 1.5rem;
border-radius: 0.75rem;
gap: 0.75rem;
}
Video tile (aspect-ratio + object-fit):
.video-card {
aspect-ratio: 16 / 9;
overflow: hidden;
}
.video-card img {
width: 100%;
height: 100%;
object-fit: cover;
}
Simplify dimension fractions with the aspect ratio calculator.
CSS refactor: reconciling mixed units
If you inherit a codebase with mixed px/rem/em in the same file, reconcile using the CSS size calculator during refactor week to produce consistent token values.
Key takeaways
- REM for all typography — it is the accessible, token-consistent default.
- % and fr for layout tracks — they distribute space intrinsically, no magic numbers.
- dvh for full-screen elements — avoids the iOS
100vhmobile chrome bug. - cqw for component internals — lets components respond to their container, not the viewport.
- px for precision — borders, hairlines, focus rings. Intentional, not accidental.
- clamp() as the bridge — connects fixed minimums to fluid preferred values and hard maximums.
Frequently asked questions
- Where does % fail for height?
- When the percentage's reference is indefinite (auto-height parents, flex/grid items without explicit track sizes), % heights misbehave. Inspect the containing block before using % for height — use `min-height` or `height: fit-content` instead.
- Should I use vw or % for fluid widths?
- % is relative to the parent element's width — better for component-level fluid sizing. vw is relative to the viewport — better for full-bleed sections, overlays, and elements that should match the browser canvas regardless of parent width.
- Is px still valid in modern CSS?
- Yes. Borders (1px), focus rings (2px outline), hairlines, icon sizing in data-dense UIs, and some media queries are legitimate px use cases in 2026. The goal is intentional px, not zero px.
- What unit should I use for gap in CSS Grid?
- REM is the best default for gap — it scales with the design token system and respects user font-size preferences. For very large page-level gaps that should track viewport size, use clamp() with a vw component.
- How do I choose between em and rem for component padding?
- Use EM when the padding should scale proportionally if the component's own font-size changes (e.g., a badge where both text and padding scale together). Use REM for padding that should be consistent regardless of local font-size.
- When should I use fr units in CSS Grid?
- fr (fractional unit) distributes remaining space in a grid container after fixed and intrinsic tracks are sized. Use `1fr` for flexible columns that should share available space equally. Use `minmax(min-content, 1fr)` to prevent tracks from collapsing below their content minimum.