Responsive & Mobile Design

Over half of web traffic is mobile. If your design doesn't work on a phone, it doesn't work. Responsive design isn't a feature, it's the default way to build.

Mobile-First Approach

Start with the smallest screen, then progressively enhance for larger screens. This forces you to prioritize content and interactions because there's no room for clutter on a 375px-wide screen.

/* Mobile base styles — the default */
.component {
  padding: 16px;
  font-size: 16px;
  flex-direction: column;
}

/* Tablet enhancement — add when space allows */
@media (min-width: 768px) {
  .component {
    padding: 24px;
    font-size: 18px;
    flex-direction: row;
  }
}

/* Desktop enhancement — more space, more features */
@media (min-width: 1024px) {
  .component {
    padding: 32px;
    max-width: 1200px;
    margin: 0 auto;
  }
}

Why Mobile-First (Not Desktop-First)?

Mobile-FirstDesktop-First
Forces content prioritizationTempts you to cram everything in
Base CSS is lighter (fewer overrides)Base CSS is heavier (must undo for mobile)
Uses min-width media queries (progressive)Uses max-width media queries (regressive)
Easier to add features for larger screensHarder to remove features for smaller screens
Better performance on weaker mobile devicesMobile loads desktop CSS it doesn't need

Common mistake: Designing a polished desktop layout, then trying to "shrink it" for mobile. This always results in a cramped, unusable mobile experience. Start mobile, then expand.

Standard Breakpoints

/* Common breakpoint system */
--breakpoint-sm: 576px;    /* Large phones (landscape) */
--breakpoint-md: 768px;    /* Tablets (portrait) */
--breakpoint-lg: 1024px;   /* Small laptops / tablets (landscape) */
--breakpoint-xl: 1280px;   /* Desktops */
--breakpoint-2xl: 1536px;  /* Large monitors */

Breakpoint Decision Guide

BreakpointWhat ChangesExample
< 576pxSingle column. Stack everything. Hamburger nav.Cards stacked, full-width buttons
576-767pxStill single column but wider. Some side-by-side elements.Two small cards per row
768-1023pxTwo columns possible. Sidebar appears (if needed). Tab bar becomes top nav.Dashboard gains a sidebar
1024-1279pxFull multi-column layouts. All navigation visible.Three-column card grid
1280px+Max-width container. Generous whitespace. Optional: wider sidebars, richer data tables.1200px container, centered

Important: Let content dictate breakpoints, not device widths. If your card text wraps awkwardly at 820px, add a breakpoint at 820px. Don't force it to wait until 1024px.

/* Content-driven breakpoint */
@media (min-width: 820px) {
  .card-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

Touch Target Sizes

Fingers are imprecise. Touch targets must be large enough to tap without error.

StandardMinimum SizeRecommendedSpacing Between Targets
Apple HIG44 × 44 pt44 × 44 pt8px minimum
Google Material48 × 48 dp48 × 48 dp8px minimum
WCAG 2.2 (AA)24 × 24 px44 × 44 pxTarget must not overlap

Expanding Touch Targets Without Changing Visuals

A 24px icon can have a 48px touch target:

.icon-button {
  /* Visual size */
  width: 24px;
  height: 24px;

  /* Expanded touch area via padding */
  padding: 12px;
  margin: -12px;  /* Compensate so layout isn't affected */

  /* Or use a pseudo-element */
  position: relative;
}

.icon-button::before {
  content: '';
  position: absolute;
  inset: -12px;  /* Expands hit area by 12px in all directions */
}

Common Touch Target Failures

ElementTypical SizeProblemFix
Text links in body text~16px tallToo small to tap accuratelyAdd padding or make them buttons on mobile
Close button (×) on modals16-20pxFrustrating to tap, especially top-right cornerMake at least 44×44px target area
Checkbox/radio inputsBrowser default (13px)Nearly impossible to tapUse custom styles with 44px minimum area
Breadcrumb links14px text, tight spacingAdjacent links hard to distinguishIncrease spacing to 8px+ between items
Pagination numbersSmall, crampedWrong page selected constantlyUse buttons with 44px+ targets or prev/next only

Thumb Zone Design

Most mobile users hold their phone with one hand and operate with their thumb. The bottom of the screen is easiest to reach.

┌─────────────────────────┐
│                         │
│   ⬛ HARD TO REACH      │   Requires stretching or two hands
│                         │
│  ⬜ OK                  │   Reachable but not ideal
│        │        │       │
│  ✅ EASY │ ✅ EASY │     │   Natural thumb range
│        │        │       │
│        │        │       │
│────────┴────────┴───────│
│    ✅ PRIMARY ACTIONS    │   Bottom of screen — easiest
└─────────────────────────┘

Thumb Zone Application

ElementPlacementWhy
Primary navigation (tab bar)BottomAlways reachable with thumb
Primary action buttonBottom or center-bottomEasy to tap without shifting grip
Search barTop is conventional, but consider bottom sheetTop requires stretch on large phones
Menu/navigationBottom sheet or bottom tab barHamburger menus at top-left are hardest to reach
Destructive actionsTop area or behind confirmationHarder to reach = harder to accidentally trigger

Bottom Navigation Best Practices

┌─────────────────────────────────────────┐
│                                         │
│              Content Area               │
│                                         │
├─────────────────────────────────────────┤
│                                         │
│  🏠      🔍      ➕      ❤️      👤    │
│ Home   Search   New    Saved  Profile   │
│                                         │
└─────────────────────────────────────────┘
RuleGuideline
Item count3-5 items maximum. More requires a different pattern.
LabelsAlways include text labels with icons. Icons alone are ambiguous.
Active stateClear visual distinction for the selected item (color + weight)
Badge/notificationSmall dot or count badge for pending actions
Height56-64px including safe area padding on notched devices

Responsive Navigation Patterns

PatternBest ForProsCons
Bottom tab barApps, 3-5 primary actionsAlways visible, thumb-reachableLimited to 5 items
Hamburger menuSites with 5+ sectionsCompact, widely recognizedHides navigation, requires tap to reveal
Priority+ navSites with variable nav itemsShows most important items, overflows restCan feel inconsistent as items appear/disappear
Off-canvas/drawerDeep navigation treesRoom for nested items, familiarHides content, requires explicit open
Bottom sheetContextual actions, filtersThumb-friendly, can show lots of optionsCan feel heavy if overused
Full-screen navSimple sites (5-8 pages)Bold, easy to read, large targetsCovers all content

Implementing Priority+ Navigation

/* Show what fits, overflow the rest */
.nav {
  display: flex;
  overflow: hidden;
}

.nav-item { white-space: nowrap; }

.nav-overflow {
  margin-left: auto;
  /* "More" button that opens a dropdown with hidden items */
}

Responsive Content Strategies

What to Do With Content at Each Breakpoint

StrategyDescriptionExample
ReflowRearrange layout, keep all content3-column → 2-column → 1-column
ResizeScale elements proportionallySmaller images, fluid typography
RepositionMove elements to different locationsSidebar becomes bottom section
Reveal/HideShow/hide based on screen sizeHide secondary stats on mobile
ReplaceSwap components for mobile equivalentsData table → stacked cards

Data Tables on Mobile

Tables are one of the hardest responsive challenges. Options:

Option 1: Horizontal scroll (simplest)
┌──────────────────────────────┐
│ Name    | Email    | Role  →│  (scrollable)
│ Jane    | jane@... | Admin →│
└──────────────────────────────┘

Option 2: Stacked cards (most mobile-friendly)
┌──────────────────────────────┐
│ Jane Doe                     │
│ Email: jane@example.com      │
│ Role: Admin                  │
│ Status: Active               │
├──────────────────────────────┤
│ John Smith                   │
│ Email: john@example.com      │
│ Role: Editor                 │
│ Status: Active               │
└──────────────────────────────┘

Option 3: Priority columns (show key columns, hide others)
┌──────────────────────────────┐
│ Name        | Role           │  (Email, Status hidden behind tap)
│ Jane Doe    | Admin          │
│ John Smith  | Editor         │
└──────────────────────────────┘

Fluid Typography

Use clamp() for smooth scaling between breakpoints:

h1 {
  font-size: clamp(2rem, 5vw + 1rem, 4rem);
  /* min: 32px, preferred: 5vw + 16px, max: 64px */
}

h2 {
  font-size: clamp(1.5rem, 3vw + 0.5rem, 2.5rem);
}

p {
  font-size: clamp(1rem, 1vw + 0.75rem, 1.25rem);
  /* Body text stays between 16px and 20px */
}

Important: Body text shouldn't scale much. 16-18px on mobile, 16-20px on desktop. Headings can scale significantly.

Container Queries

Style components based on their container size, not the viewport. This makes components truly reusable. They adapt to wherever they're placed.

/* Define the container */
.card-container {
  container-type: inline-size;
}

/* Style based on container width */
@container (min-width: 400px) {
  .card {
    display: flex;
    flex-direction: row;
    gap: 24px;
  }
}

@container (max-width: 399px) {
  .card {
    display: flex;
    flex-direction: column;
    gap: 16px;
  }
}

When to Use Container Queries vs. Media Queries

Use CaseUse This
Page-level layout changes (sidebar, header)Media queries
Component-level layout changes (card, widget)Container queries
Typography scalingMedia queries or clamp()
Component adapting to different placementsContainer queries

Mobile-Specific Design Considerations

Input and Form Optimization

TechniqueImplementationWhy
Use correct input typestype="email", type="tel", type="number"Shows the right keyboard
Prevent zoom on focusfont-size: 16px minimum on inputsiOS zooms if input text < 16px
Use inputmodeinputmode="numeric" for codes, inputmode="decimal" for amountsFine-grained keyboard control
Use autocompleteautocomplete="email", autocomplete="tel"Auto-fills from saved data
Minimize typingUse select menus, toggles, date pickersTyping on mobile is slow and error-prone

Mobile Performance

TechniqueImpact
Lazy load images below the foldFaster initial load, less data usage
Serve smaller images via srcset2-5× less bandwidth on mobile
Use loading="lazy" on imagesNative lazy loading, no JavaScript
Reduce JavaScript bundleWeak mobile CPUs struggle with heavy JS
Use system fontsZero font-loading delay
Preconnect to required originsSaves DNS/TLS time on slow connections
<!-- Responsive images -->
<img
  srcset="photo-400.webp 400w,
          photo-800.webp 800w,
          photo-1200.webp 1200w"
  sizes="(max-width: 600px) 100vw,
         (max-width: 1024px) 50vw,
         33vw"
  src="photo-800.webp"
  alt="Description"
  loading="lazy"
>

Offline and Connectivity

  • Design for slow/intermittent connections. Show cached content when possible
  • Provide offline feedback: "You're offline. Changes will sync when reconnected."
  • Don't block the entire UI when one request fails
  • Queue actions for retry rather than showing an error

Testing Responsive Designs

MethodWhat It CatchesLimitation
Browser DevTools (responsive mode)Layout issues, breakpoint problemsDoesn't test real device performance
BrowserStack / Sauce LabsCross-browser, cross-device renderingSlower, requires account
Real device testingTouch interactions, performance, gesturesExpensive to maintain device lab
Chrome LighthousePerformance, accessibility scoresSimulated only
Users testing on their devicesReal-world usability issuesHarder to organize

Minimum test matrix:

Device TypeExampleScreen Width
Small phoneiPhone SE375px
Standard phoneiPhone 15 / Pixel 8390-412px
Large phoneiPhone 15 Pro Max430px
Tablet portraitiPad768px
Tablet landscapeiPad1024px
LaptopMacBook Air1280-1440px
DesktopExternal monitor1920px

Common Mistakes

MistakeImpactFix
Hover-dependent interactionsTouch devices have no hover stateEnsure all interactions work with tap only
Fixed-position elements covering contentNav bar covers text, footer blocks buttonsTest scrolling thoroughly, use safe-area padding
Not testing landscape orientationContent breaks, elements overlapTest both orientations on phones and tablets
Input font size < 16pxiOS Safari zooms the whole page on focusSet input font-size to 16px minimum
Relying on screen width for device detectionTablets can have phone-sized viewportsUse feature detection (@media (hover: hover))
Not accounting for safe areas (notch, home indicator)Content hidden behind device hardwareUse env(safe-area-inset-*) padding
/* Safe area padding for notched devices */
.bottom-nav {
  padding-bottom: calc(16px + env(safe-area-inset-bottom, 0px));
}

.header {
  padding-top: calc(16px + env(safe-area-inset-top, 0px));
}

Key Takeaways

  • Design mobile-first, then enhance for larger screens. Use min-width media queries.
  • Touch targets: 44×44px minimum. Use padding to expand hit areas beyond visual boundaries.
  • Place primary actions in the thumb zone (bottom of screen on mobile).
  • Use clamp() for fluid typography. Body text barely scales; headings scale significantly.
  • Container queries make components responsive to their container, not just the viewport.
  • Test on real devices. Browser DevTools miss touch interactions, performance, and safe areas.
  • Tables on mobile: use horizontal scroll, stacked cards, or priority columns. Don't shrink the table.
  • Prevent iOS zoom: set input font-size to at least 16px.