Back to blog
Frontend Engineeringbeginner

Responsive Web Design — Media Queries, Mobile-First, and Fluid Typography

Build websites that work on every screen size. Learn media queries, mobile-first design, fluid typography with clamp(), responsive images, and CSS container queries.

Asma HafeezApril 17, 20266 min read
cssresponsivemobile-firstmedia-queries
Share:𝕏

Responsive Web Design

A responsive website adapts its layout to work well on any screen — from a 320px phone to a 4K monitor. This is not optional. Over 60% of web traffic is mobile.


The Viewport Meta Tag

Without this, mobile browsers render pages at desktop width (980px) and scale them down. Always include it:

HTML
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

Media Queries

Media queries apply CSS only when certain conditions are true.

CSS
/* Basic syntax */
@media (max-width: 768px) {
  /* styles for screens 768px wide or smaller */
}

@media (min-width: 768px) {
  /* styles for screens 768px wide or larger */
}

/* Range (modern syntax) */
@media (width >= 768px) and (width < 1024px) { }

/* Multiple conditions */
@media (min-width: 768px) and (orientation: landscape) { }

/* Dark mode preference */
@media (prefers-color-scheme: dark) { }

/* Reduced motion preference */
@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.01ms !important; }
}

/* Print */
@media print {
  .nav, .sidebar, .ads { display: none; }
}

Mobile-First Design

Write CSS for mobile first, then add media queries for larger screens.

This is the recommended approach because:

  • Mobile styles are simpler (single column, no complex layout)
  • Adding features is easier than removing them
  • Smaller base CSS, progressively enhanced
CSS
/* Mobile-first approach */

/* Base styles — mobile */
.card-grid {
  display: grid;
  grid-template-columns: 1fr;   /* single column on mobile */
  gap: 16px;
}

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

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

Standard Breakpoints

These match Tailwind CSS breakpoints (widely adopted):

CSS
/* sm */ @media (min-width: 640px)  { }
/* md */ @media (min-width: 768px)  { }
/* lg */ @media (min-width: 1024px) { }
/* xl */ @media (min-width: 1280px) { }
/* 2xl*/ @media (min-width: 1536px) { }

Avoid too many breakpoints. Most designs need 2–3.


Fluid Typography with clamp()

clamp(min, preferred, max) scales a value between a minimum and maximum.

CSS
/* Font size that scales from 16px (mobile) to 24px (desktop) */
h1 {
  font-size: clamp(1.5rem, 4vw, 2.5rem);
  /*         min    preferred  max       */
}

p {
  font-size: clamp(1rem, 1.5vw, 1.125rem);
}

/* Fluid spacing */
.section {
  padding: clamp(40px, 8vw, 100px);
}

This eliminates the need for font-size media queries in many cases.


Flexible Layouts

Fluid Grid (auto-fill)

CSS
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr));
  gap: 20px;
}
/* Cards are min 280px wide but fill the row. No media queries needed. */

Responsive Navigation

CSS
/* Mobile: hamburger */
.nav-links { display: none; }
.hamburger { display: block; }

/* Desktop: show links, hide hamburger */
@media (min-width: 768px) {
  .nav-links { display: flex; }
  .hamburger { display: none; }
}

Stack to Row Pattern

CSS
.hero {
  display: flex;
  flex-direction: column; /* stack on mobile */
  gap: 24px;
}

@media (min-width: 768px) {
  .hero {
    flex-direction: row;    /* side by side on desktop */
    align-items: center;
  }
}

Responsive Images

HTML
<!-- Basic: let image scale with container -->
<img src="photo.jpg" alt="Description" />

<style>
img {
  max-width: 100%;  /* never wider than container */
  height: auto;     /* maintain aspect ratio */
  display: block;
}
</style>

srcset — Different Sizes for Different Screens

HTML
<!-- Browser picks the right size based on screen -->
<img
  src="photo-800.jpg"
  srcset="
    photo-400.jpg 400w,
    photo-800.jpg 800w,
    photo-1200.jpg 1200w
  "
  sizes="
    (max-width: 640px) 100vw,
    (max-width: 1024px) 50vw,
    33vw
  "
  alt="Photo description"
/>

Picture — Art Direction (Different Crops)

HTML
<picture>
  <!-- Portrait crop for mobile -->
  <source
    media="(max-width: 640px)"
    srcset="photo-portrait.jpg"
  />
  <!-- Landscape for desktop -->
  <source
    media="(min-width: 641px)"
    srcset="photo-landscape.jpg"
  />
  <!-- Fallback -->
  <img src="photo-landscape.jpg" alt="Photo" />
</picture>

CSS Container Queries

Newer than media queries — respond to the container's size, not the viewport's.

CSS
/* Define a containment context */
.card-wrapper {
  container-type: inline-size;
  container-name: card;
}

/* Style the card based on its container's width */
@container card (min-width: 400px) {
  .card {
    display: flex;
    flex-direction: row;
  }
  .card img {
    width: 160px;
    flex-shrink: 0;
  }
}

Container queries are perfect for reusable components that need to adapt to wherever they're placed, regardless of viewport size.


Complete Responsive Layout Example

CSS
:root {
  --max-width: 1200px;
  --spacing-sm: 16px;
  --spacing-md: 24px;
  --spacing-lg: 48px;
  --spacing-xl: 80px;
}

/* Page wrapper */
.page {
  display: grid;
  grid-template-rows: auto 1fr auto;
  min-height: 100vh;
}

/* Constrained width + centered */
.container {
  width: 100%;
  max-width: var(--max-width);
  margin-inline: auto;
  padding-inline: clamp(16px, 4vw, 48px);
}

/* Header */
.header {
  padding-block: var(--spacing-sm);
}

/* Hero — stacks on mobile, side-by-side on desktop */
.hero {
  padding-block: clamp(40px, 10vw, 120px);
  display: grid;
  gap: var(--spacing-lg);
  align-items: center;
}
@media (min-width: 768px) {
  .hero { grid-template-columns: 1fr 1fr; }
}

.hero h1 {
  font-size: clamp(2rem, 5vw, 4rem);
  line-height: 1.1;
}

/* Content sections */
.section {
  padding-block: clamp(var(--spacing-lg), 8vw, var(--spacing-xl));
}

/* Card grid — fluid, no breakpoints needed */
.cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr));
  gap: var(--spacing-md);
}

/* Feature section — stack on mobile, split on desktop */
.feature {
  display: grid;
  gap: var(--spacing-md);
}
@media (min-width: 1024px) {
  .feature { grid-template-columns: 1fr 1fr; }
  .feature.reverse .feature-image { order: -1; }
}

/* Sidebar layout */
.with-sidebar {
  display: grid;
  gap: var(--spacing-md);
}
@media (min-width: 900px) {
  .with-sidebar {
    grid-template-columns: 240px 1fr;
  }
}

Testing Responsive Design

  1. Chrome DevTools: F12 → Toggle device toolbar (Ctrl+Shift+M)
  2. Resize browser window manually across breakpoints
  3. Real device testing: BrowserStack or your own phone
  4. Accessibility: Test with keyboard navigation and screen reader

Key Takeaways

  1. Mobile-first: write base CSS for mobile, use min-width media queries to enhance
  2. clamp() for fluid typography and spacing without media queries
  3. auto-fill + minmax for responsive grids without breakpoints
  4. max-width: 100%; height: auto on all images
  5. Container queries for component-level responsiveness
  6. Test on real devices — emulators miss touch and performance issues

Enjoyed this article?

Explore the Frontend Engineering learning path for more.

Found this helpful?

Share:𝕏

Leave a comment

Have a question, correction, or just found this helpful? Leave a note below.