Files
gh-jamesrochabrun-skills/skills/frontend-designer/references/responsive_patterns.md
2025-11-29 18:48:55 +08:00

14 KiB

Responsive Design Patterns

Modern responsive design patterns and techniques for creating flexible, accessible layouts that work across all devices.

Mobile-First Approach

Start with mobile design and enhance for larger screens.

Why Mobile-First?

Benefits:

  • Forces focus on essential content
  • Better performance (smaller base CSS)
  • Progressive enhancement mindset
  • Easier to add features than remove them

Basic Pattern:

/* Base (mobile) styles - no media query */
.container {
  width: 100%;
  padding: 1rem;
}

/* Tablet and up */
@media (min-width: 768px) {
  .container {
    padding: 2rem;
  }
}

/* Desktop and up */
@media (min-width: 1024px) {
  .container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 3rem;
  }
}

Breakpoints

Standard Breakpoints

:root {
  /* Mobile first - these are MIN widths */
  --breakpoint-sm: 640px;   /* Small tablets, large phones */
  --breakpoint-md: 768px;   /* Tablets */
  --breakpoint-lg: 1024px;  /* Laptops, desktops */
  --breakpoint-xl: 1280px;  /* Large desktops */
  --breakpoint-2xl: 1536px; /* Extra large screens */
}

/* Usage */
@media (min-width: 768px) {
  /* Tablet and up */
}

@media (min-width: 1024px) {
  /* Desktop and up */
}

Custom Breakpoints

/* Content-based breakpoints */
@media (min-width: 400px) {
  /* When content needs it, not arbitrary device size */
}

/* Prefer rem-based breakpoints for accessibility */
@media (min-width: 48rem) { /* 768px at 16px base */
  /* Scales with user's font size preferences */
}

Container Queries (Modern)

/* Respond to container size, not viewport */
.card-container {
  container-type: inline-size;
  container-name: card;
}

@container card (min-width: 400px) {
  .card {
    display: grid;
    grid-template-columns: 200px 1fr;
  }
}

Responsive Typography

Fluid Typography

/* Scales smoothly between min and max */
h1 {
  font-size: clamp(2rem, 5vw + 1rem, 4rem);
  /* Min: 2rem (32px) */
  /* Preferred: 5vw + 1rem */
  /* Max: 4rem (64px) */
}

/* More examples */
.text-sm {
  font-size: clamp(0.875rem, 0.85rem + 0.125vw, 1rem);
}

.text-base {
  font-size: clamp(1rem, 0.95rem + 0.25vw, 1.125rem);
}

.text-lg {
  font-size: clamp(1.125rem, 1.05rem + 0.375vw, 1.25rem);
}

Responsive Line Height

body {
  /* Tighter on mobile, looser on desktop */
  line-height: 1.5;
}

@media (min-width: 768px) {
  body {
    line-height: 1.6;
  }
}

Layout Patterns

1. Stack Layout

Everything stacks vertically on mobile, side-by-side on larger screens.

.stack {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

@media (min-width: 768px) {
  .stack {
    flex-direction: row;
  }
}

2. Sidebar Layout

Sidebar stacks on mobile, side-by-side on desktop.

.sidebar-layout {
  display: grid;
  gap: 2rem;
}

@media (min-width: 768px) {
  .sidebar-layout {
    grid-template-columns: 250px 1fr;
  }
}

/* Flexible sidebar */
@media (min-width: 768px) {
  .sidebar-layout--flexible {
    grid-template-columns: minmax(200px, 300px) 1fr;
  }
}

3. Grid Layout

Responsive column count.

.grid {
  display: grid;
  gap: 1rem;

  /* Mobile: 1 column */
  grid-template-columns: 1fr;
}

@media (min-width: 640px) {
  .grid {
    /* Tablet: 2 columns */
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 1024px) {
  .grid {
    /* Desktop: 3-4 columns */
    grid-template-columns: repeat(4, 1fr);
  }
}

/* Auto-responsive grid (no media queries!) */
.grid-auto {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  /* Creates as many columns as fit, minimum 250px each */
}

4. Holy Grail Layout

Classic three-column layout that adapts to mobile.

.holy-grail {
  display: grid;
  gap: 1rem;
  min-height: 100vh;

  /* Mobile: stack everything */
  grid-template:
    "header" auto
    "main" 1fr
    "sidebar1" auto
    "sidebar2" auto
    "footer" auto
    / 1fr;
}

@media (min-width: 768px) {
  .holy-grail {
    /* Desktop: traditional layout */
    grid-template:
      "header header header" auto
      "sidebar1 main sidebar2" 1fr
      "footer footer footer" auto
      / 200px 1fr 200px;
  }
}

.header { grid-area: header; }
.sidebar-1 { grid-area: sidebar1; }
.main { grid-area: main; }
.sidebar-2 { grid-area: sidebar2; }
.footer { grid-area: footer; }

5. Card Layout

Responsive cards that adapt their internal layout.

.card {
  display: grid;
  gap: 1rem;

  /* Mobile: stack image and content */
  grid-template:
    "image" auto
    "content" 1fr
    / 1fr;
}

@media (min-width: 640px) {
  .card {
    /* Tablet+: side-by-side */
    grid-template:
      "image content" 1fr
      / 200px 1fr;
  }
}

.card__image { grid-area: image; }
.card__content { grid-area: content; }

6. Switcher Pattern

Switch between horizontal and vertical based on available space.

.switcher {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}

.switcher > * {
  /* Grow to fill, but switch to vertical when < 400px */
  flex-grow: 1;
  flex-basis: calc((400px - 100%) * 999);
  /* Clever calc that breaks at threshold */
}

7. Pancake Stack

Header, main, footer layout that adapts height.

.pancake {
  display: grid;
  grid-template-rows: auto 1fr auto;
  min-height: 100vh;
}

.header { /* auto height */ }
.main { /* fills available space */ }
.footer { /* auto height */ }

Responsive Images

1. Flexible Images

img {
  max-width: 100%;
  height: auto;
  display: block;
}

2. Art Direction (Different Images per Breakpoint)

<picture>
  <source media="(min-width: 1024px)" srcset="large.jpg">
  <source media="(min-width: 768px)" srcset="medium.jpg">
  <img src="small.jpg" alt="Description">
</picture>

3. Resolution Switching (Same Image, Different Sizes)

<img
  srcset="
    small.jpg 400w,
    medium.jpg 800w,
    large.jpg 1200w
  "
  sizes="
    (min-width: 1024px) 800px,
    (min-width: 768px) 600px,
    100vw
  "
  src="medium.jpg"
  alt="Description"
>

4. Background Images

.hero {
  background-image: url('small.jpg');
  background-size: cover;
  background-position: center;
}

@media (min-width: 768px) {
  .hero {
    background-image: url('medium.jpg');
  }
}

@media (min-width: 1024px) {
  .hero {
    background-image: url('large.jpg');
  }
}

@media (min-width: 1024px) and (-webkit-min-device-pixel-ratio: 2) {
  .hero {
    background-image: url('large@2x.jpg');
  }
}

5. Aspect Ratio

/* Modern aspect ratio */
.image-container {
  aspect-ratio: 16 / 9;
  overflow: hidden;
}

.image-container img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Fallback for older browsers */
.image-container-fallback {
  position: relative;
  padding-bottom: 56.25%; /* 16:9 aspect ratio */
}

.image-container-fallback img {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

Responsive Navigation

1. Mobile Menu (Hamburger)

<nav class="nav">
  <div class="nav__brand">Logo</div>

  <button class="nav__toggle" aria-expanded="false" aria-controls="nav-menu">
    <span class="sr-only">Menu</span>
    <span class="hamburger"></span>
  </button>

  <ul class="nav__menu" id="nav-menu">
    <li><a href="/">Home</a></li>
    <li><a href="/about">About</a></li>
    <li><a href="/contact">Contact</a></li>
  </ul>
</nav>
.nav {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 1rem;
}

/* Mobile: hidden menu */
.nav__menu {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  background: white;
  flex-direction: column;
  padding: 1rem;
}

.nav__menu[aria-expanded="true"] {
  display: flex;
}

.nav__toggle {
  display: block;
}

/* Desktop: visible menu */
@media (min-width: 768px) {
  .nav__menu {
    display: flex;
    position: static;
    flex-direction: row;
    gap: 2rem;
  }

  .nav__toggle {
    display: none;
  }
}

2. Priority+ Navigation

Show important items, collapse others into "More" menu.

.priority-nav {
  display: flex;
  gap: 1rem;
}

.priority-nav__item {
  flex-shrink: 0; /* Don't shrink */
}

.priority-nav__more {
  margin-left: auto; /* Push to end */
}

/* Hide items that don't fit */
@media (max-width: 768px) {
  .priority-nav__item:nth-child(n+4) {
    display: none; /* Hide items 4+ */
  }
}

Responsive Tables

1. Horizontal Scroll

.table-container {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch; /* Smooth scrolling on iOS */
}

table {
  min-width: 600px;
  width: 100%;
}

2. Stacked Table (Card View)

<table class="responsive-table">
  <thead>
    <tr>
      <th>Name</th>
      <th>Email</th>
      <th>Role</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td data-label="Name">John Doe</td>
      <td data-label="Email">john@example.com</td>
      <td data-label="Role">Developer</td>
    </tr>
  </tbody>
</table>
/* Desktop: normal table */
@media (min-width: 768px) {
  .responsive-table {
    display: table;
  }
}

/* Mobile: stacked cards */
@media (max-width: 767px) {
  .responsive-table,
  .responsive-table thead,
  .responsive-table tbody,
  .responsive-table tr,
  .responsive-table th,
  .responsive-table td {
    display: block;
  }

  .responsive-table thead {
    display: none; /* Hide table header */
  }

  .responsive-table tr {
    margin-bottom: 1rem;
    border: 1px solid #ddd;
    border-radius: 0.5rem;
    padding: 1rem;
  }

  .responsive-table td {
    display: flex;
    justify-content: space-between;
    padding: 0.5rem 0;
    border-bottom: 1px solid #eee;
  }

  .responsive-table td:last-child {
    border-bottom: none;
  }

  .responsive-table td::before {
    content: attr(data-label);
    font-weight: bold;
    margin-right: 1rem;
  }
}

Responsive Forms

1. Single Column to Multi-Column

.form {
  display: grid;
  gap: 1rem;

  /* Mobile: single column */
  grid-template-columns: 1fr;
}

@media (min-width: 768px) {
  .form {
    /* Desktop: two columns */
    grid-template-columns: repeat(2, 1fr);
  }

  .form__field--full {
    /* Some fields span both columns */
    grid-column: 1 / -1;
  }
}

2. Touch-Friendly Inputs

input,
button,
select,
textarea {
  /* Minimum 44x44px touch target */
  min-height: 44px;
  padding: 0.75rem 1rem;
  font-size: 1rem; /* Prevents zoom on iOS */
}

@media (min-width: 768px) {
  input,
  button,
  select,
  textarea {
    /* Can be smaller on desktop */
    min-height: 40px;
    font-size: 0.875rem;
  }
}

Responsive Utilities

Show/Hide at Breakpoints

/* Hide on mobile */
.hidden-mobile {
  display: none;
}

@media (min-width: 768px) {
  .hidden-mobile {
    display: block;
  }
}

/* Show only on mobile */
.visible-mobile {
  display: block;
}

@media (min-width: 768px) {
  .visible-mobile {
    display: none;
  }
}

/* Hide on desktop */
@media (min-width: 1024px) {
  .hidden-desktop {
    display: none;
  }
}

Responsive Spacing

.section {
  /* Mobile: smaller padding */
  padding: 2rem 1rem;
}

@media (min-width: 768px) {
  .section {
    /* Tablet: medium padding */
    padding: 4rem 2rem;
  }
}

@media (min-width: 1024px) {
  .section {
    /* Desktop: larger padding */
    padding: 6rem 3rem;
  }
}

/* Or use fluid spacing */
.section-fluid {
  padding: clamp(2rem, 5vw, 6rem) clamp(1rem, 3vw, 3rem);
}

Testing Responsive Design

Browser DevTools

// Common viewport sizes to test
const viewports = [
  { width: 375, height: 667, name: 'iPhone SE' },
  { width: 390, height: 844, name: 'iPhone 12 Pro' },
  { width: 428, height: 926, name: 'iPhone 14 Pro Max' },
  { width: 768, height: 1024, name: 'iPad' },
  { width: 1024, height: 768, name: 'iPad Landscape' },
  { width: 1280, height: 720, name: 'Desktop' },
  { width: 1920, height: 1080, name: 'Full HD' },
];

Responsive Testing Checklist

  • Test all breakpoints
  • Test between breakpoints (awkward sizes)
  • Test portrait and landscape
  • Test zoom levels (100%, 200%, 400%)
  • Test with real devices when possible
  • Test touch interactions on mobile
  • Test with different font sizes
  • Test with slow network (images, fonts)

Performance Considerations

Lazy Loading

<!-- Lazy load images below the fold -->
<img src="image.jpg" loading="lazy" alt="Description">

<!-- Eager load above-the-fold images -->
<img src="hero.jpg" loading="eager" alt="Hero image">

Conditional Loading

// Load component only on larger screens
if (window.matchMedia('(min-width: 768px)').matches) {
  import('./DesktopComponent.js').then(module => {
    // Initialize desktop component
  });
}

Font Loading

@font-face {
  font-family: 'CustomFont';
  src: url('font.woff2') format('woff2');
  font-display: swap; /* Show fallback while loading */
}

Modern CSS Features

1. CSS Grid Auto-Fill

.grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  /* Automatically creates columns, minimum 250px */
}

2. Flexbox Gap

.flex-container {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem; /* No more margin hacks! */
}

3. Container Queries

.card {
  container-type: inline-size;
}

@container (min-width: 400px) {
  .card__title {
    font-size: 1.5rem;
  }
}

4. Aspect Ratio

.video-container {
  aspect-ratio: 16 / 9;
}

5. Logical Properties

/* Better for RTL/LTR support */
.element {
  margin-block-start: 1rem; /* margin-top */
  margin-block-end: 1rem; /* margin-bottom */
  margin-inline-start: 1rem; /* margin-left in LTR, margin-right in RTL */
  margin-inline-end: 1rem; /* margin-right in LTR, margin-left in RTL */
}

Resources


"The best design is the one that works everywhere, for everyone."