Skip to main content

Media Queries: The Complete Guide

Media queries are CSS conditional blocks — they apply styles only when specific conditions about the device, viewport, or user preference are met. They are the primary tool for responsive design.

1. Syntax

@media [media-type] [and | not | ,] (media-feature) {
/* CSS rules */
}
/* Basic — min-width is the most common feature */
@media (min-width: 768px) { .container { max-width: 960px; } }

/* With media type */
@media screen and (min-width: 768px) { }
@media print { body { font-size: 12pt; color: black; } }

/* Combining conditions with 'and' */
@media screen and (min-width: 768px) and (max-width: 1024px) { }

2. Media Types

TypeWhen Applied
allAll devices (default)
screenScreens, tablets, phones
printPrinter or print preview
speechScreen readers

3. Standard Breakpoint Scale

/* ── Mobile base: no media query needed ── */

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

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

/* Large (lg): Tablets landscape / small laptops */
@media (min-width: 1024px) { }

/* Extra Large (xl): Desktops */
@media (min-width: 1280px) { }

/* 2X Large (2xl): Wide monitors */
@media (min-width: 1536px) { }

[!TIP] Don't follow these breakpoints rigidly. The right breakpoint is wherever your design breaks — inspect your design as you resize and add a breakpoint only when needed.

4. Width and Height Features

/* Minimum width — mobile-first (use this primarily) */
@media (min-width: 768px) { }

/* Maximum width — desktop-first */
@media (max-width: 767px) { }

/* Range (Level 4 syntax — cleaner) */
@media (768px <= width < 1024px) { }

/* Height-based */
@media (min-height: 600px) { }
@media (max-height: 400px) { .hero { min-height: 60vh; } }

5. User Preference Media Queries

These are among the most important for accessibility and UX:

/* Color scheme preference */
@media (prefers-color-scheme: dark) {
:root {
--color-bg: #0f0f0f;
--color-text: #f0f0f0;
}
}
@media (prefers-color-scheme: light) {
:root {
--color-bg: #ffffff;
--color-text: #111111;
}
}

/* Motion preference — always implement this */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}

/* Contrast preference */
@media (prefers-contrast: more) {
:root { --color-border: currentColor; }
body { border: 1px solid; }
}

/* Data saver preference */
@media (prefers-reduced-data: reduce) {
.hero-video { display: none; }
.hero-image { display: block; } /* Fallback to static image */
}

6. Input Capability Queries

/* Only apply hover effects on devices that actually have a mouse */
@media (hover: hover) and (pointer: fine) {
.card:hover { transform: translateY(-4px); box-shadow: var(--shadow-lg); }
}

/* Touch screens (coarse pointer) */
@media (pointer: coarse) {
.btn { min-height: 44px; min-width: 44px; } /* Larger tap targets */
.tooltip { display: none; } /* Hover-only UI not usable */
}

7. Orientation

@media (orientation: portrait) {
.sidebar { display: none; } /* Hide sidebar in portrait mode */
}
@media (orientation: landscape) {
.hero { min-height: 60vh; } /* Less tall hero in landscape */
}

8. Resolution / DPR (Device Pixel Ratio)

/* Target high-DPI / Retina screens */
@media (-webkit-min-device-pixel-ratio: 2),
(min-resolution: 192dpi) {
.logo { background-image: url('/images/logo@2x.png'); }
}

/* Modern syntax */
@media (min-resolution: 2dppx) {
.icon { background-image: url('/images/icon@2x.webp'); }
}

9. Logical Operators

/* AND — both conditions must match */
@media (min-width: 768px) and (orientation: landscape) { }

/* NOT — inverts the condition */
@media not (prefers-color-scheme: dark) { }

/* Comma = OR — matches if either condition is true */
@media (max-width: 480px), (orientation: portrait) { }

/* Range syntax (Modern CSS Level 4) */
@media (480px < width <= 1024px) { }

10. Container Queries — The Future of Responsive Design

Unlike media queries (based on viewport), container queries respond to the size of the parent element. Perfect for reusable components:

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

/* Step 2: Style based on container size */
@container card (min-width: 400px) {
.card {
display: flex;
flex-direction: row;
}
.card__image { width: 200px; flex-shrink: 0; }
}

[!TIP] Container queries solve the "this component looks different in the sidebar vs the main content" problem. Browser support is now excellent (89%+). Use them for reusable components that appear in multiple column widths.