Media Query Syntax
A media query wraps CSS rules in a condition. If the condition is true, the rules apply. If not, they are ignored.
@media media-type and (condition) {
/* CSS rules that apply when condition is true */
}
/* Most common: screen width */
@media screen and (max-width: 768px) {
.sidebar { display: none; }
}
/* Multiple conditions with and */
@media screen and (min-width: 600px) and (max-width: 1200px) {
.container { max-width: 960px; }
}
/* OR condition with comma */
@media (max-width: 480px), (orientation: portrait) {
.hero { padding: 40px 16px; }
}
/* NOT condition */
@media not screen and (color) {
/* applies to non-color screens */
}
Media Types
@media screen { } /* default — for screens */
@media print { } /* when printing the page */
@media all { } /* all devices (default if omitted) */
/* Print styles — hide navigation, ads, expand links */
@media print {
.site-header, .sidebar, .ad-container, footer { display: none; }
a::after { content: " (" attr(href) ")"; font-size: .85em; }
body { font-size: 12pt; color: black; }
.page-break { page-break-after: always; }
}
Width Features – The Core of Responsive Design
/* min-width — applies when viewport is AT LEAST this wide */
@media (min-width: 768px) {
.nav { display: flex; }
}
/* max-width — applies when viewport is AT MOST this wide */
@media (max-width: 767px) {
.nav { display: none; }
}
/* Exact range — between two widths */
@media (min-width: 768px) and (max-width: 1199px) {
.container { max-width: 960px; }
}
/* Modern range syntax (CSS Media Queries Level 4) */
@media (width >= 768px) { }
@media (width <= 767px) { }
@media (768px <= width <= 1199px) { }
Common Breakpoints
Breakpoints are the widths at which your layout changes. There's no universally correct set — choose based on your content, not specific devices:
/* Common breakpoints — used by Tailwind CSS, Bootstrap, and most frameworks */
/* xs: < 480px — small phones */
/* sm: >= 480px — large phones */
/* md: >= 768px — tablets */
/* lg: >= 1024px — small laptops */
/* xl: >= 1280px — desktops */
/* 2xl: >= 1536px — large screens */
/* Define as custom properties for consistency */
/* (Note: custom properties don't work inside @media queries — use variables in JS or preprocessors) */
/* Mobile-first breakpoints */
/* Base styles = mobile */
@media (min-width: 480px) {
/* Small phone landscape and up */
}
@media (min-width: 768px) {
/* Tablet and up */
.grid { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1024px) {
/* Laptop and up */
.grid { grid-template-columns: repeat(3, 1fr); }
.sidebar { display: block; }
}
@media (min-width: 1280px) {
/* Desktop and up */
.container { max-width: 1200px; }
}
Don't tie breakpoints to specific phone models — they change every year. Instead, resize your browser slowly and add a breakpoint wherever the layout starts to look broken. Content-driven breakpoints are more resilient than device-driven ones.
Mobile-First vs Desktop-First
This is one of the most important decisions in responsive CSS architecture:
/* MOBILE-FIRST: base styles are for mobile.
Progressively add styles for larger screens with min-width. */
.nav {
flex-direction: column; /* stacked on mobile */
gap: 8px;
}
@media (min-width: 768px) {
.nav {
flex-direction: row; /* horizontal on tablet+ */
gap: 24px;
}
}
/* ✅ Advantages:
- Smaller CSS loaded on mobile (fast)
- Forces thinking about the mobile experience first
- min-width queries are easier to reason about than max-width */
/* DESKTOP-FIRST: base styles are for desktop.
Override for smaller screens with max-width. */
.nav {
flex-direction: row; /* horizontal on desktop */
}
@media (max-width: 767px) {
.nav {
flex-direction: column; /* stacked on mobile */
}
}
/* ❌ Disadvantages:
- Mobile downloads all desktop CSS then overrides it (heavier)
- max-width breakpoints are harder to compose
- Works but produces more overrides and specificity conflicts */
Beyond Width – Modern Media Features
/* Orientation */
@media (orientation: portrait) { /* taller than wide */ }
@media (orientation: landscape) { /* wider than tall */ }
/* Device pixel ratio — for high-DPI (retina) screens */
@media (-webkit-min-device-pixel-ratio: 2),
(min-resolution: 192dpi) {
/* Load 2x images for retina displays */
.logo { background-image: url('logo@2x.png'); }
}
/* Color scheme preference — dark mode! */
@media (prefers-color-scheme: dark) {
:root {
--bg: #0d0d0d;
--text: #f0f0f0;
--border: #333;
}
}
@media (prefers-color-scheme: light) {
:root {
--bg: #ffffff;
--text: #111;
--border: #eee;
}
}
/* Reduced motion — respect accessibility settings */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* User prefers more contrast */
@media (prefers-contrast: more) {
:root { --border: #000; }
body { border: 2px solid; }
}
/* Hover capability — touch screens can't hover */
@media (hover: hover) {
/* Only apply hover styles on devices that support it */
.btn:hover { background: #0d5fa0; }
}
@media (hover: none) {
/* Touch devices — remove hover states, use active instead */
.btn:active { background: #0d5fa0; }
}
/* Pointer precision */
@media (pointer: coarse) {
/* Touch/stylus — make targets bigger */
.btn { min-height: 44px; }
}
@media (pointer: fine) {
/* Mouse — can use smaller targets */
.btn { min-height: 32px; }
}
/* Display mode (PWA) */
@media (display-mode: standalone) {
.install-banner { display: none; } /* hide in installed PWA */
}
Container Queries – The Next Level
Container queries (supported in all modern browsers since 2023) let you style elements based on their parent container's size rather than the viewport size — a massive improvement for component-based design.
/* 1. Define the container */
.card-wrapper {
container-type: inline-size; /* enable container queries */
container-name: card; /* optional name */
}
/* 2. Query the container size */
@container card (min-width: 400px) {
.card {
display: flex; /* wide layout when container is wide */
gap: 16px;
}
.card-image { width: 40%; }
}
@container (max-width: 399px) {
.card { display: block; } /* stacked when container is narrow */
}
/* Same component behaves differently depending on where it's placed
— not the viewport size. This is the key advantage. */
Media queries respond to the viewport. Container queries respond to the parent element. A card component in a wide sidebar should look different from the same card in a narrow column — media queries can't achieve this cleanly, but container queries can. For new projects, use container queries for components and media queries for layout-level changes.
Real Responsive Patterns
/* Base (mobile) */
.page {
display: grid;
grid-template-areas: "header" "main" "footer";
grid-template-rows: auto 1fr auto;
min-height: 100vh;
}
.sidebar { display: none; }
.container {
width: 100%;
padding: 0 16px;
}
/* Tablet (768px+) */
@media (min-width: 768px) {
.container { padding: 0 24px; }
.card-grid {
grid-template-columns: repeat(2, 1fr);
}
}
/* Desktop (1024px+) */
@media (min-width: 1024px) {
.page {
grid-template-areas: "header header" "sidebar main" "footer footer";
grid-template-columns: 260px 1fr;
}
.sidebar { display: block; }
.card-grid {
grid-template-columns: repeat(3, 1fr);
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 32px;
}
}
/* Large desktop (1280px+) */
@media (min-width: 1280px) {
.card-grid {
grid-template-columns: repeat(4, 1fr);
}
}
📋 Summary
- @media (condition) { } — apply CSS when condition is true.
- min-width — apply when viewport is at least this wide (mobile-first).
- max-width — apply when viewport is at most this wide (desktop-first, avoid).
- Mobile-first — write base styles for mobile, add larger-screen styles with
min-width. Recommended. - Common breakpoints: 480px, 768px, 1024px, 1280px.
- prefers-color-scheme: dark — system dark mode detection.
- prefers-reduced-motion — disable animations for accessibility.
- hover: hover — apply hover styles only on devices that support hover.
- Container queries — query parent size instead of viewport. Better for components.
Frequently Asked Questions
There's no universal answer. Common choices: 480px (large phones), 768px (tablets), 1024px (small laptops), 1280px (desktops). Frameworks like Tailwind (640, 768, 1024, 1280, 1536) and Bootstrap (576, 768, 992, 1200) provide established systems. The best approach: design your layout first, then add breakpoints where the layout breaks. Don't add breakpoints preemptively — add them when needed.
Use min-width for mobile-first development (recommended). Write base styles for mobile, then add progressive enhancements at larger widths. Use max-width for desktop-first development — write desktop styles as the base, then add overrides for smaller screens. Min-width queries are generally cleaner because you're adding capabilities rather than overriding them. Most modern frameworks and the industry standard favor min-width / mobile-first.
Use @media (prefers-color-scheme: dark) to detect the user's OS preference, combined with CSS custom properties: define your light theme in :root, then override the color variables inside the dark media query. For user-toggled dark mode (a button), use a class on <html> (html.dark { }) and toggle it with JavaScript. For both OS and user preference, check the OS preference on load and let the toggle override it.
Container queries let elements adapt to their parent container's size rather than the viewport. Use them for reusable components that appear in different layout contexts — a card that should be single-column in a narrow sidebar and two-column when in a wide main area. All modern browsers support them since late 2023. They complement media queries: use media queries for page-level layout, use container queries for component-level layout.