Reference · UX pattern catalogueResponsive patterns

22 patterns in this category — each with its illustration, what it is, and the fields that matter when you build or review one. Part of the UX pattern catalogue; the raw, agent-readable version is ux-pattern-catalogue/responsive-patterns.json.

Strategy

Base CSS min-width: 768px min-width: 1024px Enhance progressively →

Mobile-First CSS responsive-001

Write base CSS for the smallest screen, then add @media (min-width) queries to enhance for larger viewports. Mobile-first forces content prioritization — you start with what matters most and add enhancements, rather than hiding content on mobile.

Implementation

Base styles target mobile (no media query). Add `@media (min-width: 768px)` for tablet, `@media (min-width: 1024px)` for desktop. Each query adds complexity, never removes it. Use the design system breakpoint tokens.

Accessibility

Mobile-first ensures core content is accessible on all devices. Content hidden via `display: none` at certain breakpoints must not contain focusable elements — use `visibility: hidden` or remove from DOM instead.

overflow! 620px breakpoint Break where content breaks

Content-Driven Breakpoints responsive-002

Add breakpoints where the design breaks, not at arbitrary device widths. If a layout looks wrong at 620px, add a breakpoint at 620px. Device-based breakpoints (iPhone, iPad) become outdated as new devices launch.

Implementation

Test layouts by slowly resizing the browser. Where content overflows, line lengths become unreadable, or layouts collapse — add a breakpoint there. Standard breakpoints (768px, 1024px) are starting points, not rules.

Accessibility

Content-driven breakpoints ensure readability at every viewport width. Target 45-75 characters per line for body text — add breakpoints or adjust font size when lines get too long or too short.

Semantic HTML (works everywhere) CSS Grid, Flex, Clamp JS enhancements Animations, WebGL enhance

Progressive Enhancement responsive-003

Start with a functional baseline that works everywhere (semantic HTML + core CSS), then layer on enhancements (Grid, animations, WebGL) for capable browsers. Every user gets a working experience; modern browsers get a richer one.

Implementation

Use `@supports` to test for CSS feature support before applying advanced layouts. Use `@media (hover: hover)` to add hover effects only on devices with pointer input. Use `@media (prefers-reduced-motion: no-preference)` to gate animations.

Accessibility

Progressive enhancement is inherently accessible — the baseline works with assistive technology. Enhanced features should never break the accessible baseline.

Breakpoints

360 Mobile 768 Tablet 1024 Desktop 1440 Large sm md lg Starting points, not rules

Standard Breakpoints responsive-004

Four standard viewport breakpoints cover the vast majority of devices: 360px (small mobile minimum), 768px (tablet/large phone landscape), 1024px (desktop), and 1440px (large desktop/content max-width). Use these as starting points, then adjust based on content needs.

Implementation

Define as CSS custom properties (for documentation) and `@media` queries. `--breakpoint-sm: 640px`, `--breakpoint-md: 768px`, `--breakpoint-lg: 1024px`, `--breakpoint-xl: 1280px`. Note: CSS custom properties cannot be used directly in media queries.

Accessibility

Breakpoints should never hide essential content. If content is visible at one breakpoint, its functionality should be reachable at all breakpoints — even if the presentation changes.

Sidebar (200px) Main (600px) Same component, different container @container (min-width: 400px)

Container Queries responsive-005

Component behavior depends on its container width, not the viewport. A card component adapts differently in a sidebar (300px) vs main content (800px) using the same code. Container queries make components truly reusable across layout contexts.

Implementation

Set `container-type: inline-size` on the parent wrapper. Use `@container (min-width: 400px) { ... }` to style children based on container width. Name containers with `container-name` for specificity. 95%+ browser support (2024+).

Accessibility

No specific accessibility impact beyond standard responsive concerns. Container queries improve component reusability which indirectly improves consistency for assistive technology users.

pointer: fine 24px targets OK hover effects ✓ pointer: coarse 48px targets no hover needed @media (pointer: fine/coarse)

Pointer & Hover Detection responsive-006

Use `@media (pointer: fine)` to detect mouse/stylus input and `@media (hover: hover)` to detect hover capability. This lets you add hover effects only on devices that support them, and adjust target sizes based on input precision.

Implementation

`@media (pointer: coarse)` for touch devices — increase touch targets to 48px+. `@media (hover: hover)` — add hover states only here. `@media (pointer: fine)` — allow smaller interactive elements (24px minimum).

Accessibility

Never rely on hover as the only way to access information — `@media (hover: hover)` adds hover effects, but the content must be accessible via click/tap/keyboard too. Hover-only interactions fail for keyboard and touch users.

Typography

Heading 1.75rem (min) Heading 3rem (max) clamp(1.75rem, 1.2rem + 2vw, 3rem)

Clamp-Based Font Sizing responsive-007

Use `clamp(min, preferred, max)` for font sizes that scale smoothly between breakpoints without media queries. The preferred value uses a `rem + vw` formula to create fluid scaling. Eliminates jarring font-size jumps at breakpoints.

Implementation

`h1 { font-size: clamp(1.75rem, 1.2rem + 2vw, 3rem); }`. Body: `clamp(0.9rem, 0.85rem + 0.25vw, 1.1rem)`. Use a fluid type scale generator to calculate values. Always use `rem` for the min to ensure zoom works.

Accessibility

WCAG 1.4.4 (Resize Text) requires text to be zoomable to 200%. `clamp()` respects browser zoom when the min value uses `rem`. Never use `vw` alone — it ignores zoom and becomes unreadable at extremes.

Mobile 4px Desktop 12 clamp(0.75rem, 0.5rem + 1vw, 2rem)

Fluid Spacing responsive-008

Apply the same `clamp()` approach to spacing (margins, padding, gaps) so layouts breathe proportionally to viewport width. Tight spacing on mobile, generous spacing on desktop — without media query jumps.

Implementation

`gap: clamp(0.75rem, 0.5rem + 1vw, 2rem)`. Apply to grid gaps, section padding, and content margins. Match fluid spacing scale to fluid type scale for visual consistency.

Accessibility

Fluid spacing benefits users who zoom — spacing scales proportionally rather than staying fixed. Ensure minimum spacing never goes below 4px to maintain visual separation.

100+ characters — hard to read max-width: 65ch (45-75 chars)

Line Length Control responsive-009

Optimal line length is 45-75 characters for body text (66ch ideal per typographic research). Lines too long reduce reading comprehension; lines too short cause excessive eye movement. Use `max-width` in `ch` units to constrain.

Implementation

`max-width: 65ch` on text containers. For wider layouts, use multi-column with `column-width: 25ch; column-count: auto`. `ch` unit is based on the width of the "0" character in the current font.

Accessibility

WCAG 1.4.8 (Visual Presentation, AAA) recommends max 80 characters per line. Using `ch` units naturally adapts when users change font size or zoom.

Images

400w 800w 1200w Browser picks the best match sizes="(max-width: 768px) 100vw, 33vw" Cap at 2x — eyes can't tell 3x

srcset and sizes responsive-010

Provide multiple image resolutions and let the browser choose the best one based on viewport width and device pixel ratio. The `srcset` attribute lists available widths; `sizes` tells the browser how wide the image renders at each breakpoint.

Implementation

`<img srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1200.jpg 1200w" sizes="(max-width: 768px) 100vw, (max-width: 1024px) 50vw, 33vw" src="photo-800.jpg" alt="Description" loading="lazy">`. Cap at 2x — human eyes can't distinguish 2x from 3x.

Accessibility

Always include `alt` attribute. The responsive image mechanism is transparent to assistive technology — `alt` text applies regardless of which source is loaded.

Mobile (square) Tablet (4:3) Desktop (panoramic) Different crops per breakpoint <picture> + <source media>

Art Direction with Picture responsive-011

The `<picture>` element lets you serve different image crops or formats per breakpoint (art direction). A wide panoramic hero on desktop can become a square crop on mobile — different compositions optimized for each viewport.

Implementation

`<picture><source media="(min-width: 1024px)" srcset="hero-wide.webp" type="image/webp" /><source media="(min-width: 768px)" srcset="hero-medium.webp" /><img src="hero-mobile.jpg" alt="Hero" /></picture>`. List sources from largest to smallest.

Accessibility

The `alt` attribute goes on the `<img>` fallback element, not on `<source>`. All art-directed crops should convey the same essential content — don't change the semantic meaning between breakpoints.

No aspect-ratio image loads ↓ CLS! aspect-ratio: 16/9 reserved! stable Prevents layout shift (CLS)

Aspect Ratio Preservation responsive-012

Always set `aspect-ratio` or explicit `width`/`height` on images and videos to prevent Cumulative Layout Shift (CLS). Without dimensions, the browser can't reserve space before the image loads, causing content to jump.

Implementation

`img { aspect-ratio: 16/9; object-fit: cover; width: 100%; height: auto; }`. Or use explicit HTML attributes: `<img width="800" height="450">`. Framework image components (Next.js Image, Astro Image) handle this automatically.

Accessibility

Layout shift is disorienting for all users, especially those using screen magnification or with cognitive disabilities. Stable layouts reduce cognitive load.

viewport Hero: loading="eager" fetchpriority="high" below fold lazy lazy lazy

Lazy Loading Strategy responsive-013

Below-fold images use `loading="lazy"` to defer loading until they're near the viewport. Above-fold and LCP (Largest Contentful Paint) images must NEVER be lazy-loaded — this delays the critical rendering path and causes visible pop-in.

Implementation

Hero/LCP image: `loading="eager" fetchpriority="high"` (exactly one per page). All other images: `loading="lazy"`. If the LCP image is CSS-rendered, add `<link rel="preload" as="image" fetchpriority="high">`.

Accessibility

Lazy loading has no direct accessibility impact, but faster page loads benefit users on assistive technology who may experience compounded delays.

Layout

Card Card Card Card Card auto-fill

Auto-Fill Grid responsive-014

A CSS Grid that automatically adjusts column count based on available space — no media queries needed. Cards flow into as many columns as fit, with a minimum card width. The simplest responsive card layout.

Implementation

`display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: var(--spacing-4)`. Use `auto-fill` (empty tracks) vs `auto-fit` (collapsed tracks) based on whether you want items to stretch to fill.

Accessibility

Grid items maintain DOM order which matches visual order. Never use CSS Grid to reorder items in a way that contradicts the logical reading order.

React TypeScript Next.js CSS Tailwind PostgreSQL Prisma flex-wrap: wrap + gap Items wrap naturally at any width

Flex Wrap Layout responsive-015

A flexbox layout where items wrap to the next line when they don't fit. Best for 1D collections of varying-width items like tags, buttons, or navigation items. Items maintain their natural width.

Implementation

`display: flex; flex-wrap: wrap; gap: var(--spacing-2)`. Use `flex: 1 1 200px` on children for equal-width wrapping. Use `flex: 0 0 auto` for natural-width items.

Accessibility

Wrapped flex items maintain DOM order. Use `gap` instead of margins for consistent spacing. Avoid `order` property which creates a mismatch between visual and DOM order.

LTR (English) RTL (Arabic) Same CSS code margin-inline-start: 1rem Auto-adapts to dir="rtl"

Logical Properties responsive-016

Use `margin-inline`, `padding-block`, `border-inline-start` instead of physical properties like `margin-left`, `padding-top`. Logical properties automatically adapt to RTL (`dir="rtl"`) and vertical writing modes without additional CSS.

Implementation

`margin-left` → `margin-inline-start`. `padding-top` → `padding-block-start`. `width` → `inline-size`. `text-align: left` → `text-align: start`. Apply to all new CSS — no performance or size cost.

Accessibility

Logical properties make RTL support automatic, benefiting Arabic, Hebrew, Farsi, and other RTL language users. Combined with `lang` and `dir` attributes, layouts adapt without any extra CSS.

Mobile Label Input Button flex-direction: column Desktop Label Input Btn flex-direction: row Most common responsive layout shift

Stack-to-Row Pattern responsive-018

Elements stack vertically on mobile and arrange horizontally on wider viewports. The most common responsive layout shift — used for header layouts, form fields, card content, and action button groups.

Implementation

`display: flex; flex-direction: column; gap: var(--spacing-2)` then `@media (min-width: 768px) { flex-direction: row; align-items: center }`. Use `flex-wrap: wrap` as a safety net on intermediate widths.

Accessibility

Maintain logical reading order regardless of layout direction. Stacked (mobile) and inline (desktop) should read in the same sequence. Focus order must match visual order.

Navigation

1024+ More 768-1023 <768 bottom tab bar

Responsive Nav Pattern responsive-019

Navigation adapts across three breakpoints: horizontal top bar on desktop (1024px+), condensed top bar or collapsible sidebar on tablet (768px-1023px), and bottom tab bar or hamburger drawer on mobile (<768px). Bottom tab bar is preferred for primary actions — it's in the thumb zone.

Implementation

Desktop: `<nav>` with horizontal `<ul>`. Tablet: priority+ pattern (show top items, overflow into "More" menu). Mobile: bottom tab bar for 3-5 items, hamburger drawer for 5+ items. Use `@media` to switch.

Accessibility

`<nav aria-label="Main navigation">` at all breakpoints. Hamburger button: `aria-expanded`, `aria-controls`. Tab bar: `role="tablist"` with `aria-selected`. All items keyboard-navigable.

Home Products About More ▾ Blog Contact Overflow items → "More" ResizeObserver recalculates

Priority+ Navigation responsive-020

Show as many nav items as fit in the available space; overflow the rest into a "More" dropdown. The nav adapts dynamically to any width without needing specific breakpoints. Used by Google and many responsive web apps.

Implementation

Use `ResizeObserver` on the nav container. Calculate which items fit based on their widths. Move overflow items to a "More" dropdown. Recalculate on resize. Libraries: `react-priority-navigation`.

Accessibility

"More" button: `aria-expanded`, `aria-haspopup="true"`. Overflow menu: `role="menu"`. Items in "More" must be keyboard-navigable with arrow keys. Focus returns to "More" button on close.

× role= "dialog" focus trapped

Off-Canvas Navigation responsive-021

A navigation panel that slides in from the side of the viewport, overlaying or pushing the main content. The standard mobile navigation pattern for apps with many sections. Triggered by a hamburger icon.

Implementation

Width: `min(80vw, 360px)`. Transform: `translateX(-100%)` → `translateX(0)` over 250ms ease-out. Close: X button + backdrop click + swipe + Escape. `overscroll-behavior: contain` to prevent body scroll bleed.

Accessibility

Focus trap when open. `role="dialog"`, `aria-modal="true"`, `aria-label="Site navigation"`. Close on Escape. Return focus to hamburger trigger on close. Hamburger: `aria-expanded` + `aria-controls`.

Desktop: table Name Email Role Alice a@b.co Admin Bob b@c.co User Mobile: cards Name Alice Email a@b.co Name Bob Email b@c.co 3 strategies: scroll, cards, or hide columns

Responsive Table responsive-022

Tables are inherently wide and overflow on mobile. Three adaptation strategies: horizontal scroll (simplest), card transformation (each row becomes a card), or column prioritization (hide low-priority columns). Choose based on data density.

Implementation

Scroll: wrap in `<div style="overflow-x: auto">` with `-webkit-overflow-scrolling: touch`. Cards: `@media (max-width: 768px)` — set `display: block` on rows, use `data-label` attributes for headers. Column priority: `display: none` on low-priority columns.

Accessibility

Scrollable tables: add `tabindex="0"` and `role="region"` with `aria-label` on the scroll container. Card view: use `aria-label` on each card. Never remove columns that contain essential data without providing an alternative view.