Styles that depend on conditions
A media query says: "apply these rules only if this condition is true." The condition can be about the screen size, the device, or the visitor's own settings. The syntax wraps normal rules in @media:
@media (min-width: 40rem) {
.sidebar { display: block; }
}Rules inside apply only when the condition matches, and they follow the usual cascade order, so a later query can override earlier rules.
Width queries and mobile-first
The query you will write most is the width of the viewport (the visible browser area):
min-width: true when the viewport is at least this wide.max-width: true when it is at most this wide.
Using min-width fits mobile-first: write the simplest styles with no query, then add layers.
<style>
.layout { display: grid; gap: 12px; }
.layout div { padding: 16px; border-radius: 8px; background: #dbeafe; }
/* from 30rem upward: two columns */
@media (min-width: 30rem) {
.layout { grid-template-columns: 1fr 1fr; }
}
/* from 50rem upward: three columns, and a darker tint */
@media (min-width: 50rem) {
.layout { grid-template-columns: repeat(3, 1fr); }
.layout div { background: #bfdbfe; }
}
</style>
<div class="layout"><div>1</div><div>2</div><div>3</div></div>Drag the preview from narrow to wide to see one column, then two, then three. The preview itself is narrower than most screens, so rem breakpoints around 30rem and 50rem are easy to see here.
Use em or rem for breakpoints rather than pixels: they scale with the reader's font size, so someone who enlarges their text gets the compact layout sooner, as they should.
A range can use both: @media (min-width: 40rem) and (max-width: 60rem), or the modern syntax @media (40rem <= width <= 60rem).
Combining conditions
@media (min-width: 40rem) and (orientation: landscape) { … }
@media (max-width: 30rem), print { … } /* comma means "or" */
@media not print { … }andrequires all conditions.- A comma-separated list means or.
notnegates the whole query.
Orientation and pointer
@media (orientation: landscape) { .hero { min-height: 60vh; } }
@media (hover: none) { .menu-item:hover { background: none; } } /* touch devices */
@media (pointer: coarse) { button { min-height: 44px; } } /* fingers need bigger targets */These describe the device rather than the width. A big tablet and a small laptop can have the same width but different input methods.
Respecting user preferences
Visitors can tell their system what they prefer, and CSS can read those settings. Honouring them is one of the kindest things a stylesheet can do.
/* Dark mode */
@media (prefers-color-scheme: dark) {
:root { --bg: #0f172a; --text: #e2e8f0; }
}
/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
* { animation: none !important; transition: none !important; }
}
/* More contrast */
@media (prefers-contrast: more) {
:root { --border: #000; }
}prefers-reduced-motion matters: large movement, parallax and auto-playing animation can cause dizziness or nausea in people with vestibular disorders. If you animate, give them a calm alternative.
Print styles
When someone prints or saves your page as PDF, a separate stylesheet can clean it up:
@media print {
nav, aside, .ads, button { display: none; }
body { color: black; background: white; font-size: 12pt; }
a[href^="http"]::after { content: " (" attr(href) ")"; } /* show link addresses on paper */
}Where to put queries
Two common styles: put each query next to the component it changes (easy to maintain), or collect them at the end of the stylesheet. Either way, keep breakpoints consistent and few. If every component has its own special width, the design becomes hard to reason about.
You can also load a whole stylesheet conditionally: <link rel="stylesheet" href="print.css" media="print">.
Container queries: a preview
Media queries react to the viewport. Newer container queries react to the size of a component's container, so a card can rearrange itself when placed in a narrow sidebar or a wide main column. They are supported in all current browsers and are worth learning once media queries feel comfortable.
Common mistakes
- Mixing
min-widthandmax-widthstyles in one file, creating confusion about which applies. - Pixel breakpoints on text-heavy layouts; prefer
emorrem. - Hiding content for small screens rather than adapting it.
- A missing viewport meta tag, so no query ever matches the device width.
- Forgetting reduced motion when you add animations.
Exercise
Write CSS that is a single column by default, switches to a 2/1 two-column grid from 45rem, uses a dark background in dark mode, and hides a .cookie-note when printing.
Show one possible answer
<style>
:root { --bg: #ffffff; --text: #111827; }
body { background: var(--bg); color: var(--text); }
.layout { display: grid; gap: 1rem; }
@media (min-width: 45rem) {
.layout { grid-template-columns: 2fr 1fr; }
}
@media (prefers-color-scheme: dark) {
:root { --bg: #0f172a; --text: #e2e8f0; }
}
@media print {
.cookie-note { display: none; }
}
</style>
<div class="layout"><main>Main</main><aside>Aside</aside></div>Key points
- A media query wraps CSS rules that apply only when its condition is true, such as a minimum viewport width.
- Mobile-first stylesheets use min-width queries to add layout for bigger screens.
- Queries can also read user preferences, like prefers-color-scheme for dark mode and prefers-reduced-motion.
- Conditions can be combined with and, or lists separated by commas, and negated with not.
- A print media query lets you hide menus and adjust colours for paper or PDF.