TH The Sass Way
Responsive Design

CSS Media Queries: How to Make Your Site Responsive

CSS Media Queries: How to Make Your Site Responsive
tldrA CSS media query applies styles only when the browser meets a condition, usually viewport width. You wrap CSS in an @media block, like @media (min-width: 768px) { }, and those rules run only when the condition is true. This lets one stylesheet adapt to phones, tablets, and desktops. Write mobile-first with min-width, and always include the viewport meta tag.

What a CSS Media Query Actually Does

A media query is a CSS rule that applies styles only when the browser meets a condition you specify, most often the width of the viewport. You wrap normal CSS in an @media block, and the browser runs those declarations only when the condition is true. That single mechanism is what lets one stylesheet serve a 360px phone and a 1440px monitor without shipping two separate sites.

Here is the whole idea in four lines:

.card {
  padding: 1rem;
}

@media (min-width: 768px) {
  .card {
    padding: 2rem;
  }
}

On narrow screens the card has 1rem of padding. Once the viewport reaches 768px, the second rule wins and padding becomes 2rem. Nothing else changes. Media queries never replace your CSS; they layer conditional overrides on top of it.

The Syntax, Piece by Piece

Every media query has up to three parts: a media type, one or more media features in parentheses, and the styles inside the braces.

@media screen and (min-width: 600px) and (max-width: 1199px) {
  /* styles here apply only in this range */
}

If you omit the media type, the default is all, so @media (min-width: 600px) is exactly equivalent to @media all and (min-width: 600px). That means it also applies when printing, not just on screen. This bare-feature form is the common shorthand precisely because it targets every medium. Add an explicit type only when you specifically mean to target print, or to scope styles to screen and keep them out of print.

Combining Conditions: and, comma, not

Operator Meaning Example
and All conditions must match (min-width: 768px) and (orientation: landscape)
, (comma) Either condition matches (OR) (max-width: 600px), (orientation: portrait)
not Negate the whole query not all and (min-width: 768px)

The comma is an OR: the styles apply if any comma-separated query is true. Mixing them lets you express fairly precise rules without JavaScript.

min-width vs max-width: The Decision That Shapes Your CSS

This is the question most developers get stuck on, so answer it first and the rest of your responsive CSS falls into place.

These are two independent approaches to the same layout. They use different class names (.grid-mf and .grid-df) so you can drop both into one stylesheet to compare them without the base rules colliding. In a real project you would pick one approach and use a single .grid class.

/* Mobile-first: base styles are the phone layout */
.grid-mf {
  display: block;
}
@media (min-width: 768px) {
  .grid-mf {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
  }
}
/* Desktop-first: base styles are the desktop layout */
.grid-df {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
}
@media (max-width: 767px) {
  .grid-df {
    display: block;
  }
}

Both produce the same result here, but the mental models are different. Mobile-first is the modern default because base styles stay simple, you ship less override code, and the smallest devices (often the slowest) get the leanest CSS. If you want the full argument for why, see our guide on mobile-first CSS.

Watch the off-by-one gap

A classic bug: pairing max-width: 768px with min-width: 768px. At exactly 768px both rules can match, causing overlap. Keep ranges from colliding by using max-width: 767.98px, or better, avoid max-width in a mobile-first codebase entirely so you never need paired boundaries.

Choosing Breakpoints

A breakpoint is the viewport width where your layout changes. The single biggest mistake is picking breakpoints to match specific phones. Devices change every year; your content does not.

Set breakpoints where the design breaks, not where a device sits. Widen your browser slowly and watch. The moment a line of text gets too long, a card gets awkwardly wide, or whitespace balloons, that width is a breakpoint. This gives you a layout that looks intentional on hardware that does not exist yet.

That said, a conventional starting set is useful as a reference. These roughly follow the values popular frameworks converged on:

Breakpoint Common min-width Typical target
Small 640px Large phones, small tablets portrait
Medium 768px Tablets
Large 1024px Small laptops, tablets landscape
Extra large 1280px Desktops
2XL 1536px Large monitors

Use these as a skeleton, then add or drop breakpoints based on what your actual content needs. Two well-placed breakpoints often beat five arbitrary ones.

Use rem, not px, for query units

Breakpoints written in px ignore the user's browser font-size preference. Written in rem, they scale with it, which is better for accessibility. 768px becomes 48rem (768 ÷ 16). Note that em/rem in media queries are always relative to the browser default (usually 16px), not the element's font size. If the difference between px, em, and rem is fuzzy, our CSS units guide untangles it.

@media (min-width: 48rem) {
  /* 768px at the default font size, but respects user zoom */
}

Modern Range Syntax

Older media queries lean on min- and max- prefixes. Modern browsers support cleaner comparison operators that read like math:

/* Old way */
@media (min-width: 768px) and (max-width: 1199px) { }

/* New range syntax */
@media (768px <= width <= 1199px) { }

/* Single bound */
@media (width >= 768px) { }

The range syntax also sidesteps the off-by-one boundary problem, since < and <= are explicit. It is supported in all current versions of Chrome, Edge, Firefox, and Safari (Safari gained it in 16.4, early 2023). If you must support older browsers, keep the min-width/max-width form or provide it as a fallback.

Media Features Beyond Width

Width gets the attention, but several other features are genuinely useful in production.

prefers-color-scheme

Respect the user's system dark-mode setting with zero JavaScript:

:root {
  --bg: #ffffff;
  --text: #1a1a1a;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #1a1a1a;
    --text: #f5f5f5;
  }
}

prefers-reduced-motion

Users who get motion sickness can ask the OS to reduce animation. Honor it:

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

orientation and others

Feature What it tests Example value
orientation Portrait vs landscape landscape
aspect-ratio Viewport width-to-height ratio 16/9
resolution Pixel density 2dppx (retina)
hover Whether the device can hover hover / none
pointer Precision of the pointer fine / coarse

hover and pointer are quietly powerful: they let you distinguish a mouse from a touchscreen and, for example, only show hover effects on devices that can actually hover.

The One Line That Makes It All Work

None of this responds correctly on real phones without the viewport meta tag in your HTML <head>:

<meta name="viewport" content="width=device-width, initial-scale=1">

Without it, mobile browsers pretend to be about 980px wide and zoom out, so your min-width queries fire against the wrong number and the page looks like a shrunken desktop. This single tag is the most common reason "my media queries aren't working." Add it to every page.

A Complete, Practical Example

Here is a mobile-first card grid that adapts across three breakpoints, uses rem units, and respects dark mode:

.cards {
  display: grid;
  gap: 1rem;
  grid-template-columns: 1fr;
  padding: 1rem;
}

@media (min-width: 40rem) {
  .cards {
    grid-template-columns: repeat(2, 1fr);
  }
}

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

One column on phones, two on tablets, three on desktops, all from a single stylesheet with no JavaScript. That is the entire promise of responsive design delivered by three small blocks.

When Media Queries Aren't the Right Tool

Media queries respond to the viewport, not to the space a component actually occupies. A sidebar card and a full-width card have the same viewport but very different available widths. For component-level responsiveness, container queries (@container) are the better tool, and they are now supported in all major browsers. Media queries remain the right choice for page-level layout, global spacing, dark mode, and motion preferences. Both fit into the bigger picture covered in our responsive design guide.

Quick Reference

Master those seven habits and media queries stop being a source of mystery bugs and become the reliable backbone of every responsive site you build.

FAQ

How do I use media queries in CSS?

Write your base styles normally, then wrap conditional styles in an @media block that names a feature and value, such as @media (min-width: 768px) { .card { padding: 2rem; } }. The browser applies those rules only when the viewport meets the condition. Include the viewport meta tag in your HTML head so mobile browsers report the real width and the queries fire correctly.

What is the difference between min-width and max-width media queries?

min-width applies styles at that width or wider, so styles cascade upward from small to large screens (mobile-first). max-width applies at that width or narrower, cascading downward from large to small (desktop-first). min-width is the modern default because base styles stay lean and the smallest, slowest devices load the least override CSS. Avoid pairing both on the same value to prevent boundary overlap.

What are the standard media query breakpoints?

Common min-width breakpoints are 640px (large phones), 768px (tablets), 1024px (laptops), 1280px (desktops), and 1536px (large monitors), roughly matching popular frameworks. But these are only a starting skeleton. Set breakpoints where your content actually breaks, not where specific devices sit, so your layout looks intentional on any screen size including hardware that does not exist yet.

Why are my media queries not working?

The most common cause is a missing viewport meta tag. Without <meta name="viewport" content="width=device-width, initial-scale=1"> in your HTML head, mobile browsers assume roughly 980px width and zoom out, so width-based queries test the wrong number. Other causes include specificity conflicts, source order (a later rule overriding your query), or overlapping min-width and max-width ranges that collide at the boundary.

Should I use px, em, or rem in media queries?

Prefer rem or em so breakpoints scale with the user's browser font-size preference, which is better for accessibility. A 768px breakpoint becomes 48rem (768 divided by 16). Note that em and rem in media queries are always relative to the browser default font size, not any element's font size, so both behave identically here. px breakpoints ignore user zoom, so avoid them for width thresholds.

What is the difference between media queries and container queries?

Media queries respond to the viewport, the whole browser window, making them right for page-level layout, global spacing, dark mode, and motion preferences. Container queries (@container) respond to the size of a parent element, so a component adapts to the space it actually occupies rather than the screen. Use container queries for reusable components that appear in sidebars, grids, or varying widths.