Episode 1 — Fundamentals / 1.9 — CSS Responsive Design
1.9.b — Media Queries
In one sentence:
@mediarules let you apply CSS conditionally based on viewport width, user preferences, input capabilities, and more — they are the backbone of every responsive layout.
Navigation: ← 1.9.a — Mobile-First Strategy · 1.9.c — Breakpoint Planning →
1. Basic syntax
@media <media-type>? and (<feature>: <value>) {
/* rules that apply when condition is true */
}
@media screen and (min-width: 768px) {
.sidebar { display: block; }
}
@media— keyword that opens the block.- Media type — optional filter (
screen,print,all). Defaults toallif omitted. - Media feature — the condition to test (e.g.,
min-width: 768px). - Rules inside — normal CSS that activates when the condition is true.
2. Media types
| Type | Applies to |
|---|---|
all | Every device (default if omitted) |
screen | Screens — monitors, phones, tablets |
print | Print preview and printed pages |
/* Hide nav and ads when printing */
@media print {
.nav, .ad-banner { display: none; }
body { font-size: 12pt; color: #000; }
}
Note: tty, tv, handheld, and others are deprecated — only all, screen, and print are reliable.
3. Media features reference
Width-based (most common)
| Feature | Tests |
|---|---|
width | Exact viewport width |
min-width | Viewport ≥ value (mobile-first) |
max-width | Viewport ≤ value (desktop-first) |
height / min-height / max-height | Viewport height (used less often) |
/* Mobile-first: add sidebar at 1024px */
@media (min-width: 1024px) {
.layout { grid-template-columns: 250px 1fr; }
}
Orientation
| Feature | Values |
|---|---|
orientation | portrait (height > width) or landscape (width > height) |
@media (orientation: landscape) {
.hero { min-height: 60vh; }
}
User preferences
| Feature | Values | Use case |
|---|---|---|
prefers-color-scheme | light, dark | Dark mode toggle |
prefers-reduced-motion | reduce, no-preference | Disable/simplify animations |
prefers-contrast | more, less, no-preference | High-contrast adjustments |
/* Respect OS dark mode */
@media (prefers-color-scheme: dark) {
:root {
--bg: #1a1a2e;
--text: #e0e0e0;
}
}
/* Remove animations for users who prefer reduced motion */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
Input capabilities
| Feature | Values | Meaning |
|---|---|---|
hover | hover, none | Can the primary input hover? |
pointer | fine, coarse, none | Precision of primary pointer |
any-hover | hover, none | Can any input hover? |
any-pointer | fine, coarse, none | Precision of any available pointer |
/* Only show hover effects on devices that support hover */
@media (hover: hover) {
.card:hover { transform: translateY(-4px); box-shadow: 0 8px 24px rgba(0,0,0,0.15); }
}
/* Enlarge targets for coarse pointers (touch) */
@media (pointer: coarse) {
.btn { min-height: 48px; padding: 0.75rem 1.5rem; }
}
4. Combining conditions
and — all conditions must be true
@media screen and (min-width: 768px) and (orientation: landscape) {
.dashboard { grid-template-columns: repeat(3, 1fr); }
}
, (comma / or) — any condition can be true
@media (max-width: 480px), (orientation: portrait) {
.sidebar { display: none; }
}
not — negates the entire query
/* Applies to everything EXCEPT screens ≥ 1024px */
@media not screen and (min-width: 1024px) {
.wide-table { overflow-x: auto; }
}
Gotcha: not negates the whole query up to the comma, not just the next feature. Parenthesize carefully.
only — hides query from old browsers
@media only screen and (min-width: 768px) { … }
Rarely needed today — only prevents ancient browsers (IE 6–8) from applying styles they can't parse correctly.
5. Media Queries Level 4: range syntax
Modern browsers support a cleaner range syntax that reads more naturally:
| Old syntax | New range syntax |
|---|---|
(min-width: 768px) | (width >= 768px) |
(max-width: 1023px) | (width < 1024px) |
(min-width: 768px) and (max-width: 1023px) | (768px <= width < 1024px) |
/* Old */
@media (min-width: 768px) and (max-width: 1023px) {
.content { padding: 2rem; }
}
/* New — same result, easier to read */
@media (768px <= width < 1024px) {
.content { padding: 2rem; }
}
Browser support: Supported in Chrome 104+, Firefox 102+, Safari 16.4+. Safe for new projects; add fallbacks if you need older browser support.
6. Practical responsive patterns
Pattern A: stack → side-by-side
.feature-grid {
display: grid;
gap: 1.5rem;
}
@media (min-width: 768px) {
.feature-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1200px) {
.feature-grid { grid-template-columns: repeat(3, 1fr); }
}
Pattern B: show/hide elements
.mobile-menu-btn { display: block; }
.desktop-nav { display: none; }
@media (min-width: 768px) {
.mobile-menu-btn { display: none; }
.desktop-nav { display: flex; gap: 2rem; }
}
Pattern C: responsive font sizing
h1 { font-size: 1.75rem; }
@media (min-width: 768px) {
h1 { font-size: 2.5rem; }
}
@media (min-width: 1200px) {
h1 { font-size: 3rem; }
}
Pattern D: print-friendly
@media print {
.no-print { display: none; }
a[href]::after { content: " (" attr(href) ")"; font-size: 0.8em; }
body { font-size: 12pt; line-height: 1.4; }
}
7. Where to place media queries
| Approach | Description | Pros / Cons |
|---|---|---|
| Bottom of stylesheet | All queries grouped at the end | Easy to scan breakpoints; repeated selectors |
| Near each component | Query immediately follows the base rule | Co-located, easier to maintain; breakpoints scattered |
| Separate files | mobile.css, tablet.css, etc. | Clean separation; more HTTP requests (mitigated by bundlers) |
Most modern projects use the component-adjacent approach — especially with CSS Modules, Sass partials, or utility frameworks.
8. Key takeaways
@media (min-width)is the mobile-first query;max-widthis desktop-first.- User preference queries (
prefers-color-scheme,prefers-reduced-motion) are accessibility essentials, not nice-to-haves. hoverandpointerlet you tailor interactions to the input device.- Use Level 4 range syntax (
width >= 768px) in new projects for readability. - Combine with
and,,(or), andnot— but be precise withnotscope.
Explain-It Challenge
Explain without notes:
- What is the difference between
(hover: hover)and(any-hover: hover), and when would you use each? - Write a media query that applies styles only to tablet-sized screens in portrait orientation, using both old and new (Level 4) syntax.
- Why should
prefers-reduced-motion: reducedefault to removing animation rather than reducing it?
Navigation: ← 1.9.a — Mobile-First Strategy · 1.9.c — Breakpoint Planning →