📏 CSS Units

Every size in CSS — font sizes, widths, margins, paddings — needs a unit. Choosing the right unit makes the difference between a layout that breaks on different screen sizes and one that adapts gracefully.

📝 Two categories

Absolute units — always the same physical size regardless of context (e.g. px).

Relative units — scale based on something else: a parent element, the root font size, or the viewport (e.g. rem, %, vw).

Absolute Units

Absolute units produce a fixed size. They are not affected by the user's browser font-size settings or screen size.

Unit Name Equivalent Use when
px Pixel 1/96 of 1 inch Borders, shadows, precise UI details
pt Point 1/72 of 1 inch Print stylesheets only
cm Centimeter 96/2.54 px Print stylesheets only
mm Millimeter 1/10 of 1 cm Print stylesheets only
in Inch 96 px Print stylesheets only
💡 Practical tip

For screen design, px is the only absolute unit you will realistically use. The others (pt, cm, mm, in) exist mainly for print CSS.

px — Pixels

The pixel is the most familiar CSS unit. On a standard screen, 1px equals one device pixel. On high-DPI (retina) screens the browser scales automatically, so your CSS pixels still look sharp.

Good uses of px
/* ✅ Borders — thin, precise lines */
.card {
  border: 1px solid #ddd;
  border-radius: 8px;
}

/* ✅ Box shadows — precise offsets */
.card {
  box-shadow: 0 2px 8px rgba(0,0,0,0.12);
}

/* ✅ Fixed-size icons or images */
.icon {
  width: 24px;
  height: 24px;
}

/* ❌ Avoid px for font sizes — blocks user zoom preferences */
body {
  font-size: 16px; /* works, but rem is better here */
}

Relative Units

Relative units are the foundation of responsive, accessible web design. Instead of being fixed, they scale based on another value.

% — Percentage

A percentage is relative to the parent element's corresponding property. For width/height it's the parent's width/height; for font-size it's the parent's font size.

.container {
  width: 800px;
}

/* This child will always be exactly half the parent */
.half {
  width: 50%; /* = 400px */
}

/* Common pattern: full-width sections */
.section {
  width: 100%;
  padding: 5%; /* padding also relative to parent WIDTH */
}

/* Two equal columns side by side */
.column {
  width: 50%;
  float: left;
}
⚠️ Percentage height gotcha

height: 50% only works if the parent has a defined height. If the parent's height is auto (default), the percentage is ignored.

/* ❌ Won't work — parent has no fixed height */
.parent { /* height: auto */ }
.child { height: 50%; } /* has no effect */

/* ✅ Works — parent height is defined */
.parent { height: 400px; }
.child { height: 50%; } /* = 200px */

em — Relative to the element's font size

1em equals the current element's font size. If no font size is set on the element, it inherits from the parent. This makes em great for component-level spacing that scales with text.

.button {
  font-size: 16px;

  /* em is relative to this element's font-size (16px) */
  padding: 0.75em 1.5em; /* = 12px 24px */
  border-radius: 0.25em;  /* = 4px */
}

.button--large {
  font-size: 20px;
  /* Padding now scales automatically: 15px 30px */
}
📝 Compounding effect

em compounds when nested. If a parent is 1.2em and a child is also 1.2em, the child ends up at 1.44em (1.2 × 1.2) relative to the root. This can cause unexpected sizes in deep nesting.

body   { font-size: 16px; }
.a     { font-size: 1.2em; } /* 19.2px */
.a .b  { font-size: 1.2em; } /* 23.04px — compounding! */

rem — Relative to the root font size

rem stands for root em. It is always relative to the <html> element's font size — never the parent. This avoids the compounding problem of em and is the recommended unit for font sizes and spacing.

/* Root font size (browser default is 16px) */
html {
  font-size: 16px; /* 1rem = 16px everywhere */
}

h1 { font-size: 2rem; }      /* 32px */
h2 { font-size: 1.5rem; }    /* 24px */
h3 { font-size: 1.25rem; }   /* 20px */
p  { font-size: 1rem; }       /* 16px */
small { font-size: 0.875rem; } /* 14px */

/* Spacing scale using rem */
.section {
  padding: 2rem;        /* 32px */
  margin-bottom: 1.5rem; /* 24px */
}
💡 Accessibility benefit

When a user increases their browser's default font size (e.g., from 16px to 20px), rem values scale with it — so your entire layout grows proportionally. px values stay fixed and ignore the user's preference. This is why rem is preferred for font sizes and layout spacing.

em vs rem comparison
html { font-size: 16px; }

.parent {
  font-size: 20px;
}

.child {
  font-size: 1.5em;  /* 30px — relative to parent (20px) */
  margin: 1.5rem;   /* 24px — always relative to html (16px) */
}

vw / vh — Viewport Width and Height

1vw = 1% of the viewport width.
1vh = 1% of the viewport height.
100vw = full width of the browser window.

/* Full-screen hero section */
.hero {
  width: 100vw;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Responsive heading that scales with viewport */
h1 {
  font-size: 5vw; /* on a 1200px screen = 60px */
}

/* Sticky sidebar with full viewport height */
.sidebar {
  height: 100vh;
  position: sticky;
  top: 0;
  overflow-y: auto;
}
⚠️ 100vh on mobile

On mobile browsers, 100vh includes the browser's address bar, which can cause content to be hidden behind it. Modern browsers added svh (small viewport height) and dvh (dynamic viewport height) to solve this:

.hero {
  height: 100vh;   /* classic — may overlap address bar */
  height: 100svh;  /* small viewport — excludes browser chrome */
  height: 100dvh;  /* dynamic — updates when address bar hides */
}

vmin / vmax

vmin is 1% of the smaller dimension (width or height).
vmax is 1% of the larger dimension.

/* A square that fits inside any screen orientation */
.square {
  width: 80vmin;
  height: 80vmin;
  /* Portrait 390×844: 80 × 3.9px = 312px */
  /* Landscape 844×390: 80 × 3.9px = 312px — same! */
}

/* Responsive font that avoids getting too big on wide screens */
h1 {
  font-size: 8vmin;
}

ch — Character width

1ch equals the width of the "0" (zero) character in the current font. It's ideal for sizing text inputs and readable line lengths.

/* Readable text width — 45–75 characters per line */
article {
  max-width: 65ch;
}

/* Form input sized to fit exactly a phone number */
input[type="tel"] {
  width: 12ch;
}

/* ZIP code field */
input[name="zip"] {
  width: 5ch;
}

ex — x-height

1ex equals the height of the lowercase letter "x" in the current font. Rarely used in practice, but useful for fine-tuning icon alignment with text.

/* Vertically center a small inline icon with text */
.icon {
  vertical-align: -0.15ex;
}

Quick Reference: Which Unit to Use?

Property Recommended unit Why
Font size (body) rem Respects user preferences, no compounding
Font size (component) rem or em em if you want it to scale with parent text
Padding / margin rem Consistent spacing scale across the site
Component spacing em Scales with the component's own font size
Layout widths % or rem % for fluid, rem for max-width
Full-screen sections vw / dvh Fills the viewport regardless of content
Borders, shadows px Should stay sharp and thin at all sizes
Border radius px or rem px for fixed roundness, rem to scale
Text input widths ch Directly matches the expected character count
Readable text column ch or rem max-width: 65ch is a proven reading width
Square that fits screen vmin Works in both portrait and landscape

Practical Patterns

Responsive Typography Scale

/* Set the base size once on html */
html {
  font-size: 16px; /* 1rem = 16px */
}

/* Scale up for large screens */
@media (min-width: 1200px) {
  html { font-size: 18px; } /* All rem values grow automatically */
}

h1 { font-size: 2.5rem; }
h2 { font-size: 2rem; }
h3 { font-size: 1.5rem; }
p  { font-size: 1rem; }

Fluid Viewport Font Size with clamp()

The clamp(min, preferred, max) function lets a value grow with the viewport but stay within limits.

/* Font grows from 1rem (mobile) to 1.5rem (desktop),
   scaling smoothly based on viewport width */
h1 {
  font-size: clamp(1.5rem, 4vw, 3rem);
  /*            min  preferred  max */
}

p {
  font-size: clamp(1rem, 1.5vw, 1.25rem);
}

Full-Height Hero Section

.hero {
  /* Fallback for older browsers */
  height: 100vh;

  /* Modern: avoids the mobile address-bar overlap */
  height: 100dvh;

  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 2rem;
}

Self-Contained Button Component

/* Using em for padding — scales with the button's own font-size */
.btn {
  font-size: 1rem;
  padding: 0.6em 1.4em;   /* 9.6px 22.4px */
  border: 2px solid currentColor;
  border-radius: 0.3em;
}

.btn--small {
  font-size: 0.875rem;
  /* padding/radius scale down automatically — no extra rules needed */
}

.btn--large {
  font-size: 1.25rem;
  /* padding/radius scale up automatically */
}

Readable Article Layout

article {
  /* ~65 characters wide — proven comfortable reading line length */
  max-width: 65ch;

  /* Centered with breathing room */
  margin: 0 auto;
  padding: 2rem 1rem;

  /* Comfortable line height */
  line-height: 1.6;
  font-size: 1.125rem;
}

Summary

📚 What you learned
  • px — fixed pixel; use for borders, shadows, icons
  • % — relative to parent; use for fluid layout widths
  • em — relative to element's font size; use for component-level spacing
  • rem — relative to root font size; use for font sizes and global spacing
  • vw / vh — relative to viewport; use for full-screen sections
  • dvh / svh — viewport height that handles mobile browser bars
  • vmin / vmax — relative to the smaller/larger viewport dimension
  • ch — width of "0"; use for input widths and readable text columns
  • clamp(min, fluid, max) combines units for smooth responsive scaling
  • Default to rem for fonts and px for thin decorative lines