In 2024, roughly 60% of all website visits come from mobile phones. If your website is designed for a 1440px desktop monitor only, the majority of your visitors are getting a broken, zoomed-out, hard-to-read experience — and most of them leave immediately.

Responsive design is the practice of building websites that work well on every screen size automatically. The same HTML and CSS adapts its layout — rearranging columns, resizing text, stacking elements — depending on how much screen space is available.

The good news: you do not need separate codebases for mobile and desktop. You write one set of HTML and CSS that handles both. This guide covers every technique you need to do that properly.

What Is Responsive Design

Responsive design means your layout responds to the environment it is in. On a 375px phone screen, content stacks into a single column. On a 768px tablet, it might use two columns. On a 1200px desktop, three or four columns. The same page, the same code, different presentations.

Three techniques make this possible: fluid layouts that use percentages instead of fixed pixel widths, media queries that apply different CSS rules at different screen sizes, and flexible images that scale within their containers.

ℹ️ Google uses mobile-friendliness as a ranking factor. A non-responsive site ranks lower in search results on mobile searches. Responsive design is not just about user experience — it directly affects how discoverable your site is.

The Viewport Meta Tag — The Most Important Line

Before any CSS does anything, you need this one line in the <head> of every HTML page. Without it, mobile browsers zoom out to fit your desktop layout onto the screen — making everything tiny and unreadable.

HTML — the viewport meta tag
<!-- Put this inside <head> -- without it, mobile browsers ignore your responsive CSS --> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- width=device-width tells the browser: use the actual screen width, not a fake 980px --> <!-- initial-scale=1.0 means: do not zoom in or out on first load -->
⚠️ This tag is not optional. Without it, even perfectly written media queries and fluid layouts will be ignored by mobile browsers. It is the single most important line for responsive design.

Responsive CSS Units

Fixed pixel values like width: 400px do not respond to screen size changes. Responsive units do. Here are the ones you need to know.

Percentages — Relative to Parent

A percentage width means "take up this much of my parent container". If the parent is 800px wide, width: 50% means 400px. If the parent is 400px wide, the same rule gives 200px. The element automatically adapts.

CSS — percentage widths adapt to their container
/* Fixed — same width regardless of screen */ .card { width: 400px; } /* overflows on small phones */ /* Fluid — adapts to the container */ .card { width: 100%; } /* always fills the container */ .card { width: 50%; } /* half the container — two cards per row */ /* max-width prevents it from getting too wide on large screens */ .container { width: 100%; max-width: 1200px; /* never wider than 1200px */ margin: 0 auto; /* centres it horizontally */ padding: 0 20px; /* breathing room on small screens */ }

em and rem — Scalable Type Sizes

rem is relative to the root font size (the html element). If the root is 16px, then 1rem = 16px, 2rem = 32px. If the user has changed their browser font size for accessibility, rem values scale with it — px values do not.

em is relative to the current element's font size. It is useful for padding and spacing that should scale proportionally with the text size.

CSS — rem for type, em for spacing
/* Root font size — everything rem-based scales from here */ html { font-size: 16px; } /* rem — predictable, based on root */ h1 { font-size: 2.5rem; } /* 40px */ h2 { font-size: 1.75rem; } /* 28px */ p { font-size: 1rem; } /* 16px */ small{ font-size: 0.875rem; } /* 14px */ /* em — padding scales with the element's own font size */ .button { font-size: 1rem; padding: 0.75em 1.5em; /* 12px top/bottom, 24px left/right */ } /* If you change font-size to 1.25rem, the padding scales proportionally */

vw and vh — Viewport Units

vw is 1% of the viewport width. vh is 1% of the viewport height. 100vw means "exactly as wide as the screen". These are useful for hero sections, full-screen elements and fluid typography.

CSS — viewport units for full-screen layouts
/* Full-screen hero section */ .hero { width: 100vw; height: 100vh; /* takes up the entire visible screen */ display: flex; align-items: center; justify-content: center; } /* Fluid font size that scales with viewport width */ h1 { font-size: 5vw; } /* 5% of screen width — shrinks on phones */ /* Sticky sidebar that takes full viewport height */ .sidebar { height: 100vh; position: sticky; top: 0; overflow-y: auto; }

clamp() — Fluid Values with Min and Max

clamp(min, preferred, max) sets a value that scales fluidly between a minimum and maximum. It is perfect for font sizes and spacing — the value grows with the screen but never gets too small or too large.

CSS — clamp() for smooth scaling without media queries
/* font-size is at least 1rem, scales fluidly, never exceeds 3rem */ h1 { font-size: clamp(1.5rem, 4vw, 3rem); } /* padding grows with screen width but stays within sensible bounds */ .section { padding: clamp(1rem, 5vw, 4rem); } /* container max-width with fluid side padding */ .container { width: clamp(320px, 90%, 1200px); margin: 0 auto; } /* On a 375px phone: h1 = max(1.5rem, 4% of 375px) = max(1.5rem, 15px) = 1.5rem On a 768px tablet: h1 = 4% of 768px = 30.7px = ~1.92rem On a 1440px desktop: min(3rem, 57.6px) = 3rem (capped) */

Media Queries — Applying CSS at Specific Screen Sizes

A media query is a CSS rule that only applies when a condition is true — most commonly when the screen is narrower or wider than a given width. You use them to completely change the layout, hide elements, adjust font sizes or anything else at different breakpoints.

Common Breakpoints

📱
Mobile
0 – 767px
Phones
📱
Tablet
768 – 1023px
Tablets, large phones
💻
Desktop
1024 – 1439px
Laptops, small monitors
🖥️
Wide
1440px +
Large desktop monitors
ℹ️ Breakpoints are not rigid rules. These are the most common values but the best breakpoints for your site are wherever your design starts to look bad as you resize the browser. Design first, add breakpoints where needed.

Media Query Syntax

CSS — media query syntax and common patterns
/* min-width: applies when screen is AT LEAST this wide */ @media (min-width: 768px) { .container { max-width: 720px; } } /* max-width: applies when screen is NO WIDER than this */ @media (max-width: 767px) { .nav-links { display: none; } /* hide desktop nav on mobile */ } /* range: applies between two widths */ @media (min-width: 768px) and (max-width: 1023px) { .grid { grid-template-columns: repeat(2, 1fr); } } /* Modern range syntax (Chrome 113+) */ @media (768px <= width < 1024px) { .grid { grid-template-columns: repeat(2, 1fr); } } /* orientation: portrait or landscape */ @media (orientation: landscape) { .hero { height: 60vh; } /* shorter hero in landscape mode */ } /* prefers-reduced-motion: respect accessibility preferences */ @media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; } }

Mobile-First Design — Write Small Screens First

There are two approaches to responsive design. Desktop-first means you design for big screens and use max-width media queries to shrink things down for smaller screens. Mobile-first means you design for the smallest screen first and use min-width media queries to enhance the layout as the screen gets bigger.

Mobile-first is the industry standard today. There are several good reasons: it forces you to prioritise what actually matters (limited space), it results in cleaner and less CSS overall, and it naturally aligns with how Google indexes pages.

CSS — desktop-first vs mobile-first approach
/* ── DESKTOP-FIRST (start big, shrink down) ──────────────────── */ .card-grid { display: grid; grid-template-columns: repeat(3, 1fr); /* 3 columns on desktop */ } @media (max-width: 1023px) { .card-grid { grid-template-columns: repeat(2, 1fr); } } @media (max-width: 767px) { .card-grid { grid-template-columns: 1fr; } /* 1 column on phone */ } /* ── MOBILE-FIRST (start small, enhance up) ──────────────────── */ .card-grid { display: grid; grid-template-columns: 1fr; /* 1 column on phone — base style */ } @media (min-width: 768px) { .card-grid { grid-template-columns: repeat(2, 1fr); } } @media (min-width: 1024px) { .card-grid { grid-template-columns: repeat(3, 1fr); } } /* Mobile-first is shorter and clearer — the base styles are for phones */

Flexbox for Responsive Layouts

Flexbox is great for one-dimensional layouts — things that need to be arranged in a row or a column. It handles navigation bars, card rows, centring elements and wrapping content onto new lines automatically.

flex-wrap — Let Items Wrap to a New Line

CSS — flex-wrap lets items wrap onto new lines automatically
.card-row { display: flex; flex-wrap: wrap; /* items wrap to next line when they run out of space */ gap: 20px; } .card { flex: 1 1 280px; /* grow | shrink | basis */ /* flex-basis: 280px = each card wants to be at least 280px wide flex-grow: 1 = if there is extra space, all cards grow equally flex-shrink: 1 = if space is tight, cards shrink equally */ } /* Result: 3 cards per row on wide screens, 1 per row on phones */ /* No media queries needed for this particular behaviour */

flex-direction — Switch Between Row and Column

CSS — switching from row to column layout with a media query
.feature { display: flex; flex-direction: column; /* stacked on mobile — image above text */ gap: 24px; } @media (min-width: 768px) { .feature { flex-direction: row; /* side by side on tablet and up */ align-items: center; } .feature-image { width: 45%; flex-shrink: 0; } .feature-text { flex: 1; } } /* Flip the order — text first on desktop even if image is first in HTML */ @media (min-width: 768px) { .feature.reversed { flex-direction: row-reverse; } }

CSS Grid for Responsive Layouts

Grid is the best tool for two-dimensional layouts — page structures with rows and columns. It handles full page layouts, photo galleries, card grids and any design where you need elements to align in both directions at once.

auto-fill and minmax — Automatic Responsive Columns

This combination is one of the most powerful patterns in responsive CSS. It creates as many columns as fit in the container, each at least a given minimum width. No media queries needed for the columns themselves.

CSS — auto-fill grid that adjusts columns automatically
.grid { display: grid; /* auto-fill: create as many columns as fit minmax(260px, 1fr): each column is at least 260px, max 1fr (equal share) */ grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 24px; } /* On a 375px phone: 1 column (375px / 260px minimum = 1 fits) On a 700px tablet: 2 columns (700px / 260px minimum = 2 fit) On a 1200px desktop: 4 columns (1200px / 260px minimum = 4 fit) */ /* auto-fit vs auto-fill: auto-fill: empty columns still take up space auto-fit: empty columns collapse — items stretch to fill the row */ .grid-stretch { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }

Named Grid Areas — Readable Page Layouts

Grid areas let you name zones in your layout and rearrange them visually in CSS without changing the HTML order. On mobile you stack everything. On desktop you put them side by side. The HTML does not change at all.

CSS — page layout with named grid areas that rearrange at breakpoints
/* HTML structure — order does not matter */ <div class="page"> <header>...</header> <nav>...</nav> <main>...</main> <aside>...</aside> <footer>...</footer> </div> /* Mobile layout — single column, all stacked */ .page { display: grid; grid-template-areas: "header" "nav" "main" "aside" "footer"; } /* Desktop layout — sidebar next to main content */ @media (min-width: 1024px) { .page { grid-template-columns: 240px 1fr; grid-template-areas: "header header" "nav main" "nav aside" "footer footer"; } } /* Assign each element to its named area */ header { grid-area: header; } nav { grid-area: nav; } main { grid-area: main; } aside { grid-area: aside; } footer { grid-area: footer; }

Responsive Images

Images are a very common cause of broken mobile layouts. A 1200px wide image placed in a 375px container will either overflow and cause horizontal scrolling, or it will be tiny and hard to see. Two CSS rules and the srcset attribute solve this.

CSS — making all images fluid by default
/* The two most important CSS rules for responsive images */ img { max-width: 100%; /* never wider than its container */ height: auto; /* maintain aspect ratio */ display: block; /* removes the default inline gap below images */ } /* Object-fit for images in a fixed container */ .card-image { width: 100%; height: 200px; object-fit: cover; /* crop to fill without distorting */ object-position: center; }
HTML — srcset for serving different image sizes per device
<!-- The browser picks the most appropriate image based on screen size --> <img src="hero-800.jpg" srcset="hero-400.jpg 400w, hero-800.jpg 800w, hero-1200.jpg 1200w" sizes="(max-width: 600px) 100vw, (max-width: 1024px) 80vw, 1200px" alt="Hero image" loading="lazy" > <!-- srcset: tells the browser which image file corresponds to which width --> <!-- sizes: tells the browser how wide the image will be rendered --> <!-- loading="lazy": only load the image when it scrolls into view -->

Responsive Typography

Text that is the right size on a desktop can be too large and wasteful on a phone, or too small and unreadable. Responsive typography means type that scales gracefully across all screen sizes without needing a different font size at every breakpoint.

CSS — fluid typography that scales automatically
/* Method 1: clamp — smooth scaling with no media queries */ h1 { font-size: clamp(1.75rem, 4vw, 3.5rem); } h2 { font-size: clamp(1.25rem, 3vw, 2.25rem); } p { font-size: clamp(0.9rem, 1.5vw, 1.1rem); } /* Method 2: media queries — explicit control at each breakpoint */ h1 { font-size: 1.75rem; } /* mobile base */ @media (min-width: 768px) { h1 { font-size: 2.5rem; } } @media (min-width: 1024px) { h1 { font-size: 3.5rem; } } /* Always set a comfortable line-height for readability */ body { font-size: 1rem; line-height: 1.7; } /* Prevent long words from overflowing narrow containers */ p, h1, h2, h3 { overflow-wrap: break-word; hyphens: auto; }

Full Page Layout Example

Here is a complete responsive page layout that uses everything covered in this guide together — mobile-first approach, CSS Grid, Flexbox, media queries and the viewport meta tag:

HTML + CSS — complete responsive page layout
<!-- Viewport meta — REQUIRED --> <meta name="viewport" content="width=device-width, initial-scale=1.0"> /* ── Reset and base ─────────────────────────────── */ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: system-ui, sans-serif; font-size: 1rem; line-height: 1.7; } img { max-width: 100%; height: auto; display: block; } /* ── Container ──────────────────────────────────── */ .container { width: 100%; max-width: 1200px; margin: 0 auto; padding: 0 clamp(16px, 5vw, 40px); } /* ── Page grid — mobile stacks, desktop has sidebar ─ */ .page-layout { display: grid; grid-template-columns: 1fr; /* single column on mobile */ gap: 40px; padding-top: 40px; } @media (min-width: 1024px) { .page-layout { grid-template-columns: 1fr 300px; } /* main + sidebar */ } /* ── Card grid — auto-responsive, no media queries needed ─ */ .cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 24px; } .card { background: #fff; border: 1px solid #e5e7eb; border-radius: 12px; overflow: hidden; } .card-image { width: 100%; aspect-ratio: 16 / 9; /* maintains ratio without fixed heights */ object-fit: cover; } .card-body { padding: clamp(16px, 3vw, 24px); } /* ── Hero section ────────────────────────────────── */ .hero { text-align: center; padding: clamp(60px, 10vh, 120px) 20px; } .hero h1 { font-size: clamp(2rem, 5vw, 4.5rem); line-height: 1.15; } .hero p { font-size: clamp(1rem, 2vw, 1.25rem); max-width: 60ch; margin: 1rem auto; }

Testing Responsiveness

Writing responsive CSS without testing it is guesswork. Here is how to properly test your layout at every screen size.

Testing checklist — what to check at each breakpoint
── Chrome DevTools (F12) ──────────────────────────────────────── # Click the device icon (top-left of DevTools) to enter responsive mode # Drag the viewport edge to resize — watch for layout breaks # Use the device dropdown to test specific phones (iPhone SE, Pixel 7, etc.) # Check both portrait and landscape orientations ── Checklist at 375px (smallest common phone) ─────────────────── # ✓ No horizontal scrollbar appears # ✓ All text is readable without zooming (min 16px for body) # ✓ Tap targets are at least 44x44px (buttons, links) # ✓ Images do not overflow their containers # ✓ Navigation is accessible (hamburger menu works) # ✓ Forms are usable with a phone keyboard ── Checklist at 768px (tablet) ───────────────────────────────── # ✓ Layout transitions smoothly from 1 to 2 columns # ✓ No awkward gaps or orphaned elements # ✓ Images are not blurry or over-stretched ── Checklist at 1200px+ (desktop) ────────────────────────────── # ✓ Content does not stretch uncomfortably wide # ✓ max-width container is centred # ✓ Typography is not too large

Quick Reference

TechniqueWhat It DoesBest For
Viewport meta tagTells mobile browsers to use real screen widthEvery HTML page — required
% widthsScales relative to parent containerFluid layouts, column widths
remScales with root font sizeFont sizes, accessible spacing
vw / vhPercentage of viewport dimensionsFull-screen sections, fluid type
clamp()Fluid value between min and maxTypography, padding — no breakpoints needed
Media queriesApply CSS only at certain screen widthsLayout changes, showing/hiding elements
Mobile-firstBase styles for small screens, enhance upAll new projects — industry standard
Flexbox flex-wrapItems flow and wrap to new lines naturallyCard rows, nav bars, tag clouds
Grid auto-fill minmaxColumns adjust automatically to available spaceCard grids, galleries
Grid template areasName layout zones, rearrange at breakpointsFull page layouts with header/sidebar/footer
max-width: 100%Images never overflow their containerEvery image on every page
aspect-ratioMaintains image/video ratio without fixed heightsConsistent card images

⚡ Key Takeaways
  • Add <meta name="viewport" content="width=device-width, initial-scale=1.0"> to every HTML page. Without it, mobile browsers ignore your responsive CSS entirely.
  • Use percentage widths instead of fixed pixels for containers and columns. Add max-width to prevent content from stretching too wide on large monitors, and margin: 0 auto to centre it.
  • Use rem for font sizes so they scale with the user's browser font preference (accessibility). Use em for padding that should scale proportionally with the element's own text size.
  • clamp(min, preferred, max) creates smooth fluid scaling between a minimum and maximum value. It is the cleanest way to handle responsive typography and spacing without writing a media query for every size change.
  • Media queries apply CSS only when a condition is true. Use min-width for mobile-first (start small, enhance up) and max-width for desktop-first (start big, shrink down). Mobile-first is the industry standard.
  • The best breakpoints are not 768px or 1024px by definition — they are wherever your design starts to look bad as you resize the browser. Add breakpoints where needed, not where a framework tells you to.
  • Flexbox is best for one-dimensional layouts — rows or columns. Use flex-wrap: wrap combined with flex: 1 1 280px to get automatically wrapping card rows with no media queries.
  • CSS Grid with auto-fill and minmax creates automatically responsive column grids that adjust to available space without any media queries: grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)).
  • Always set max-width: 100% and height: auto on every image. This prevents images from overflowing their containers on small screens.
  • Test at 375px (smallest common phone) by dragging the browser window narrow in Chrome DevTools. Check for: no horizontal scroll, readable text, tap targets at least 44px, no overflowing images.