Frontend Development

Responsive Web Design: Mobile-First Approach

Mayur Dabhi
Mayur Dabhi
March 1, 2026
18 min read

You build a page on a 27-inch monitor, it looks great, and then someone opens it on their phone. The sidebar squeezes the content into a 120px column, the hero image is a 2MB file scaled down to thumbnail size, and the stylesheet has three max-width blocks fighting each other to undo the desktop layout. Most sites get more than half their traffic from phones, so this isn't an edge case.

Mobile-first flips the order: write the small-screen styles first, then add layout for bigger screens. This post covers why that order matters, how to write min-width media queries, responsive images, fluid grids and type, and the mistakes I run into most often.

The Mobile-First Principle

Your styles outside any media query are the mobile styles. Everything inside a min-width query adds layout for wider screens. Phones get the simple version, and nothing has to be undone.

Why Mobile-First Matters

This is as much about performance as design. Desktop-first means phones download the full layout, wide images and all, and then CSS hides or overrides most of it. Mobile-first starts from the lean version and only adds what each screen size actually needs.

DESKTOP-FIRST ❌ Not Recommended Desktop Remove Mobile • Loads all CSS • Then hides things • Wasted bandwidth • Slower mobile MOBILE-FIRST ✓ Recommended Base Add Tablet Add Desktop • Loads minimal CSS • Enhances up • Fast on mobile

Desktop-first loads everything then hides; Mobile-first starts lean and adds progressively

Key Benefits of Mobile-First

Performance Benefits

  • Fewer overrides: phones apply the base rules and skip the media query blocks they don't match
  • Lighter pages on phones: paired with responsive images, small screens stop downloading desktop-sized assets
  • Easier Core Web Vitals: smaller images and simpler layouts help LCP and CLS, and less work on the main thread helps INP
  • Progressive enhancement: complexity only shows up where there's room for it

Understanding Breakpoints

A breakpoint is just the width where your layout changes. There's no official list, but most teams end up close to Tailwind's defaults, so they're a reasonable starting point:

Common Breakpoint Ranges

<640px
640-1024px
>1024px
0px 640px 1024px 1536px+
Breakpoint Min Width Target Devices Tailwind Class
sm 640px Large phones, small tablets sm:
md 768px Tablets (portrait) md:
lg 1024px Tablets held sideways, small laptops lg:
xl 1280px Laptops, desktops xl:
2xl 1536px Large desktops, 4K monitors 2xl:
Don't Over-Breakpoint

You don't need a breakpoint for every device. Drag the browser window wider and add one where your content actually breaks: lines of text get too long to read, or cards look cramped. The content should decide the breakpoints, not a list of device specs.

Mobile-First Media Queries

In practice, mobile-first CSS mostly means writing min-width queries instead of max-width ones. The base styles apply everywhere, and each query adds to them as the screen gets wider.

"My media queries don't work on my phone"

If your queries fire in DevTools but not on a real phone, check for <meta name="viewport" content="width=device-width, initial-scale=1"> in the <head>. Without it, mobile browsers render the page at a virtual width of around 980px and zoom out, so your min-width: 768px styles apply on a phone and the base mobile styles never show up.

Desktop-First vs Mobile-First Syntax

Desktop-First

Uses max-width: starts large, scales down

/* Base: Desktop styles */
.container {
  width: 1200px;
  display: flex;
}

/* Override for tablet */
@media (max-width: 1024px) {
  .container {
    width: 100%;
  }
}

/* Override for mobile */
@media (max-width: 768px) {
  .container {
    display: block;
  }
}

Mobile-First

Uses min-width: starts small, scales up

/* Base: Mobile styles */
.container {
  width: 100%;
  display: block;
}

/* Enhance for tablet */
@media (min-width: 768px) {
  .container {
    display: flex;
  }
}

/* Enhance for desktop */
@media (min-width: 1024px) {
  .container {
    width: 1200px;
  }
}

Complete Mobile-First Layout Example

CSS
/* ========================================
   BASE STYLES (Mobile First - No Media Query)
   ======================================== */

/* Reset & Base Typography */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: system-ui, -apple-system, sans-serif;
  font-size: 16px;
  line-height: 1.6;
  color: #1a1a1a;
}

/* Container - Full width with padding on mobile */
.container {
  width: 100%;
  padding: 0 16px;
}

/* Navigation - Stacked on mobile */
.nav {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px;
}

.nav-logo {
  font-size: 1.5rem;
  font-weight: 700;
}

.nav-links {
  display: none; /* Hidden by default, toggle with JS */
}

.nav-links.active {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Hero - Single column */
.hero {
  padding: 48px 16px;
  text-align: center;
}

.hero h1 {
  font-size: 2rem;
  margin-bottom: 16px;
}

.hero p {
  font-size: 1rem;
  color: #666;
}

/* Cards Grid - Single column stack */
.cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: 24px;
  padding: 24px 16px;
}

.card {
  padding: 24px;
  border-radius: 12px;
  background: #f5f5f5;
}

/* ========================================
   TABLET STYLES (min-width: 768px)
   ======================================== */

@media (min-width: 768px) {
  .container {
    padding: 0 32px;
  }
  
  /* Navigation - Horizontal */
  .nav {
    flex-direction: row;
    justify-content: space-between;
    align-items: center;
  }
  
  .nav-links {
    display: flex;
    flex-direction: row;
    gap: 24px;
  }
  
  .nav-toggle {
    display: none; /* Hide hamburger */
  }
  
  /* Hero - Larger text */
  .hero {
    padding: 80px 32px;
  }
  
  .hero h1 {
    font-size: 2.5rem;
  }
  
  /* Cards - 2 columns */
  .cards {
    grid-template-columns: repeat(2, 1fr);
    padding: 32px;
  }
}

/* ========================================
   DESKTOP STYLES (min-width: 1024px)
   ======================================== */

@media (min-width: 1024px) {
  .container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 48px;
  }
  
  /* Hero - Side by side layout */
  .hero {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 48px;
    text-align: left;
    padding: 120px 48px;
    align-items: center;
  }
  
  .hero h1 {
    font-size: 3.5rem;
  }
  
  .hero p {
    font-size: 1.25rem;
  }
  
  /* Cards - 3 columns */
  .cards {
    grid-template-columns: repeat(3, 1fr);
    gap: 32px;
  }
}

/* ========================================
   LARGE DESKTOP (min-width: 1536px)
   ======================================== */

@media (min-width: 1536px) {
  .container {
    max-width: 1400px;
  }
  
  .cards {
    grid-template-columns: repeat(4, 1fr);
  }
}

Responsive Images

Images are usually the heaviest thing on the page, and CSS can't fix that. A 1600px photo shrunk to fit a phone still downloads at full size. You have to tell the browser which files exist.

The srcset Attribute

srcset lists the available widths, and sizes tells the browser how wide the image will render. The browser then picks a file based on that and the screen's pixel density:

HTML
<img 
  src="image-400.jpg"
  srcset="
    image-400.jpg 400w,
    image-800.jpg 800w,
    image-1200.jpg 1200w,
    image-1600.jpg 1600w
  "
  sizes="
    (max-width: 640px) 100vw,
    (max-width: 1024px) 50vw,
    33vw
  "
  alt="Responsive image example"
  loading="lazy"
/>

How sizes Works

(max-width: 640px) 100vw: image takes the full viewport width
(max-width: 1024px) 50vw: image takes half the viewport
33vw: the default, image takes one third of the viewport

One gotcha: if you leave out sizes, the browser assumes 100vw. Your image might sit in a 300px sidebar, but a high-density laptop screen will still download the 1600px file. If the network tab shows bigger images than you expected, check sizes first.

The <picture> Element

Use <picture> when you need different images (not just sizes) for different breakpoints, such as a tighter crop on phones or a newer format with a fallback. The browser uses the first <source> that matches, so order matters:

HTML
<picture>
  <!-- WebP for modern browsers -->
  <source 
    type="image/webp"
    srcset="hero-mobile.webp 640w, hero-desktop.webp 1200w"
    sizes="100vw"
  />
  
  <!-- Different crop for mobile -->
  <source 
    media="(max-width: 640px)"
    srcset="hero-mobile-crop.jpg"
  />
  
  <!-- Desktop version -->
  <source 
    media="(min-width: 641px)"
    srcset="hero-desktop.jpg"
  />
  
  <!-- Fallback -->
  <img src="hero-fallback.jpg" alt="Hero image" loading="lazy" />
</picture>

Fluid Layouts with CSS Grid & Flexbox

Grid and Flexbox can handle a lot of responsive behavior without any breakpoints. I'd reach for these before adding another media query.

Auto-Fit Grid

One line of CSS gives you a card grid that goes from one column to four as space allows:

CSS
/* Responsive card grid - no media queries needed! */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 24px;
}

/* How it works:
   - auto-fit: Creates as many columns as will fit
   - minmax(280px, 1fr): Each column is at least 280px, 
     but grows equally to fill space
   
   Result:
   - On mobile (< 560px): 1 column
   - On tablet (~840px): 2-3 columns  
   - On desktop (1200px+): 4+ columns
*/
Mobile (320px) Tablet (768px) Desktop (1200px)

auto-fit with minmax() adjusts the column count to the container width

Watch the minimum value. On a 320px phone with 16px padding on each side, the container is only 288px wide, so a minmax(300px, 1fr) column overflows and you get horizontal scrolling. minmax(min(280px, 100%), 1fr) avoids that.

Flexible Flexbox Navigation

This example uses a max-width query for the small-screen tweak. That's fine for a one-off adjustment, but if you find yourself writing lots of them, the base styles are probably desktop styles in disguise.

CSS
/* Responsive navigation that wraps naturally */
.nav {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  justify-content: space-between;
  align-items: center;
}

.nav-links {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 24px;
}

/* On mobile, nav-links can stack below logo */
@media (max-width: 640px) {
  .nav {
    flex-direction: column;
    gap: 12px;
  }
  
  .nav-links {
    justify-content: center;
  }
}

Fluid Typography

Rather than bumping font sizes at every breakpoint, clamp() lets text scale smoothly between a minimum and a maximum:

CSS
/* Fluid typography using clamp() */
:root {
  /* clamp(minimum, preferred, maximum) */
  
  /* Headings */
  --font-h1: clamp(2rem, 5vw + 1rem, 4rem);
  --font-h2: clamp(1.5rem, 3vw + 0.75rem, 2.5rem);
  --font-h3: clamp(1.25rem, 2vw + 0.5rem, 1.75rem);
  
  /* Body text */
  --font-body: clamp(1rem, 0.5vw + 0.875rem, 1.125rem);
  --font-small: clamp(0.875rem, 0.5vw + 0.75rem, 1rem);
}

h1 { font-size: var(--font-h1); }
h2 { font-size: var(--font-h2); }
h3 { font-size: var(--font-h3); }
body { font-size: var(--font-body); }
small { font-size: var(--font-small); }

/* How clamp() works:
   clamp(2rem, 5vw + 1rem, 4rem)
   
   - Minimum: 2rem (32px) - never smaller
   - Preferred: 5vw + 1rem - scales with viewport
   - Maximum: 4rem (64px) - never larger
   
   The browser picks the middle value, clamped 
   between min and max */

Keep a rem part in the middle value. A preferred size of pure vw ignores the user's font settings and barely changes when they zoom, which is an accessibility problem.

Fluid Spacing Too

Apply the same technique to margins, padding, and gaps:

:root {
  --space-sm: clamp(0.5rem, 1vw, 1rem);
  --space-md: clamp(1rem, 2vw, 2rem);
  --space-lg: clamp(2rem, 4vw, 4rem);
}

section { padding: var(--space-lg) var(--space-md); }
.card { padding: var(--space-md); gap: var(--space-sm); }

Mobile-First Best Practices

1

Design Mobile Wireframes First

If a layout works on a small screen, spreading it out for desktop is easy. Squeezing a desktop layout into a phone is much harder.

2

Content Priority

A small screen makes you decide what matters. Put that first. Secondary content can wait for wider screens or go further down the page.

3

Touch-Friendly Targets

Buttons and links should be at least 44×44px. Space interactive elements far enough apart to prevent accidental taps.

4

Test on Real Devices

DevTools device mode doesn't emulate a real phone's CPU, touch, or browser toolbars. On iOS Safari, for example, 100vh includes the area behind the address bar, so full-height sections get cut off. Use 100dvh (or svh) and check on an actual device.

5

Use Relative Units

Prefer rem, em, %, vw, and vh over fixed pixels. They scale better across devices and respect user font preferences.

Useful Tools & Resources

Browser DevTools

All major browsers have responsive design modes:

  • Chrome/Edge: F12 → Toggle Device Toolbar (Ctrl+Shift+M)
  • Firefox: F12 → Responsive Design Mode (Ctrl+Shift+M)
  • Safari: Develop → Enter Responsive Design Mode

Also drag the width slowly through odd sizes like 700px or 900px, not just the device presets. Layouts usually break in the gaps between your breakpoints.

Performance Testing

  • Lighthouse: Built into Chrome DevTools, audits performance and responsiveness
  • PageSpeed Insights: Google's online tool for mobile/desktop performance
  • WebPageTest: Advanced testing with real devices and network conditions
  • Chrome UX Report: Real-world performance data from Chrome users

CSS Frameworks with Mobile-First

  • Tailwind CSS: Mobile-first by default. Unprefixed utilities are mobile; use sm:, md:, lg: for larger screens
  • Bootstrap 5: Mobile-first grid system with responsive breakpoint classes
  • Foundation: Mobile-first with a flexible grid and responsive utilities

When mobile-first is worth it (and when it isn't)

For anything public facing, like marketing sites, blogs, stores, or signup flows, I'd start mobile-first every time. Your phone users are often on slower networks and weaker CPUs, and they're the ones who suffer most from a stylesheet full of overrides.

It's less clear-cut for internal tools. An admin panel with dense data tables that people only open on work laptops doesn't gain much from being designed phone-first. There I'd build for desktop, make sure nothing overflows horizontally on a small screen, and stop there. The same goes for retrofitting a big existing desktop-first codebase: flipping every max-width query is a lot of risk for little gain. Use min-width for new components and leave the rest alone.

The mistakes I see most often:

Before you ship, open the page on a real phone over a throttled connection and scroll the whole thing. If nothing scrolls sideways and the main content shows up fast, you're in good shape.

Responsive Design Mobile-First CSS Media Queries Web Development
Mayur Dabhi

Mayur Dabhi

Full Stack Developer with 5+ years of experience building responsive, performant web applications. Passionate about clean code and user experience.