CSS@media

@media: The Complete Deep-Dive

@media is the workhorse of responsive design. It applies a block of CSS only when the environment matches: viewport size, device orientation, display resolution, input capabilities, or user preferences like dark mode and reduced motion. This page covers the full syntax — including the modern range syntax — and the strategy questions: mobile-first vs desktop-first, and which features to query.

Anatomy of a Media Query

CSS
@media media-type and (media-feature) {
  /* rules applied only when the query matches */
}

/* Real example */
@media screen and (min-width: 768px) {
  .nav { display: flex; }
}

/* The media type is optional — this is equivalent for screens */
@media (min-width: 768px) {
  .nav { display: flex; }
}
Media Types

Type

Matches

Common use

all

Every device (default)

Rarely written explicitly

screen

Monitors, phones, tablets

Almost always implied

print

Print preview and printed pages

Print stylesheets

speech

Screen readers reading aloud

Rarely used in practice

CSS
/* Hide navigation and expand content when printing */
@media print {
  nav, footer, .ads { display: none; }
  main { width: 100%; }
  a::after { content: " (" attr(href) ")"; }
}
Width and Height Features

The classic responsive features. min-width means "at this width and above"; max-width means "at this width and below".

CSS
/* Applies at 768px and wider */
@media (min-width: 768px) {
  .grid { grid-template-columns: repeat(2, 1fr); }
}

/* Applies at 767.98px and narrower */
@media (max-width: 767.98px) {
  .sidebar { display: none; }
}

/* Height queries — useful for short landscape phones */
@media (max-height: 500px) {
  .hero { min-height: auto; padding-block: 2rem; }
}
Use rem in breakpoints
Breakpoints in rem (e.g. 48rem instead of768px) respect users who increase their default font size — the layout adapts earlier, exactly when text needs more room.
Modern Range Syntax

Media Queries Level 4 lets you write comparisons directly, which is clearer and avoids the off-by-0.02px dance between max-width and min-width:

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

/* New range syntax — supported in all modern browsers */
@media (768px <= width < 1200px) {
  .layout { padding-inline: 2rem; }
}

/* Single comparisons work too */
@media (width >= 768px)  { ... }
@media (width < 480px)   { ... }
Orientation

CSS
@media (orientation: landscape) {
  .gallery { grid-template-columns: repeat(4, 1fr); }
}

@media (orientation: portrait) {
  .gallery { grid-template-columns: repeat(2, 1fr); }
}
Note
Orientation is derived from the aspect ratio, not the device sensor. A desktop window taller than it is wide reportsportrait. Prefer width queries for layout decisions and orientation only when the distinction genuinely matters.
User Preference Queries

These features query what the user asked their operating system for. Respecting them is table stakes for accessibility.

CSS
/* Dark mode */
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0f1115;
    --text: #e6e6e6;
  }
}

/* Reduced motion — never ignore this one */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Higher contrast */
@media (prefers-contrast: more) {
  .card { border: 2px solid currentColor; }
}
Input Capability: hover and pointer

Instead of guessing "is this a phone?", query what the primary input can actually do. hover asks whether the device can hover; pointer asks how precise the pointing device is.

Query

Matches

Typical device

(hover: hover)

Primary input can hover

Mouse, trackpad

(hover: none)

No hover capability

Touchscreen

(pointer: fine)

Precise pointer

Mouse, stylus

(pointer: coarse)

Imprecise pointer

Finger on touchscreen

(any-hover: hover)

Any attached input hovers

Laptop with touchscreen

CSS
/* Only add hover effects where hover exists —
   avoids "sticky hover" on touchscreens */
@media (hover: hover) and (pointer: fine) {
  .card:hover {
    transform: translateY(-4px);
    box-shadow: 0 8px 24px rgb(0 0 0 / 0.15);
  }
}

/* Bigger tap targets for coarse pointers */
@media (pointer: coarse) {
  .icon-button { min-width: 44px; min-height: 44px; }
}
Resolution

CSS
/* Serve crisper assets on high-density (retina) screens */
@media (min-resolution: 2dppx) {
  .logo {
    background-image: url("logo@2x.png");
    background-size: 120px 40px;
  }
}
Tip
For images, srcset on the img element usually beats resolution media queries — the browser picks the best candidate automatically.
Combining Queries: and, or, not

CSS
/* AND — every condition must match */
@media (min-width: 768px) and (orientation: landscape) { ... }

/* OR — comma-separated list, any query may match */
@media (max-width: 480px), (orientation: portrait) { ... }

/* NOT — negates one whole query (needs a media type) */
@media not screen and (min-width: 768px) { ... }

/* Level 4: or / not inside conditions */
@media ((min-width: 768px) or (orientation: landscape)) { ... }
Mobile-First vs Desktop-First

Mobile-first writes base styles for the smallest screen, then layers on min-width queries as space grows. Desktop-first does the reverse with max-width. Mobile-first is the recommended default: the base experience works everywhere, and each breakpoint adds complexity instead of undoing it.

CSS
/* Mobile-first: base = single column */
.cards {
  display: grid;
  gap: 1rem;
}

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

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

CSS
/* Desktop-first: you spend queries UNDOING styles */
.cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
}

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

@media (max-width: 47.99rem) {
  .cards { grid-template-columns: 1fr; }
}
Breakpoints belong to content, not devices
Do not memorize device widths. Resize your layout until it breaks — that is your breakpoint. Device-specific breakpoints (iPhone width, iPad width) rot as hardware changes.
Media Queries Beyond Stylesheets

HTML
<!-- Conditional stylesheet loading -->
<link rel="stylesheet" href="print.css" media="print" />
<link rel="stylesheet" href="wide.css" media="(min-width: 1024px)" />

<!-- Responsive images -->
<picture>
  <source srcset="hero-wide.avif" media="(min-width: 800px)" />
  <img src="hero.avif" alt="Mountain landscape" />
</picture>

In JavaScript, window.matchMedia('(min-width: 768px)') evaluates the same queries and fires change events — handy for syncing JS behavior with CSS breakpoints.

Key Takeaways
  • The media type (screen/print) is optional; features in parentheses do the real work.

  • Prefer the modern range syntax — (width >= 768px) — it is clearer and supported everywhere modern.

  • Query capabilities (hover, pointer) and preferences (color scheme, motion), not devices.

  • Always honor prefers-reduced-motion.

  • Go mobile-first: base styles for small screens, min-width queries to enhance.

  • Set breakpoints where your content breaks, in rem, not at device widths.

  • Remember media queries also work on link tags, picture sources, and window.matchMedia.