Step 1: The Viewport Meta Tag
Without this tag, mobile browsers render pages at desktop width and then scale them down — making text tiny. This is the first thing in every responsive project:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Breakdown:
width=device-width — viewport width = actual device screen width
initial-scale=1.0 — no zoom on load
Avoid adding these (they disable accessibility):
user-scalable=no — prevents pinch-to-zoom (BAD for accessibility)
maximum-scale=1 — same problem — never use this
-->
user-scalable=no and maximum-scale=1 prevent users from pinching to zoom. This is an accessibility violation — many users with low vision rely on zoom to read content. WCAG 1.4.4 requires that text can be resized up to 200% without loss of content or functionality. Google also penalizes sites that disable zoom in mobile search ranking.
Mobile-First in CSS: Base + Enhancement
Write CSS in two layers:
- Base styles — work for all screens, optimized for mobile
- Enhanced styles — progressively added via
min-widthmedia queries for larger screens
/* ====== BASE: mobile (no media query) ====== */
.nav {
display: flex;
flex-direction: column; /* stack vertically on mobile */
gap: 8px;
}
.hero { padding: 48px 16px; }
.hero h1 { font-size: 2rem; }
.card-grid {
display: grid;
grid-template-columns: 1fr; /* single column on mobile */
gap: 16px;
}
.sidebar { display: none; } /* hidden on mobile */
/* ====== TABLET: 768px and up ====== */
@media (min-width: 768px) {
.nav {
flex-direction: row; /* horizontal nav on tablet */
gap: 24px;
}
.hero { padding: 80px 32px; }
.hero h1 { font-size: 3rem; }
.card-grid {
grid-template-columns: repeat(2, 1fr); /* 2 columns */
}
}
/* ====== DESKTOP: 1024px and up ====== */
@media (min-width: 1024px) {
.sidebar { display: block; } /* show sidebar */
.card-grid {
grid-template-columns: repeat(3, 1fr); /* 3 columns */
}
}
/* ====== LARGE: 1280px and up ====== */
@media (min-width: 1280px) {
.container {
max-width: 1200px;
margin: 0 auto;
}
}
Touch Targets – Designing for Fingers
Mouse pointers are precise. Fingers are not. Any clickable element on mobile must be large enough to tap reliably:
/* Google recommends: minimum 48×48px touch targets */
/* Apple HIG: minimum 44×44pt */
/* WCAG 2.5.5 (AAA): minimum 44×44px */
/* Apply to all interactive elements */
a, button, input, select, textarea, [role="button"] {
min-height: 44px;
}
/* Nav links — add padding so the tap area is large even if text is small */
.nav-link {
display: inline-flex;
align-items: center;
padding: 12px 16px; /* creates 44px+ height with normal font */
text-decoration: none;
}
/* Icons with small visual size but large tap area */
.icon-btn {
width: 44px;
height: 44px;
display: flex;
align-items: center;
justify-content: center;
}
/* Space between touch targets */
.nav-links {
display: flex;
gap: 4px; /* minimum gap between tappable elements */
}
/* On desktop, targets can be smaller */
@media (min-width: 1024px) and (hover: hover) {
.nav-link { padding: 8px 12px; }
}
Mobile Typography
/* Base (mobile) font sizes */
:root {
font-size: 16px; /* never go below this on mobile */
}
body {
font-size: 1rem; /* 16px */
line-height: 1.6;
}
h1 { font-size: clamp(1.8rem, 6vw, 3rem); } /* fluid: 28.8px → 48px */
h2 { font-size: clamp(1.4rem, 4vw, 2rem); } /* fluid: 22.4px → 32px */
h3 { font-size: clamp(1.1rem, 3vw, 1.5rem); } /* fluid: 17.6px → 24px */
/* clamp(min, preferred, max) scales with viewport without media queries */
/* Explicit steps for headings */
h1 { font-size: 2rem; } /* mobile */
@media (min-width: 768px) { h1 { font-size: 2.5rem; } }
@media (min-width: 1024px) { h1 { font-size: 3rem; } }
Common Mobile-First Patterns
/* 1. Hamburger nav: hidden on desktop, toggle on mobile */
.mobile-nav { display: none; }
.mobile-nav.open { display: block; }
@media (min-width: 768px) {
.mobile-nav { display: none !important; } /* always hide on desktop */
.desktop-nav { display: flex; }
}
/* 2. Full-width buttons on mobile, auto-width on desktop */
.btn { display: block; width: 100%; }
@media (min-width: 768px) {
.btn { display: inline-block; width: auto; }
}
/* 3. Stacked form fields on mobile, side-by-side on desktop */
.form-row { display: flex; flex-direction: column; gap: 12px; }
@media (min-width: 768px) {
.form-row { flex-direction: row; }
.form-row .field { flex: 1; }
}
/* 4. Full-screen modal on mobile, dialog on desktop */
.modal {
position: fixed;
inset: 0; /* full screen on mobile */
border-radius: 0;
}
@media (min-width: 768px) {
.modal {
inset: auto;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 560px;
max-height: 80vh;
border-radius: 12px;
}
}
/* 5. Scroll instead of paginate on mobile */
.tabs {
display: flex;
overflow-x: auto; /* horizontal scroll on mobile */
-webkit-overflow-scrolling: touch;
gap: 8px;
scrollbar-width: none; /* hide scrollbar visually */
}
@media (min-width: 768px) {
.tabs { overflow-x: visible; }
}
Performance – Mobile-First Wins
/* Load heavy styles only when needed */
/* Inline critical CSS in <style> for fastest first paint */
/* Defer non-critical CSS */
/* Animations — disable on slow connections or user preference */
@media (prefers-reduced-motion: reduce) {
* { transition: none !important; animation: none !important; }
}
/* Use transform/opacity for animations (GPU, no reflow) */
.slide-in {
transform: translateX(-100%);
transition: transform 0.3s ease;
}
.slide-in.open { transform: translateX(0); }
/* Avoid expensive properties on mobile (scroll performance) */
.sticky-el {
position: sticky;
top: 0;
will-change: transform; /* promote to own compositor layer */
}
/* Critical rendering path: only load fonts needed */
<link rel="preload" as="font" href="font.woff2" crossorigin>
Converting Desktop-First to Mobile-First
/* BEFORE: desktop-first */
.grid { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 1023px) { .grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 767px) { .grid { grid-template-columns: 1fr; } }
/* AFTER: mobile-first (same result, less override) */
.grid { grid-template-columns: 1fr; }
@media (min-width: 768px) { .grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .grid { grid-template-columns: repeat(3, 1fr); } }
/* Strategy for conversion:
1. Identify the mobile layout (usually the simplest)
2. Make that the base (no media query)
3. Replace max-width queries with min-width queries
4. Build up from simple to complex */
📋 Summary
- Viewport meta:
<meta name="viewport" content="width=device-width, initial-scale=1.0">— required on every page. Never disable user-scalable. - Mobile-first: base styles for mobile,
min-widthmedia queries for larger screens. - Touch targets: minimum 44×44px for all interactive elements.
- Typography: minimum 16px body text. Use
clamp()for fluid headings. - Full-width buttons, stacked forms on mobile. Auto-width, horizontal on desktop.
- Performance: respect
prefers-reduced-motion. Usetransformfor animations. - Conversion: take the simplest state (mobile) as base, build up with
min-width.
Frequently Asked Questions
For most projects: yes. Mobile-first produces smaller CSS payloads for mobile devices, forces better content prioritization, and produces cleaner code with fewer overrides. The exception: if you're building a tool primarily used on desktops (a complex data dashboard, a code editor, an admin panel), desktop-first may be more natural for the design process. For public-facing websites with mixed audiences, always start mobile-first.
Apple's Human Interface Guidelines originally defined 44pt as the minimum based on research into average fingertip width and tap accuracy. Google's Material Design uses 48dp. WCAG 2.5.5 specifies 44×44 CSS pixels. Elements smaller than this have high miss rates — users tap adjacent elements accidentally. The 44px rule significantly reduces user frustration on touch interfaces, especially for users with motor impairments.
Responsive design is the broad goal — making a layout adapt to different screen sizes. Mobile-first is one approach to achieving it — write mobile styles as the base, then enhance for larger screens. The alternative is desktop-first (write desktop styles, override for mobile). Responsive design can be achieved with either approach; mobile-first just tends to produce better results and is the industry standard.