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
| Type | When Applied |
|---|---|
all | All devices (default) |
screen | Screens, tablets, phones |
print | Printer or print preview |
speech | Screen 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.