Responsive Design Cheatsheet
Standard Breakpoints
/* Mobile-first: base styles → enhance up */
/* Breakpoint tokens */
:root {
--bp-sm: 480px; /* Large phones */
--bp-md: 768px; /* Tablets */
--bp-lg: 1024px; /* Laptops */
--bp-xl: 1280px; /* Desktops */
--bp-2xl: 1536px; /* Wide screens */
}
/* Usage pattern: min-width = mobile first */
/* Small (base) — no media query needed */
.c-hero { font-size: 2rem; }
@media (min-width: 768px) { .c-hero { font-size: 3rem; } }
@media (min-width: 1024px) { .c-hero { font-size: 4rem; } }
Media Query Syntax
/* Width-based (most common) */
@media (min-width: 768px) { } /* Tablet and above */
@media (max-width: 767px) { } /* Below tablet (use sparingly with mobile-first) */
@media (min-width: 768px) and (max-width: 1023px) { } /* Tablet only */
/* Orientation */
@media (orientation: portrait) { }
@media (orientation: landscape) { }
/* Resolution / DPI */
@media (min-resolution: 2dppx) { } /* Retina / HiDPI */
@media (-webkit-min-device-pixel-ratio: 2) { } /* Safari fallback */
/* User Preferences */
@media (prefers-color-scheme: dark) { }
@media (prefers-color-scheme: light) { }
@media (prefers-reduced-motion: reduce) { }
@media (prefers-reduced-motion: no-preference) { }
@media (prefers-contrast: more) { }
@media (prefers-contrast: less) { }
@media (forced-colors: active) { } /* Windows High Contrast mode */
/* Hover: does the device support hover? */
@media (hover: hover) { } /* Mouse device — can show :hover styles */
@media (hover: none) { } /* Touch device — avoid hover-only interactions */
@media (pointer: fine) { } /* Mouse: precise pointer */
@media (pointer: coarse) { } /* Touch: larger touch targets needed */
/* Display mode (PWA) */
@media (display-mode: standalone) { } /* Installed as PWA */
@media (display-mode: browser) { }
Container Queries Syntax
Container queries respond to the parent container — not the viewport:
/* Step 1: Define a container */
.c-card-wrapper {
container-type: inline-size; /* Watches width */
container-name: card; /* Optional name */
}
/* Shorthand: container: name / type */
.c-card-wrapper { container: card / inline-size; }
/* Step 2: Query the container */
@container (min-width: 500px) {
.c-card { flex-direction: row; }
}
/* Named container query */
@container card (min-width: 400px) {
.c-card__title { font-size: var(--text-xl); }
}
/* Container types */
container-type: inline-size; /* Watch width only (most common) */
container-type: size; /* Watch width AND height */
container-type: normal; /* Reset — no queries (default) */
Viewport Units Reference
| Unit | Meaning | Use Case |
|---|---|---|
vw | % of viewport width | Fluid text: clamp(1rem, 4vw, 2rem) |
vh | % of viewport height | ⚠ Mobile bug: excludes browser bar |
dvh | Dynamic viewport height ✅ | Hero sections — accounts for mobile bar |
svh | Small viewport height | Safe minimum — assumes bar always visible |
lvh | Large viewport height | Assumes bar always hidden |
vmin | Smaller of vw/vh | Square that fits viewport |
vmax | Larger of vw/vh | Rare use |
Use 100dvh instead of 100vh for mobile full-screen sections.
Fluid Sizing: clamp() Patterns
/* clamp(minimum, ideal, maximum) */
/* Font sizes */
h1 { font-size: clamp(2rem, 5vw, 4.5rem); }
h2 { font-size: clamp(1.5rem, 3vw, 3rem); }
body { font-size: clamp(1rem, 1.5vw, 1.125rem); }
/* Spacing */
.l-section { padding-block: clamp(3rem, 8vw, 8rem); }
.o-container { padding-inline: clamp(1rem, 4vw, 3rem); }
gap: clamp(1rem, 3vw, 2rem);
/* Container width */
.o-container { width: min(100% - 2rem, 1200px); margin-inline: auto; }
Responsive Image Patterns
/* Fluid image — always fits container */
img { max-width: 100%; height: auto; display: block; }
/* Fixed aspect ratio, fills container */
.c-img-cover {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
object-position: center;
}
/* srcset in HTML — browser picks right size */
<img
src="img-800.jpg"
srcset="img-400.jpg 400w, img-800.jpg 800w, img-1200.jpg 1200w"
sizes="(min-width: 768px) 50vw, 100vw"
alt="Description"
loading="lazy"
>
<!-- Art direction: different crop for mobile vs desktop -->
<picture>
<source media="(min-width: 768px)" srcset="landscape.jpg">
<img src="portrait.jpg" alt="Description">
</picture>
Responsive Layout Patterns
/* ── Auto-filling card grid ── */
.o-card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 1.5rem;
}
/* ── Flex wrap: 3-up → 1-up ── */
.o-flex-grid { display: flex; flex-wrap: wrap; gap: 1.5rem; }
.o-flex-grid > * { flex: 1 1 280px; } /* Wraps below 280px */
/* ── Sidebar collapses on mobile ── */
.l-sidebar {
display: grid;
grid-template-columns: 1fr; /* Mobile: stacked */
}
@media (min-width: 768px) {
.l-sidebar { grid-template-columns: 280px 1fr; } /* Desktop: side by side */
}
/* ── Nav: hamburger on mobile, horizontal on desktop ── */
.c-nav__list {
display: none; /* Hidden on mobile */
}
.c-nav__list.is-open { display: flex; flex-direction: column; }
@media (min-width: 768px) {
.c-nav__list { display: flex; /* Always visible */ }
}
Nested Responsive (with Nesting)
/* CSS nesting: keep component responsive code together */
.c-card {
flex-direction: column;
@media (min-width: 640px) { flex-direction: row; }
.c-card__image {
aspect-ratio: 16 / 9;
@media (min-width: 640px) { width: 200px; aspect-ratio: 1; }
}
}
/* Container query inside component */
.c-card-wrapper { container-type: inline-size; }
.c-card {
flex-direction: column;
@container (min-width: 500px) { flex-direction: row; }
}