CSS Reference Guide

All TopicsPlayground

Responsive Design

Building websites that adapt to any screen size, from mobile phones to ultra-wide monitors. Master the techniques that make layouts flexible and content accessible everywhere.

LayoutMobileMedia QueriesEssential

Viewport Meta Tag

The single most important line for responsive design. Without it, mobile browsers render the page at a virtual desktop width (~980px) and then scale it down.

HTML
<!-- Required in every responsive page's <head> -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
Attribute Value Purpose
width device-width Match the screen's width in CSS pixels
initial-scale 1.0 No zoom on page load
maximum-scale 1.0 Prevent zoom (avoid this for accessibility!)
user-scalable no Disable pinch-to-zoom (avoid this!)
Never disable user zoom

Avoid maximum-scale=1 and user-scalable=no. These break accessibility by preventing users with low vision from zooming in. Many accessibility guidelines (WCAG) require that zoom is not disabled.

Mobile-First vs Desktop-First

Two approaches to writing responsive CSS. Mobile-first is the industry standard and recommended approach.

CSS
/* ==============================
   MOBILE-FIRST (recommended)
   Base styles = mobile
   Add complexity as screen grows
   Uses: min-width
   ============================== */

/* Base: mobile styles (no media query needed) */
.container {
  padding: 1rem;
  font-size: 0.875rem;
}

@media (min-width: 768px) {
  .container {
    padding: 2rem;
    font-size: 1rem;
  }
}

@media (min-width: 1024px) {
  .container {
    padding: 3rem;
    max-width: 1200px;
    margin: 0 auto;
  }
}
CSS
/* ==============================
   DESKTOP-FIRST (legacy approach)
   Base styles = desktop
   Remove complexity as screen shrinks
   Uses: max-width
   ============================== */

/* Base: desktop styles */
.container {
  padding: 3rem;
  max-width: 1200px;
  margin: 0 auto;
}

@media (max-width: 1023px) {
  .container {
    padding: 2rem;
    max-width: none;
  }
}

@media (max-width: 767px) {
  .container {
    padding: 1rem;
  }
}
Why Mobile-First Wins

1. Performance: Mobile devices get only the CSS they need; desktop enhancements load progressively. 2. Simplicity: It is easier to add layout complexity than to undo it. 3. Future-proof: Most web traffic is mobile, so start there.

Media Queries

The core mechanism for responsive design. Apply CSS rules conditionally based on viewport characteristics.

CSS
/* Basic syntax */
@media (min-width: 768px) {
  /* styles for 768px and wider */
}

@media (max-width: 767px) {
  /* styles for 767px and narrower */
}

/* Combining with AND */
@media (min-width: 768px) and (max-width: 1023px) {
  /* tablet only */
}

/* OR (comma-separated) */
@media (max-width: 480px), (orientation: portrait) {
  /* small screens OR portrait orientation */
}

/* NOT */
@media not print {
  /* everything except print */
}

/* Media types */
@media screen { }   /* screens only */
@media print { }    /* print only */
@media all { }      /* all media (default) */
Live Example — Resize to See Changes
Try resizing your browser window:

Common Breakpoints

Standard breakpoints used by major CSS frameworks and design systems. These are starting points — adjust based on your content.

Name Min-Width Typical Devices CSS Variable
xs 0px Small phones (base styles)
sm 576px Large phones (landscape) --bp-sm: 576px
md 768px Tablets --bp-md: 768px
lg 1024px Small laptops, tablets landscape --bp-lg: 1024px
xl 1280px Desktops --bp-xl: 1280px
2xl 1536px Large desktops, ultra-wide --bp-2xl: 1536px
CSS
/* Mobile-first breakpoints (most common) */
/* Base: 0 - 575px (mobile) */

@media (min-width: 576px)  { /* sm: landscape phones */ }
@media (min-width: 768px)  { /* md: tablets */ }
@media (min-width: 1024px) { /* lg: laptops */ }
@media (min-width: 1280px) { /* xl: desktops */ }
@media (min-width: 1536px) { /* 2xl: large screens */ }
Content-Based Breakpoints

Rather than targeting specific devices, set breakpoints where your content naturally breaks. Resize the browser and add a breakpoint when the layout starts looking awkward. This future-proofs your design against new device sizes.

Modern Range Syntax

Modern CSS allows comparison operators in media queries, making them more readable and less error-prone.

CSS
/* Old syntax */
@media (min-width: 768px) { }
@media (max-width: 1023px) { }
@media (min-width: 768px) and (max-width: 1023px) { }

/* NEW range syntax (supported in all modern browsers) */
@media (width >= 768px) { }
@media (width < 1024px) { }
@media (768px <= width < 1024px) { }

/* Also works with height */
@media (height >= 600px) { }
@media (400px <= height <= 800px) { }
Why Range Syntax is Better

The traditional min-width: 768px and max-width: 767px gap can cause issues with fractional pixels. The range syntax (width >= 768px and width < 768px) eliminates this 1px overlap problem entirely. Supported in all browsers since late 2023.

Feature & Preference Queries

Media queries go beyond screen size. Detect user preferences, device capabilities, and more.

prefers-color-scheme

CSS
/* Match the user's OS dark/light preference */
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0f172a;
    --text: #e2e8f0;
  }
}

@media (prefers-color-scheme: light) {
  :root {
    --bg: #ffffff;
    --text: #1e293b;
  }
}

prefers-reduced-motion

CSS
/* Respect users who prefer reduced motion */
@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* Progressive approach: only animate for users who are OK with it */
@media (prefers-reduced-motion: no-preference) {
  .element {
    transition: transform 0.3s ease;
  }
}

orientation

CSS
@media (orientation: portrait) {
  /* taller than wide */
}

@media (orientation: landscape) {
  /* wider than tall */
}

Other Useful Feature Queries

CSS
/* High-resolution (retina) displays */
@media (min-resolution: 2dppx) { }

/* Hover capability (touchscreens vs mouse) */
@media (hover: hover) {
  /* device has a mouse/pointer that can hover */
  .card:hover { transform: translateY(-4px); }
}

@media (hover: none) {
  /* touchscreen — no hover capability */
}

/* Pointer precision */
@media (pointer: coarse) {
  /* touch — make tap targets larger */
  button { min-height: 44px; }
}

/* Prefers contrast */
@media (prefers-contrast: high) {
  .card { border: 2px solid; }
}
Accessibility First

Always implement prefers-reduced-motion and prefers-color-scheme in production sites. These are not nice-to-haves — they are essential for users with vestibular disorders and visual sensitivities. Use the progressive enhancement approach: add motion for users who want it.

Responsive Units

Choose the right unit for the right purpose. Each responsive unit has specific strengths.

Unit Relative To Best For
% Parent element Widths, fluid layouts
vw 1% of viewport width Full-width sections, fluid typography
vh 1% of viewport height Full-height sections, hero banners
dvh Dynamic viewport height Mobile full-height (accounts for address bar)
svh Small viewport height Smallest possible viewport (address bar visible)
lvh Large viewport height Largest possible viewport (address bar hidden)
rem Root font size (usually 16px) Spacing, font sizes, consistent scaling
em Parent element's font size Component-local spacing, media queries
CSS
/* Responsive units in action */
.hero {
  height: 100dvh;     /* full viewport on mobile (dynamic) */
  padding: 5vw;       /* scales with viewport width */
}

.sidebar {
  width: 25%;         /* 25% of parent */
}

.container {
  max-width: 75rem;   /* 1200px at default font size */
  padding: 2rem;       /* scales if user changes font size */
}
vh on mobile is broken

On mobile browsers, 100vh includes the area behind the address bar, causing content to be hidden. Use 100dvh (dynamic viewport height) instead, which adjusts when the address bar shows/hides. Alternatively use 100svh for the smallest viewport size.

Fluid Typography with clamp()

Create text that scales smoothly between a minimum and maximum size based on the viewport width. No breakpoints needed.

CSS
/* clamp(minimum, preferred, maximum) */
h1 {
  font-size: clamp(1.5rem, 4vw, 3rem);
  /*
    - Never smaller than 1.5rem (24px)
    - Scales with viewport at 4vw
    - Never larger than 3rem (48px)
  */
}

h2 {
  font-size: clamp(1.25rem, 3vw, 2rem);
}

p {
  font-size: clamp(0.875rem, 1.5vw, 1.125rem);
}

/* Fluid spacing too! */
section {
  padding: clamp(1rem, 5vw, 4rem);
}
Live Example — Fluid Typography with clamp()

This Heading Scales Fluidly

This paragraph text also scales smoothly between 0.8rem and 1.1rem based on the viewport width. Try resizing your browser to see the text size change continuously without any breakpoint jumps.

font-size: clamp(0.8rem, 1.5vw, 1.1rem)

clamp() Formula Cheat Sheet

A useful formula: clamp(min, preferred, max) where preferred = min + (max - min) * ((100vw - min-viewport) / (max-viewport - min-viewport)). For practical use, try: clamp(1rem, 0.5rem + 2vw, 2rem). The 0.5rem + 2vw creates a more controlled scaling curve than vw alone.

Responsive Images

Images that adapt to their container and look great on all screen sizes.

CSS
/* The essential responsive image rule */
img {
  max-width: 100%;     /* never overflow container */
  height: auto;          /* maintain aspect ratio */
  display: block;        /* remove inline spacing */
}

/* object-fit: control how images fill their box */
.card-image {
  width: 100%;
  height: 200px;
  object-fit: cover;     /* fill box, crop excess */
  object-fit: contain;   /* fit inside box, may letterbox */
  object-fit: fill;      /* stretch to fill (distorts) */
  object-fit: none;      /* natural size, may crop */
  object-position: center top;  /* position within the box */
}

/* aspect-ratio: maintain proportions */
.video-container {
  width: 100%;
  aspect-ratio: 16 / 9;  /* always 16:9 */
}

.avatar {
  width: 80px;
  aspect-ratio: 1;       /* perfect square */
  border-radius: 50%;     /* circle */
  object-fit: cover;
}
Live Example — object-fit Comparison

object-fit: cover

Fills & crops

object-fit: contain

Fits inside

aspect-ratio: 16/9

16:9
Always Set aspect-ratio on Images

Set aspect-ratio on images to prevent layout shift while they load. The browser reserves the correct space before the image downloads, resulting in a much smoother loading experience (better CLS score).

Responsive Grid Pattern

The most powerful responsive pattern: a CSS Grid that auto-adjusts columns without any media queries.

CSS
/* The auto-responsive grid — no media queries! */
.auto-grid {
  display: grid;
  grid-template-columns: repeat(
    auto-fit,
    minmax(min(100%, 300px), 1fr)
  );
  gap: 1.5rem;
}

/*
  How it works:
  - auto-fit: create as many columns as fit
  - min(100%, 300px): prevents overflow on small screens
  - 1fr: columns grow to fill available space

  Result:
  - Wide screen: 4+ columns
  - Medium screen: 2-3 columns
  - Small screen: 1 column
  - ALL with zero media queries!
*/
Live Example — Auto-Responsive Grid (Resize Browser!)
Design

Adaptive layouts

Develop

Clean code

Deploy

Ship fast

Iterate

Improve always

min() Inside minmax()

Using minmax(min(100%, 300px), 1fr) instead of just minmax(300px, 1fr) prevents horizontal overflow on screens smaller than 300px. The min() function picks whichever is smaller: 100% of the container or 300px.

Container Queries

The biggest addition to responsive CSS since media queries. Style elements based on their container's size, not the viewport. Perfect for reusable components.

CSS
/* Step 1: Define a containment context */
.card-wrapper {
  container-type: inline-size;  /* enable container queries */
  container-name: card;          /* optional: give it a name */
}

/* Shorthand */
.card-wrapper {
  container: card / inline-size;
}

/* Step 2: Write container queries */
@container card (min-width: 400px) {
  .card {
    display: flex;
    gap: 1rem;
  }
}

@container card (min-width: 600px) {
  .card {
    font-size: 1.25rem;
  }
}

/* Without name: queries nearest container ancestor */
@container (min-width: 300px) {
  .card-title { font-size: 1.5rem; }
}
Live Example — Container Queries Concept
Narrow container
Card Title
Stacked layout
Wide container
Card Title
Horizontal layout in wider container

Same component, different layout based on container width (not viewport).

Container Query Units

Container queries come with new units: cqw (1% of container width), cqh (1% of container height), cqi (1% of inline size), cqb (1% of block size). Use them for truly component-relative sizing: font-size: 5cqi.

Responsive Navigation Pattern

The classic responsive nav: horizontal links on desktop, hamburger menu on mobile.

CSS
/* Mobile-first navigation */
.nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  padding: 1rem;
}

.nav-logo {
  font-weight: 700;
  font-size: 1.25rem;
}

.nav-toggle {
  display: block;           /* visible on mobile */
  background: none;
  border: none;
  font-size: 1.5rem;
  cursor: pointer;
}

.nav-links {
  display: none;             /* hidden on mobile */
  width: 100%;
  flex-direction: column;
  gap: 0.5rem;
  padding-top: 1rem;
}

.nav-links.open {
  display: flex;             /* show when toggled */
}

@media (min-width: 768px) {
  .nav-toggle {
    display: none;           /* hide hamburger on desktop */
  }

  .nav-links {
    display: flex;           /* always visible on desktop */
    width: auto;
    flex-direction: row;
    gap: 1.5rem;
    padding-top: 0;
  }
}
Live Example — Desktop Navigation
Brand
Home Products About Blog Contact
Live Example — Mobile Navigation (Expanded)
Brand
Home Products About Blog Contact

Gotchas

Forgetting the Viewport Meta Tag

Without <meta name="viewport" content="width=device-width, initial-scale=1.0">, no amount of media queries will make your site responsive on mobile. This is the number one cause of "my media queries don't work on phones."

Using px in Media Queries

Media queries with px don't respond to user zoom settings. Consider using em for breakpoints: @media (min-width: 48em) instead of 768px. This respects the user's browser font-size setting.

Horizontal Scroll on Mobile

Common causes: elements with fixed widths wider than the viewport, 100vw including scrollbar width, absolute positioning going off-screen. Debug with * { outline: 1px solid red; } to find the overflow culprit. Quick fix: body { overflow-x: hidden; } (but fix the root cause).

100vw Includes the Scrollbar

100vw includes the width of the vertical scrollbar, which can cause horizontal overflow. Use width: 100% instead of width: 100vw for full-width elements, or use the newer 100dvw unit.

Touch Targets Too Small

Apple and Google both recommend minimum 44x44px touch targets. Small buttons and links are a major mobile usability issue. Always test with @media (pointer: coarse) to increase target sizes on touch devices.

CSS
/* Ensure accessible touch targets */
@media (pointer: coarse) {
  button,
  a,
  input,
  select {
    min-height: 44px;
    min-width: 44px;
  }
}

Pro Tips

The "Intrinsic Design" Approach

Modern responsive design is moving beyond breakpoints. Combine clamp(), min(), max(), CSS Grid with auto-fit/minmax(), and container queries to create layouts that are intrinsically responsive — they adapt without a single media query.

CSS
/* A fully responsive layout with ZERO media queries */
.page {
  max-width: min(90%, 1200px);
  margin: 0 auto;
  padding: clamp(1rem, 5vw, 3rem);
}

.page h1 {
  font-size: clamp(1.5rem, 4vw, 3rem);
}

.card-grid {
  display: grid;
  grid-template-columns: repeat(
    auto-fit,
    minmax(min(100%, 300px), 1fr)
  );
  gap: clamp(0.75rem, 2vw, 1.5rem);
}
Test on Real Devices

Browser DevTools device emulation is useful but imperfect. Real devices have different rendering engines, touch behaviors, and viewport quirks (especially iOS Safari). Use services like BrowserStack for comprehensive testing, and always test on at least one real phone.

Use Logical Properties for Internationalization

Instead of margin-left, use margin-inline-start. Instead of width, use inline-size. Logical properties automatically adapt to different writing modes (RTL languages, vertical text), making your responsive design truly global.

CSS
/* Physical (breaks in RTL) */
.sidebar {
  margin-left: 2rem;
  padding-right: 1rem;
  border-left: 3px solid blue;
}

/* Logical (works in all directions) */
.sidebar {
  margin-inline-start: 2rem;
  padding-inline-end: 1rem;
  border-inline-start: 3px solid blue;
}
Performance Budget

Responsive images are the biggest performance opportunity. Use the HTML <picture> element or srcset attribute to serve appropriately sized images. A 2000px hero image on a 375px phone wastes enormous bandwidth. Use loading="lazy" for below-the-fold images.

HTML
<!-- Responsive images with srcset -->
<img
  src="image-800.jpg"
  srcset="image-400.jpg 400w,
         image-800.jpg 800w,
         image-1200.jpg 1200w"
  sizes="(max-width: 600px) 100vw,
        (max-width: 1200px) 50vw,
        33vw"
  alt="Description"
  loading="lazy"
>
PreviousCSS Grid