---
name: responsive-design
description: "Start with base styles for mobile, use min-width queries to layer complexity. Desktop-first (max-width) means mobile loads unnecessary styles first."
version: 1.10.0
---

# Responsive Design

## Mobile-First: Write It Right

Start with base styles for mobile, use `min-width` queries to layer complexity. Desktop-first (`max-width`) means mobile loads unnecessary styles first.

## Mobile-First Is a Strategy, Not a Media-Query Order

Responsive design adapts a desktop layout downward; mobile-first designs the small-screen experience upward. The difference shows in what gets cut:

| Criterion | Responsive (top-down) | Mobile-first (bottom-up) |
|---|---|---|
| Content priority | Layout is squeezed to fit | Non-essential content is cut at the design stage |
| Performance | Desktop scripts and assets often load hidden | Budgeted for the smallest device from the start |
| Touch | Hover behaviors retrofitted | Designed for touch and gestures natively |

Operational rules:

- **Full content parity.** Mobile users expect to complete the same tasks as on desktop. Organize content sequentially instead of removing it, and never ship a "view desktop site" escape hatch.
- **Every element pays rent.** Reading comprehension on mobile drops to roughly half of desktop for complex content (NN/g), and anything scrolled off-screen taxes short-term memory. Each decorative block has a real opportunity cost.
- **No false floors.** Full-width decorative images and banner-shaped blocks read as ads (banner blindness) and signal "end of page", stopping the scroll early. Cut them or make them unmistakably content.

## Breakpoints: Content-Driven

Don't chase device sizes—let content tell you where to break. Start narrow, stretch until design breaks, add breakpoint there. Three breakpoints usually suffice (640, 768, 1024px). Use `clamp()` for fluid values without breakpoints.

## Detect Input Method, Not Just Screen Size

**Screen size doesn't tell you input method.** A laptop with touchscreen, a tablet with keyboard—use pointer and hover queries:

```css
/* Fine pointer (mouse, trackpad) */
@media (pointer: fine) {
  .button { padding: 8px 16px; }
}

/* Coarse pointer (touch, stylus) */
@media (pointer: coarse) {
  .button { padding: 12px 20px; }  /* Larger touch target */
}

/* Device supports hover */
@media (hover: hover) {
  .card:hover { transform: translateY(-2px); }
}

/* Device doesn't support hover (touch) */
@media (hover: none) {
  .card { /* No hover state - use active instead */ }
}
```

**Critical**: Don't rely on hover for functionality. Touch users can't hover.

## Safe Areas: Handle the Notch

Modern phones have notches, rounded corners, and home indicators. Use `env()`:

```css
body {
  padding-top: env(safe-area-inset-top);
  padding-bottom: env(safe-area-inset-bottom);
  padding-left: env(safe-area-inset-left);
  padding-right: env(safe-area-inset-right);
}

/* With fallback */
.footer {
  padding-bottom: max(1rem, env(safe-area-inset-bottom));
}
```

**Enable viewport-fit** in your meta tag:
```html
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
```

## Responsive Images: Get It Right

### srcset with Width Descriptors

```html
<img
  src="hero-800.jpg"
  srcset="
    hero-400.jpg 400w,
    hero-800.jpg 800w,
    hero-1200.jpg 1200w
  "
  sizes="(max-width: 768px) 100vw, 50vw"
  alt="Hero image"
>
```

**How it works**:
- `srcset` lists available images with their actual widths (`w` descriptors)
- `sizes` tells the browser how wide the image will display
- Browser picks the best file based on viewport width AND device pixel ratio

### Picture Element for Art Direction

When you need different crops/compositions (not just resolutions):

```html
<picture>
  <source media="(min-width: 768px)" srcset="wide.jpg">
  <source media="(max-width: 767px)" srcset="tall.jpg">
  <img src="fallback.jpg" alt="...">
</picture>
```

## Layout Adaptation Patterns

**Navigation**: Three stages—hamburger + drawer on mobile, horizontal compact on tablet, full with labels on desktop. **Tables**: Transform to cards on mobile using `display: block` and `data-label` attributes. **Progressive disclosure**: Use `<details>/<summary>` for content that can collapse on mobile.

## Testing: Don't Trust DevTools Alone

DevTools device emulation is useful for layout but misses:

- Actual touch interactions
- Real CPU/memory constraints
- Network latency patterns
- Font rendering differences
- Browser chrome/keyboard appearances

**Test on at least**: One real iPhone, one real Android, a tablet if relevant. Cheap Android phones reveal performance issues you'll never see on simulators.

---

**Avoid**: Desktop-first design. Device detection instead of feature detection. Separate mobile/desktop codebases. Ignoring tablet and landscape. Assuming all mobile devices are powerful.

---

## Container queries

Media queries respond to the viewport. Container queries respond to the component's own available width, so the same component adapts correctly in a sidebar, a grid cell, or a full-bleed section without viewport-specific overrides.

```css
.card-list { container-type: inline-size; container-name: cards; }

.card { display: grid; gap: 0.5rem; }

@container cards (min-width: 30rem) {
  .card { grid-template-columns: 8rem 1fr; } /* switch to horizontal when the container is wide */
}
```

Container query length units (`cqi`, `cqw`, `cqb`) size type and spacing against the container:

```css
.card h3 { font-size: clamp(1rem, 4cqi, 1.5rem); }
```

Use container queries for reusable components placed in varying contexts. Keep media queries for page-level layout (global breakpoints, overall grid). Feature support is baseline across current browsers; for old engines the unqueried base styles remain the fallback.
