Skip to content
beginner css 12 min read

CSS Media Queries: Complete Guide to Responsive Design

Master CSS media queries for building truly responsive layouts across all screen sizes.

css media queries media query css responsive css breakpoints css

What Are CSS Media Queries?

CSS media queries let you apply styles conditionally based on the characteristics of the user's device or viewport. Think of them as if statements in CSS: "if the screen is wider than 768px, apply these styles."

The core syntax looks like this:

@media <media-type> and (<media-feature>) {
  /* styles applied when condition is true */
}

Media queries sit at the foundation of responsive CSS. While Flexbox and Grid handle internal layout fluidity, media queries control the structural shifts — when a sidebar collapses, when a nav becomes a hamburger menu, when a single column becomes three.

Core Syntax and Components

Media Types

The three media types you'll actually use:

  • screen — for screens (monitors, phones, tablets)
  • print — for printed pages
  • all — matches everything (this is the default when omitted)

Most of your queries will target screen, or omit the type entirely since all is implied.

Media Features

Media features are the conditions inside parentheses. The most common is width, but there are many others:

/* Viewport width range */
@media (min-width: 768px) { }

/* Exact width (avoid this in practice) */
@media (width: 768px) { }

/* Device orientation */
@media (orientation: landscape) { }

/* Touch vs. pointer devices */
@media (hover: none) { }          /* touch devices */
@media (hover: hover) { }         /* mouse/trackpad */
@media (pointer: coarse) { }      /* fat fingers */

/* User preferences */
@media (prefers-reduced-motion: reduce) { }
@media (prefers-color-scheme: dark) { }

Combining Conditions

Use and to require multiple conditions, or commas for OR logic:

/* Both conditions must be true */
@media screen and (min-width: 768px) and (orientation: landscape) {
  .sidebar { display: block; }
}

/* Either condition triggers the styles */
@media (max-width: 600px), (orientation: portrait) {
  .nav { flex-direction: column; }
}

Mobile-First vs. Desktop-First

This is the most consequential decision you'll make when writing responsive CSS. Mobile-first means you write base styles for small screens, then use min-width queries to progressively enhance for larger screens.

/* Mobile-first approach */
.nav {
  display: flex;
  flex-direction: column;   /* stacked on mobile */
  gap: 0.5rem;
}

@media (min-width: 768px) {
  .nav {
    flex-direction: row;    /* horizontal on tablet+ */
    gap: 2rem;
  }
}

@media (min-width: 1024px) {
  .nav {
    justify-content: space-between;
  }
}

Desktop-first does the opposite — base styles for large screens, max-width queries to scale down.

Mobile-first wins for most projects because:

  • Mobile CSS loads first; desktop enhancements are additive
  • You avoid the "undo" problem where mobile styles override desktop defaults
  • It aligns with how browsers and search engines prioritize content

The exception: if you're building an internal tool or dashboard used primarily on desktop, desktop-first may feel more natural.

Choosing Your Breakpoints CSS

The biggest mistake developers make is targeting specific devices. Forget memorizing iPhone dimensions. Instead, let your content tell you where breakpoints belong.

Open your browser's dev tools, drag the viewport narrower, and watch where your layout breaks. That's where you add a breakpoint.

That said, here's a practical starting set of breakpoints that works well for most projects:

/* Base: mobile (no query needed) */

/* Small tablets and large phones */
@media (min-width: 480px) { }

/* Tablets */
@media (min-width: 768px) { }

/* Laptops and small desktops */
@media (min-width: 1024px) { }

/* Large desktops */
@media (min-width: 1280px) { }

Use em Units for Breakpoints

Pixel-based breakpoints ignore user browser font size settings. When a user sets their browser default to 20px, a 768px breakpoint behaves differently than expected. Use em instead:

/* 768px ÷ 16 = 48em */
@media (min-width: 48em) {
  .grid { grid-template-columns: repeat(2, 1fr); }
}

/* 1024px ÷ 16 = 64em */
@media (min-width: 64em) {
  .grid { grid-template-columns: repeat(3, 1fr); }
}

Building a Responsive Grid

Here's a complete, copy-paste-ready responsive grid using mobile-first media queries. You can prototype the grid structure with our CSS Grid Generator before adding the responsive logic.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
    }

    .grid {
      display: grid;
      grid-template-columns: 1fr;        /* 1 column: mobile */
      gap: 1rem;
      padding: 1rem;
      max-width: 1200px;
      margin: 0 auto;
    }

    .card {
      background: #f5f5f5;
      border-radius: 8px;
      padding: 1.5rem;
      border: 1px solid #e0e0e0;
    }

    /* Tablet: 2 columns */
    @media (min-width: 48em) {
      .grid {
        grid-template-columns: repeat(2, 1fr);
        gap: 1.5rem;
        padding: 1.5rem;
      }
    }

    /* Desktop: 3 columns */
    @media (min-width: 64em) {
      .grid {
        grid-template-columns: repeat(3, 1fr);
        gap: 2rem;
        padding: 2rem;
      }
    }

    /* Landscape phones: slight gap increase */
    @media (orientation: landscape) and (max-width: 47.9em) {
      .grid { gap: 1.5rem; }
    }
  </style>
</head>
<body>
  <div class="grid">
    <div class="card"><h2>Card One</h2><p>Content here.</p></div>
    <div class="card"><h2>Card Two</h2><p>Content here.</p></div>
    <div class="card"><h2>Card Three</h2><p>Content here.</p></div>
    <div class="card"><h2>Card Four</h2><p>Content here.</p></div>
    <div class="card"><h2>Card Five</h2><p>Content here.</p></div>
    <div class="card"><h2>Card Six</h2><p>Content here.</p></div>
  </div>
</body>
</html>

Don't forget the <meta name="viewport"> tag — without it, mobile browsers render at desktop width and your media queries effectively stop working.

Modern Feature Queries Worth Using

Respecting User Preferences

These queries tap into OS-level settings and meaningfully improve accessibility:

/* Respect reduced motion preferences */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* Dark mode support */
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #1a1a1a;
    --text: #f0f0f0;
    --border: #333;
  }
}

/* Light mode (default) */
:root {
  --bg: #ffffff;
  --text: #1a1a1a;
  --border: #e0e0e0;
}

body {
  background-color: var(--bg);
  color: var(--text);
}

Adapting for Touch Devices

/* Default: assume hover-capable device */
.btn {
  transition: background-color 0.2s ease;
}

.btn:hover {
  background-color: #0056b3;
}

/* Touch devices: remove hover effects, increase tap targets */
@media (hover: none) and (pointer: coarse) {
  .btn {
    min-height: 44px;         /* Apple's recommended tap target */
    min-width: 44px;
  }

  .btn:hover {
    background-color: inherit; /* disable hover on touch */
  }
}

Container Queries: The Modern Complement

Container queries are now universally supported (Chrome/Edge 105+, Firefox 110+, Safari 16+) and solve a problem media queries can't: component-level responsiveness based on the component's own size, not the viewport.

/* Set up the container */
.card-wrapper {
  container-type: inline-size;
  container-name: card;
}

/* Style the card based on its container width */
.card {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

/* When the card-wrapper is at least 400px wide */
@container card (min-width: 25rem) {
  .card {
    flex-direction: row;
    align-items: center;
  }

  .card__image {
    width: 200px;
    flex-shrink: 0;
  }
}

This approach means you can drop .card-wrapper into a narrow sidebar or a full-width section, and the card will respond to its actual available space — not the viewport width. This makes components genuinely reusable.

Use container queries for component-level adaptation and viewport media queries for page-level structural shifts. They complement each other.

Avoiding Common Mistakes

Avoid overlapping ranges. If you use both min-width and max-width queries, be careful about which ranges share a boundary:

/* Problem: 768px matches BOTH queries */
@media (max-width: 768px) { }
@media (min-width: 768px) { }

/* Fix: use 769px or shift to mobile-first min-width only */
@media (max-width: 767px) { }
@media (min-width: 768px) { }

Don't overuse media queries. If you find yourself writing dozens of queries, you probably need more fluid base styles. The Flexbox Generator can help you build flex layouts that adapt without breakpoints. Intrinsic sizing with clamp(), minmax(), and auto-fit often eliminates entire breakpoints.

Keep queries close to components. Scattering all your media queries at the bottom of a massive stylesheet makes maintenance painful. Co-locate them with the component they affect:

/* ✅ Component-organized */
.nav { /* base styles */ }
@media (min-width: 48em) { .nav { /* tablet */ } }
@media (min-width: 64em) { .nav { /* desktop */ } }

.hero { /* base styles */ }
@media (min-width: 48em) { .hero { /* tablet */ } }

Browser Compatibility

Basic media queries (min-width, max-width, orientation) work in every browser including IE9+, so you have effectively 100% coverage there. Modern features:

Feature Chrome Firefox Safari
min-width / max-width All All All
prefers-color-scheme 76+ 67+ 12.1+
prefers-reduced-motion 74+ 63+ 10.1+
Container queries 105+ 110+ 16+
hover / pointer 38+ 64+ 9+

For container queries, you can progressively enhance with @supports:

@supports (container-type: inline-size) {
  .card-wrapper {
    container-type: inline-size;
  }
}

Format your CSS cleanly before production — the CSS Formatter can help normalize inconsistent whitespace in media query blocks.

Next Steps

Now that you understand the fundamentals of CSS media queries, here's where to go next:

  • CSS Grid Generator — Visually build grid layouts that you can then make responsive with the techniques in this guide
  • Flexbox Generator — Generate flexible container styles that reduce how many breakpoints you need
  • CSS Formatter — Clean up and normalize your responsive CSS before shipping

From here, dig deeper into:

  • CSS clamp() and fluid typography — Replace some breakpoints with smooth scaling using clamp(1rem, 2.5vw, 1.5rem)
  • CSS Grid's intrinsic sizinggrid-template-columns: repeat(auto-fit, minmax(280px, 1fr)) creates responsive grids with zero media queries
  • Container query unitscqi, cqb, and related units let you size elements proportionally to their container

The best responsive CSS uses media queries strategically, not exhaustively. Combine them with fluid layouts and modern sizing functions, and you'll write far less code while supporting far more devices.

Related Tools

Continue Learning