The Many Flavors of CSS Media Queries
CSS media queries let you target browsers by specific characteristics and user preferences — viewport width being the most famous, but hardly the only one. You can query screen resolution, device orientation, operating-system settings, and more, then conditionally apply styles based on what you find.
Where Media Queries Can Live
Media queries get the most attention inside CSS stylesheets, but they are equally at home in HTML and JavaScript.
In HTML
The <link> element accepts a media attribute, letting you split your CSS into separate files that load conditionally:
<html>
<head>
<!-- Served to all users -->
<link rel="stylesheet" href="all.css" media="all" />
<!-- Served to screens that are at least 20em wide -->
<link rel="stylesheet" href="small.css" media="(min-width: 20em)" />
<!-- Served to screens that are at least 64em wide -->
<link rel="stylesheet" href="medium.css" media="(min-width: 64em)" />
<!-- Served to screens that are at least 90em wide -->
<link rel="stylesheet" href="large.css" media="(min-width: 90em)" />
<!-- Served to screens that are at least 120em wide -->
<link rel="stylesheet" href="extra-large.css" media="(min-width: 120em)" />
<!-- Served to print media, like printers -->
<link rel="stylesheet" href="print.css" media="print" />
</head>
<!-- ... -->
</html>
That can be a performance win, but note the caveat: stylesheets that don't match the query aren't necessarily blocked from downloading — they're just deprioritized. A phone will typically fetch only the small-screen file, while a desktop browser matching all queries will download the whole set.
The <source> element works the same way inside a <picture> element, letting you serve different image versions to different viewports:
<picture>
<!-- Use this image if the screen is at least 800px wide -->
<source media="(min-width: 800px)">
<!-- Use this image if the screen is at least 600px wide -->
<source media="(min-width: 600px)">
<!-- Use this image if nothing matches -->
<img src="cat.png" alt="A calico cat with dark aviator sunglasses.">
</picture>
You can also put a media attribute directly on the <style> element:
<style>
p {
background-color: blue;
color: white;
}
</style>
<style media="all and (max-width: 500px)">
p {
background-color: yellow;
color: blue;
}
</style>
In CSS
The classic @media rule wraps a block of styles that apply only when its conditions match:
/* Viewports between 320px and 480px wide */
@media only screen and (min-device-width: 320px) and (max-device-width: 480px) {
.card {
background: #bada55;
}
}
It's technically possible to scope an @import with media conditions, but avoid @import generally — it performs poorly.
/* Avoid using @import if possible! */
/* Base styles for all screens */
@import url("style.css") screen;
/* Styles for screens in a portrait (narrow) orientation */
@import url('landscape.css') screen and (orientation: portrait);
/* Print styles */
@import url("print.css") print;
In JavaScript
JavaScript approaches media queries via window.matchMedia(). First define the condition, then act on it:
// Create a media condition that targets viewports at least 768px wide
const mediaQuery = window.matchMedia( '( min-width: 768px )' )
// Create a media condition that targets viewports at least 768px wide
const mediaQuery = window.matchMedia( '( min-width: 768px )' )
// Note the `matches` property
if ( mediaQuery.matches ) {
console.log('Media Query Matched!')
}
That one-shot check won't re-fire if the viewport changes. Use a listener for ongoing updates:
// Create a condition that targets viewports at least 768px wide
const mediaQuery = window.matchMedia('(min-width: 768px)')
function handleTabletChange(e) {
// Check if the media query is true
if (e.matches) {
// Then log the following message to the console
console.log('Media Query Matched!')
}
}
// Register event listener
mediaQuery.addListener(handleTabletChange)
// Initial check
handleTabletChange(mediaQuery)
For a deeper comparison against the older resize-event-plus-window.innerWidth pattern, see the full guide to JavaScript media queries.
Dissecting a Media Query

The @media Rule
@media [media-type] ([media-feature]) {
/* Styles! */
}
This at-rule is geared to the type of media, the features that media type supports, and the operators that combine conditions into simple or complex matches.
Media Types
@media screen {
/* Styles! */
}
In most cases, you'll target screen. The spec defines a few others:
all: matches all devicesprint: matches print preview and paginated outputscreen: matches devices with a screenspeech: matches screen readers and other audible output; replaces the deprecatedauraltype since Media Queries Level 4
Broader compatibility notes: other legacy types like tty, tv, projection, handheld, braille, embossed, and aural are deprecated. Browsers are advised to recognize them but must evaluate them to nothing. All major browsers can emulate print output in DevTools, which is handy for previewing those styles without a printer.
Media Features
Once the type is set, you can match specific features. The classic pair is min-width and max-width, but Media Queries Level 5 groups the full set into several categories.
Viewport/Page Characteristics
| Feature | Summary | Values | Added |
|---|---|---|---|
width | Defines the widths of the viewport. This can be a specific number (e.g. 400px) or a range (using min-width and max-width). | <length> | |
height | Defines the height of the viewport. This can be a specific number (e.g. 400px) or a range (using min-height and max-height). | <length> | |
aspect-ratio | Defines the width-to-height aspect ratio of the viewport | <ratio> | |
orientation | The way the screen is oriented, such as tall (portrait) or wide (landscape) based on how the device is rotated. | portraitlandscape | |
overflow-block | Checks how the device treats content that overflows the viewport in the block direction, which can be scroll (allows scrolling), optional-paged (allows scrolling and manual page breaks), paged (broken up into pages), and none (not displayed). | scrolloptional-pagedpaged | Media Queries Level 4 |
overflow-inline | Checks if content that overflows the viewport along the inline axis be scrolled, which is either none (no scrolling) or scroll (allows scrolling). | scrollnone | Media Queries Level 4 |
Display Quality
| Feature | Summary | Values | Added |
|---|---|---|---|
resolution | Defines the target pixel density of the device | <resolution>infinite | |
scan | Defines the scanning process of the device, which is the way the device paints an image onto the screen (where interlace draws odd and even lines alternately, and progressive draws them all in sequence). | interlaceprogressive | |
grid | Determines if the device uses a grid (1) or bitmap (0) screen | 0 = Bitmap1 = Grid | Media Queries Level 5 |
update | Checks how frequently the device can modify the appearance of content (if it can at all), with values including none, slow and fast. | slowfastnone | Media Queries Level 4 |
environment-blending | A method for determining the external environment of a device, such as dim or excessively bright places. | opaqueadditivesubtractive | |
display-mode | Tests the display mode of a device, including fullscreen(no browsers chrome), standalone (a standalone application), minimal-ui (a standalone application, but with some navigation), and browser (a more traditional browser window) | fullscreenstandaloneminimal-uibrowser | Web App Manifest |
Color
| Feature | Summary | Values | Added |
|---|---|---|---|
color | Defines the color support of a device, expressed numerically as bits. So, a value of 12 would be the equivalent of a device that supports 12-bit color, and a value of zero indicates no color support. | <integer> | |
color-index | Defines the number of values the device supports. This can be a specific number (e.g. 10000) or a range (e.g. min-color-index: 10000, max-color-index: 15000), just like width. | <integer> | |
monochrome | The number of bits per pixel that a device’s monochrome supports, where zero is no monochrome support. | <integer> | |
color-gamut | Defines the range of colors supported by the browser and device, which could be srgb, p3 or rec2020 | srgbp3rec2020 | Media Queries Level 4 |
dynamic-range | The combination of how much brightness, color depth, and contrast ratio supported by the video plane of the browser and user device. | standardhigh | |
inverted-colors | Checks if the browser or operating system is set to invert colors (which can be useful for optimizing accessibility for sight impairments involving color) | invertednone | Media Queries Level 5 |
Interaction
| Feature | Summary | Values | Added |
|---|---|---|---|
pointer | Sort of like any-pointer but checks if the primary input mechanism is a pointer and, if so, how accurate it is (where coarse is less accurate, fine is more accurate, and none is no pointer). | coarsefinenone | Media Queries Level 4 |
hover | Sort of like any-hover but checks if the primary input mechanism (e.g. mouse of touch) allows the user to hover over elements | hovernone | Media Queries Level 4 |
any-pointer | Checks if the device uses a pointer, such as a mouse or styles, as well as how accurate it is (where coarse is less accurate and fine is more accurate) | coarsefinenone | Media Queries Level 4 |
any-hover | Checks if the device is capable of hovering elements, like with a mouse or stylus. In some rare cases, touch devices are capable of hovers. | | Media Queries Level 4 |
Video Prefixed
Some user agents, including TVs, render video and graphics on separate planes with their own characteristics:
| Feature | Summary | Values | Added |
|---|---|---|---|
video-color-gamut | Describes the approximate range of colors supported by the video plane of the browser and user device | srgbp3rec2020 | Media Queries Level 5 |
video-dynamic-range | The combination of how much brightness, color depth, and contrast ratio supported by the video plane of the browser and user device. | standardhigh | Media Queries Level 5 |
video-width¹ | The width of the video plane area of the targeted display | <length> | Media Queries Level 5 |
video-height¹ | The height of the video plane area of the targeted display | <length> | Media Queries Level 5 |
video-resolution¹ | The resolution of the video plane area of the targeted display | <resolution>inifinite | Media Queries Level 5 |
Scripting
| Feature | Summary | Values | Added |
|---|---|---|---|
scripting | Checks whether the device allows scripting (i.e. JavaScript) where enabled allows scripting, iniital-only | enabledinitial-only | Media Queries Level 5 |
User Preference
| Feature | Summary | Values | Added |
|---|---|---|---|
prefers-reduced-motion | Detects if the user’s system settings are set to reduce motion on the page, which is a great accessibility check. | no-preferencereduce | Media Queries Level 5 |
prefers-reduced-transparency | Detects if the user’s system settings prevent transparent across elements. | no-preferencereduce | Media Queries Level 5 |
prefers-contrast | Detects if the user’s system settings are set to either increase or decrease the amount of contrast between colors. | no-preferencehighlowforced | Media Queries Level 5 |
prefers-color-scheme | Detects if the user prefers a light or dark color scheme, which is a rapidly growing way to go about creating “dark mode” interfaces. | lightdark | Media Queries Level 5 |
forced-colors | Tests whether the browser restricts the colors available to use (which is none or active) | activenone | Media Queries Level 5 |
prefers-reduced-data | Detects if the user prefers to use less data for the page to be rendered. | no-preferencereduce | Media Queries Level 5 |
Deprecated
| Name | Summary | Removed |
|---|---|---|
device-aspect-ratio | The width-to-height aspect ratio of the output device | Media Queries Level 4 |
device-height | The height of the device’s surface that displays rendered elements | Media Queries Level 4 |
device-width | The width of the device’s surface that displays rendered elements | Media Queries Level 4 |
Operators
@media itself behaves like an if. The and operator chains conditions together, as in a width range:
/* Matches screen between 320px AND 768px */
@media screen (min-width: 320px) and (max-width: 768px) {
.element {
/* Styles! */
}
}
Comma-separated queries act like or, matching any one condition:
/*
Matches screens where either the user prefers dark mode or the screen is at least 1200px wide */
@media screen (prefers-color-scheme: dark), (min-width 1200px) {
.element {
/* Styles! */
}
}
The not operator matches by exclusion:
@media print and ( not(color) ) {
body {
background-color: none;
}
}
Matching Value Ranges
Features like width, height, color, and color-index accept min- and max- prefixes, which lets you express a range of values rather than exact ones:
body {
background-color: #fff;
}
@media (min-width: 30em) and (max-width: 80em) {
body {
background-color: purple;
}
}
Media Queries Level 4 introduces a simpler range syntax using <, >, and =, which would rewrite that same rule as:
@media (30em <= width <= 80em) {
/* ... */
}
Nesting and Complex Conditions
Parentheses and nesting allow arbitrarily intricate expressions:
@media (min-width: 20em), not all and (min-height: 40em) {
@media not all and (pointer: none) { ... }
@media screen and ( (min-width: 50em) and (orientation: landscape) ), print and ( not (color) ) { ... }
}
Be careful: deep nesting produces opinionated, hard-to-maintain queries. Complexity is a maintenance cost; as Brad Frost puts it, the more complex the interface, the more thought required to maintain it.
Accessibility-Focused Queries
Several Media Queries Level 4 additions put user preferences front and center.
prefers-reduced-motion
This detects whether the user has requested less on-screen movement. Valid values are:
no-preference: user has made no preference knownreduce: user wants minimal, ideally non-essential only, movement

This matters for people with vestibular disorders or vertigo, where motion can trigger dizziness, migraine, nausea, or hearing loss. Eric Bailey suggests stopping all animation outright with this universal override:
@media screen and (prefers-reduced-motion: reduce) {
* {
/* Very short durations means JavaScript that relies on events still works */
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.001ms !important;
}
}
Frameworks like Bootstrap enable this by default. There is little excuse not to include it.
prefers-contrast
no-preference: user has made no preference known; false when used as a booleanhigh: user prefers higher contrastlow: user prefers lower contrast

At the time of writing, no browser supports this feature. Microsoft's earlier non-standard -ms-high-contrast implementation works only in Edge v18 and earlier, not Chromium-based versions.
.button {
background-color: #0958d8;
color: #fff;
}
@media (prefers-contrast: high) {
.button {
background-color: #0a0db7;
}
}
That snippet bumps a .button's contrast from AA to AAA when high contrast is requested.
inverted-colors
none: colors display normallyinverted: user has chosen to invert colors

A common side effect is that images and videos get inverted too, looking like x-rays. Applying a CSS invert filter to media content can compensate:
@media (inverted-colors) {
img, video {
filter: invert(100%);
}
}
Of note: Safari is the only browser supporting this feature as of writing.
prefers-color-scheme
Dark mode support uses this feature to follow the user's system-level theme:
light: user prefers light theme or has no preference setdark: user prefers a dark display

body {
--bg-color: white;
--text-color: black;
background-color: var(--bg-color);
color: var(--text-color);
}
@media screen and (prefers-color-scheme: dark) {
body {
--bg-color: black;
--text-color: white;
}
}
There's more to dark mode than swapping a background color. The complete dark mode guide covers the strategy, and CSS custom properties make the swap considerably more maintainable.
The Road Ahead: Media Queries Level 5
Level 5 is still a Working Draft, so items here may shift before becoming a recommendation, but it opens interesting doors.
Detecting forced color palettes
Forced colors mode lets users restrict a page to a limited pallette, improving contrast and readability. The forced-colors feature accepts an active value, which requires the browser to supply its color choices through CSS system colors. Browsers can also decide whether the page background is light or dark and trigger the matching prefers-color-scheme value.
Dynamic range and video features
The dynamic-range feature matches super-bright, wide-gamut, high-contrast displays with the high keyword and everything else with standard. What constitutes "high" brightness or contrast remains uncertain; the browser may make that call.
The draft also proposes new video-prefixed features for TVs and similar devices that render video and graphics on separate planes, covering color gamut and dynamic range. Proposals to detect video's height, width, and resolution are still under debate.
Browser Support
Browsers ship support at different speeds. Since this is a moving target, reference the MDN compatibility tables before relying on a feature.
When Media Queries Aren't the Answer
Container queries answer a different question. Media queries key off the browser viewport, but components often need to adapt to their own containing block — whether that's a sidebar, a full-width footer, or an unknown grid column. That was the unsolved problem until Chrome 105 and Safari 16.1 shipped CSS Container Queries; Firefox support was still pending at the time of writing.
This browser support data comes from Caniuse; numbers denote the first supported version.
Desktop
| Chrome | Firefox | IE | Edge | Safari |
|---|---|---|---|---|
| 106 | 110 | No | 106 | 16.0 |
Mobile / Tablet
| Android Chrome | Android Firefox | Android | iOS Safari |
|---|---|---|---|
| 151 | 153 | 151 | 16.0 |
A Philosophical Note
With roughly 150 browsers, around 50 user-preference combinations, and more than 24,000 distinct Android devices, content can render in millions of possible contexts. Assuming a universal browsing experience is a dangerous trap.
Media queries are the tool to handle that variety deliberately. But accommodating every conceivable scenario produces an unmaintainable codebase. The alternative ideal — universal design, making interfaces passable by all people without specialized adaptation — has its own challenges; as Laura Kalbag explains in “Accessibility for Everyone”, the accessible designer builds a wide door for wheelchair users, while the universal designer builds an entry anyone can use. Miriam Suzanne's advice puts it bluntly: given an unknown canvas and infinite content variables, no one can know precisely what they're doing, so leave assumptions behind.
Examples in Practice
The possible combinations of media features and operators are nearly endless. Here are a handful of immediately useful patterns.
Flip a layout at different viewport widths
This is where responsive design started: width informed the layout's breakpoints, a concept Ethan Marcotte famously coined. Luke Wroblewski later introduced mobile-first, often identifiable by min-width queries.
Targeting the specific pixel width of a certain device — an iPhone, say — is tempting but brittle, given the estimated 24,000 device variations. Let the content, not the device, determine your breakpoints. And don't forget that modern CSS grid and flexible layout often obviate breakpoint queries entirely:
.container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
For more along that line, see thinking beyond width in media queries and Una Kravets' ten modern one-line layouts.
Offer dark mode
Using prefers-color-scheme with CSS custom properties means defining token colors once and swapping the whole set inside the media query.
A responsive card gallery without width
Orientation, hover, and motion preferences can handle layout gracefully:
orientation: portraitmoves a sidebar to the page top;landscapekeeps it on the sidepointer: coarsevs.finedetermines clickable target sizeshovercapabilities show or hide card checkboxesprefers-reduced-motion: reduceremoves animations
The card sizing itself uses a minmax() grid function, no media query required. The full result is responsive without ever measuring width.
Target an iPhone in landscape mode
/* iPhone X Landscape */
@media only screen
and (min-device-width: 375px)
and (max-device-width: 812px)
and (-webkit-min-device-pixel-ratio: 3)
and (orientation: landscape) {
/* Styles! */
}
The orientation feature tests whether a device is wide (landscape) or tall (portrait):

Media queries can't identify the exact device, but pairing orientation with device dimensions narrows the match — the snippet above targets the iPhone X.
Sticky header on tall screens only
A height query keeps a fixed navigation bar pinned on tall viewports but detaches it on short ones, preserving precious screen real estate. Height-based design matters more as mobile viewports in all dimensions — Apple's hero scaling is a strong example.
Fluid typography without the media query
Setting a small default font on <html> and a large one in a width query was the original fluid-type approach. Newer CSS math functions like min(), max(), and clamp() get the job done in a single declaration.
Larger touch targets for coarse pointers
Detecting hover capability distinguishes touch devices from mouse-driven ones. Targeting coarse pointers to enlarge elements (like the checkboxes in this demo) creates more forgiving targets on touchscreens. Just remember such assumptions aren't always accurate — Patrick Lauke's article lists the pitfalls of interaction media features.
Specifications
- Media Queries Level 4 (Candidate Recommendation)
- Media Queries Level 5 (Working Draft)



