Skip to main content

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:

  1. What is the value relative to? The root (rem), the parent (%, em), the viewport (vw, vh), the container (cqw), or nothing (px, pt)?
  2. Should it scale with user font-size preferences? If yes, avoid px.
  3. Should it track the viewport? If yes, vw/vh/dvh family.
  4. 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 concernBest unit(s)Rationale
Body text, headingsremRespects root and user prefs
Component padding/gapremToken-consistent, scales with type
Inline component spacing (badge, chip)emTracks component font-size
Full-bleed section width100% or 100vw% for layout tracks, vw for truly full-bleed
Full-screen overlays, modals100dvhAvoids mobile chrome clip
Fluid hero sizingclamp() with vwSmooth interpolation
Component-internal sizingcqw / cqhTracks container, not viewport
Grid track sizingfr, minmax()Intrinsic layout distribution
Borders, focus rings, hairlinespxSub-pixel precision
Print stylesheetspt, mm, cmPhysical paper dimensions
Media query breakpointsremScales 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 100vh mobile 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.