How much do you really know about media queries?
How much do you really know about media queries? êŽë š
Earlier this year, I realized that I knew very little about possibly most of the media queries.
Maybe thatâs not surprising â since I never hear about them.
Beyond the classics like @media(min-width: 400px) and the user-preference media queries such as @media (prefers-reduced-motion: reduce), and maaaybe orientation, I canât say that I was using media queries a whole lot. Especially since flexbox, grid layout, and calc() became fairly normalized, in addition to newer sizing values such as min-content, max-content, fit-content, and more recently, stretch.
But there are so many descriptors! Most of these Iâve never used:
any-hoverany-pointeraspect-ratiocolorcolor-gamutcolor-indexdisplay-modedynamic-rangeenvironment-blendingforced-colorsgridheighthorizontal-viewport-segmentshoverinverted-colorsmonochromenav-controlsorientationoverflow-blockoverflow-inlinepointerprefers-color-schemeprefers-contrastprefers-reduced-dataprefers-reduced-motionprefers-reduced-transparencyresolutionscanscriptingupdatevertical-viewport-segmentsvideo-color-gamutvideo-dynamic-rangewidth
Itâs not that I thought media queries were only for responsive design, but out of sight, out of mind, right? Nobody talks about them. Granted, some of them have few use-cases, but after being more mindful of them for a few months, Iâve come to the conclusion that many of them definitely deserve more attention.
Plus, there are more ways to write media queries now. This includes an @custom-media at-rule for saving media queries as custom properties, which is super cool.
Letâs dig in.
hover/pointer/any-hover/any-pointer
Modern devices can be used in many different ways. For example, itâs not uncommon to hook a mouse up to a tablet, which is why we shouldnât think of tablets as touchscreen devices anymore. In fact, itâs now unwise to use media queries to query for what specific device they âareâ, which is why the media types tty, tv, projection, handheld, braille, embossed, aural, and speech were deprecated (all, print, and screen are the only types now, but all of them will likely be deprecated eventually).
These days itâs more prudent to query the deviceâs capabilities and how the user has set it up, and thatâs where the hover, pointer, any-hover, and any-pointer media query descriptors come into it. At first glance, hover and pointer sound like the same thing, and while you could use them interchangeably for a high-level use-case, using them together is what truly enables us to target capabilities:
/* Primary input mechanism is a mouse */
@media (hover: hover) and (pointer: fine) {
...
}
/* Primary input mechanism is a joystick */
@media (hover: hover) and (pointer: coarse) {
...
}
/* Primary input mechanism is touch */
/* (also targets joystick-less controllers) */
@media (hover: none) and (pointer: coarse) {
...
}
/* Primary input mechanism is a stylus */
@media (hover: none) and (pointer: fine) {
...
}
As you mightâve guessed from @media (hover: none) and (pointer: coarse) targeting joystick-less controllers (e.g., the D-pad-only controllers that come with old-ish TVs and game consoles) in addition to touchscreen devices. Itâs not a fool-proof method, but it doesnât make any overly bold assumptions about input mechanisms.
Hereâs a simple use-case:
button:hover {
/* Default hover styles */
@media (hover: none) and (pointer: coarse) {
/* High-visibility hover styles when obscured by fingers */
}
}
The possibilities are endless, and you can of course query just one descriptor (e.g., @media (hover: none)), but theyâre rarely useful individually. A more powerful usage is in the combinative way demonstrated above, but instead of declaring alternative hover styles for touchscreen devices, providing a more touch-friendly UI, perhaps by using the display property to show/hide different components altogether.
Itâs also worth mentioning that hover and pointer query the primary input mechanism, whereas their any-hover and any-pointer counterparts query all input mechanisms. For example, the following code targets touchscreen devices with a mouse or stylus hooked up as an additional input mechanism, which might be a more accurate representation of the userâs setup:
@media (hover: none) and (pointer: coarse) and (any-pointer: fine) {
...
}
forced-colors`
Users can choose color schemes at the OS or browser level, or create their own. As an example, Firefox allows us to change the color of text, backgrounds, and links, whereas High Contrast Mode in Windows offers more customization options and has themes to choose from. In an ideal world we wouldnât need to do anything here, but if something turns out to be inaccessible or just looks odd, we can use the forced-colors media query descriptor to correct it:
@media (forced-colors: active) {
/* Corrective styles, like applying borders to controls that might not otherwise have them. */
}
If something must be a certain color, simply apply forced-color-adjust: none along with the necessary property and value:
#this-must-be-blue {
color: blue;
forced-color-adjust: none;
}
Itâs also worth noting that when forced-colors is set to active, the userâs preferred color scheme will be set to either light or dark depending on their chosen theme. You can query the assigned color scheme using @media (prefers-color-scheme: light) and @media (prefers-color-scheme: dark). In addition, prefers-contrast will be set to more, less, or custom.
You can learn more about forced-colors at MDN.
width/height
âwidth and height? I already know about those!â
Yes, these are certainly the most commonly used media queries, but did you know that a new syntax with comparison operators was introduced about three years ago? I like the operators because they can be inclusive or exclusive of the value in question. For example, we can express âmore thanâ using > or âmore than or equal toâ using >=, which enables us to drop the min-/max- prefixes (which are only inclusive) and standardize the width and height keywords across the board:
@media (width: 900px) {
/* Styles when viewport width is 900px */
}
@media (width < 900px) {
/* Styles when viewport width is less than 900px */
}
@media (width > 900px) {
/* Styles when viewport width is more than 900px */
}
@media (width <= 900px) {
/* Styles when viewport width is less than or equal to 900px */
}
@media (width >= 900px) {
/* Styles when viewport width is more than or equal to 900px */
}
Thereâs also a between-this-and-that syntax, which oddly requires âlessyâ operators but is otherwise easy to remember:
@media (900px < width < 1200px) {
/* Styles when viewport width is 901-1199px (exclusive) */
}
@media (900px <= width <= 1200px) {
/* Styles when viewport width is 900-1200px (inclusive) */
}
If youâre not keen on this syntax (yeah, itâs a little weird), you can still use the old syntax. Or, even better, use a little of both:
@media (width > 900px) and (width < 1200px) {
/* Styles when viewport width is 901-1199px (exclusive) */
}
@media (width >= 900px) and (width <= 1200px) {
/* Styles when viewport width is 900-1200px (inclusive) */
}
Itâs worth noting that logical properties like inline-size and block-size also work, but it often makes more sense in @media queries to deal with physical properties since the browser window itself doesnât change when the flow changes.
inverted-colors
Since there are a variety of viewing modes (light, dark, high-contrast, etc.) already, having a mode that inverts colors might seem unnecessary, but itâs useful when light or dark mode isnât available (because inverting the colors somewhat toggles the mode) and when the user has a Color Vision Deficiency (because inverting the colors creates new colors). While the former issue can be fixed by respecting light-dark mode preferences and implementing light-dark mode toggling, the latter issue requires color inversion.
We can query this mode using the inverted-colors media query descriptor, and what we want to do with it is revert shadows and non-UI media back to ânormal,â otherwise the non-UI media will look extremely odd and the shadows will appear as highlights. To invert a shadow color (or any color), convert it to HSL format using the relative color syntax, and add 180 to the Hue (h):
For media, filter: invert(1) will do the trick:
.box-shadow {
/* When not inverted, normal box shadow */
@media (inverted-colors: none) {
box-shadow: 0 0 8px limegreen;
}
/* When inverted, add 180 to Hue/h to revert */
@media (inverted-colors: inverted) {
box-shadow: 0 0 8px hsl(from limegreen calc(h + 180) s l);
}
}
/* Same thing for text shadows */
.text-shadow {
@media (inverted-colors: none) {
text-shadow: 0 0 8px limegreen;
}
@media (inverted-colors: inverted) {
text-shadow: 0 0 8px hsl(from limegreen calc(h + 180) s l);
}
}
/* Same thing for drop shadows */
.drop-shadow {
@media (inverted-colors: none) {
filter: drop-shadow(0 0 8px limegreen);
}
@media (inverted-colors: inverted) {
filter: drop-shadow(0 0 8px hsl(from limegreen calc(h + 180) s l));
}
}
.non-ui:is(img, svg, video, ...) {
box-shadow: 0 0 8px black;
@media (inverted-colors: inverted) {
/* Reverts the media and shadow at the same time */
filter: invert(1);
}
}
You can see it in action here (make sure to toggle inverted colors to see the UI change but images/shadows stay the same):
While forced colors are better as long as users can choose themes, some operating systems donât support it (macOS doesnât).
As of September 2025, only Safari supports the inverted-colors media query descriptor prefers-reduced-motion.
orientation
An orientation query is a rather simple media query descriptor that resolves to portrait if the viewport is larger vertically or landscape if itâs larger horizontally.
The thing with orientation is that users hold their handheld device differently based on it, which could mean having to code a slightly different layout for each orientation. Even if youâre lucky enough to avoid that issue, youâre still likely to run into issues with aspect ratios or relative units (e.g., viewport or percentage units) where something looks proportionate in one orientation but very exaggerated or de-exaggerated in the other.
Targeting a specific orientation is easy enough:
@media (orientation: portrait) {
...
}
@media (orientation: landscape) {
...
}
As for optimizing layouts for certain orientations on touchscreen devices, youâll want to combine the orientation descriptor with the âtouchscreen descriptorsâ mentioned earlier:
@media (orientation: landscape) and (hover: none) and (pointer: coarse) {
/* Landscape touchscreen styles */
}
Generally speaking, due to the diversity of devices and media queries available today, avoid using orientation to detect the device type. For example, vertical monitors do exist (youâll see them at airports, for instance), so donât assume that portrait means handheld.
overflow-inline/overflow-block
The overflow-inline and overflow-block media query descriptors with the scroll value match vendors that support overflow content on their respective axes. Weâre basically talking about web browsers here, as well as windows that display HTML content and have a scrolling mechanisms.
/* Vendor supports inline-axis scrolling */
@media (overflow-inline: scroll) {
...
}
/* Vendor supports block-axis scrolling */
@media (overflow-block: scroll) {
...
}
The overflow-inline and overflow-block media query descriptors with the none value match vendors that donât support overflow content on their respective axes. To be clear, this doesnât mean that the document has overflow: hidden declared, it means that scrolling isnât supported at all (e.g., a HTML document thatâs rendered as an OS-level widget or on a digital billboard).
/* Vendor doesnât support inline-axis scrolling */
@media (overflow-inline: none) {
...
}
/* Vendor doesnât support block-axis scrolling */
@media (overflow-block: none) {
...
}
A more common use-case exists for overflow-block, which accepts another value: paged. Naturally, paged refers to printed documents and HTML-based ebooks such as EPUBs, with printed documents obviously being the most common of the two.
/* Vendor supports block-axis scrolling, but is paged media */
@media (overflow-block: paged) {
...
}
The spec alludes to all media types being deprecated eventually, so this is somewhat designed to replaced the print media type.
The prefers- family
The prefers- family of media query descriptors should be used to cater to users with preferences and comply with the WCAG (Web Content Accessibility Guidelines).
prefers-color-scheme
/* User prefers light mode */
@media (prefers-color-scheme: light) {
...
}
/* User prefers dark mode */
@media (prefers-color-scheme: dark) {
...
}
I like to query this using JavaScript, check a âdark modeâ checkbox accordingly, and then use CSSâs light-dark() function to style according to the inferred (or user-specified) mode:
/* If dark mode is inferred, check dark mode box */
if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
document.querySelector("#dark-mode").checked = true;
}
/* But users can change at will */
<input type="checkbox" id="dark-mode">
/* If dark mode isnât checked */
:root:has(#dark-mode:indeterminate) {
color-scheme: light;
}
/* If dark mode is checked */
:root:has(#dark-mode:checked) {
color-scheme: dark;
}
body {
/* Then set values conditionally */
background: light-dark(white, black);
}
prefers-contrast
There are four possible values for prefers-contrast:
/* User has no contrast preference */
@media (prefers-contrast: no-preference) {
...
}
/* User prefers more contrast */
@media (prefers-contrast: more) {
...
}
/* User prefers less contrast */
@media (prefers-contrast: less) {
...
}
/* User has forced a color theme */
@media (prefers-contrast: custom) {
...
}
Youâre unlikely to use no-preference or more because the WCAG has defined a minimum level of color contrast that websites must adhere to. In addition, prefers-contrast: custom means that the user has specified a color theme preference via forced colors mode thatâs neither a low or high contrast theme. Itâs a matter of semantics that again has no practical use, so simply use low for the minority of users that prefer low contrast due to migraines, Dyslexia, and so on. This doesnât violate WCAG as long as thereâs a mechanism for switching low-contrast mode off.
prefers-reduced-data
prefers-reduced-data: reduce targets users that prefer to consume less data when browsing while connected to their cellular network, whereas prefers-reduced-data: no-preference naturally targets those who have no preference. Personally, Iâd love to see more websites reduce their size for those with slower connections and/or smaller data allowances, perhaps by swapping heavy decorative background images for something else:
div {
@media (prefers-reduced-data: no-preference) {
/* Heavy decorative image */
background-image: url(<url>);
}
@media (prefers-reduced-data: reduce) {
/* Literally anything else */
background: <value>;
}
}
Or loading fonts conditionally:
@media (prefers-reduced-data: no-preference) {
@font-face {
font-family: lexend;
src: <value>;
}
}
body {
/* If user prefers reduced data, fallback font sans-serif will be used */
font-family: lexend, sans-serif;
}
As of October 2025, no web browser supports this.
prefers-reduced-motion*
The key to prefers-reduced-motion, which accepts the same values as prefers-reduced-data (no-preference and reduce), is simply to reduce (or remove, in extreme cases) transitions and animations for those with vestibular disorders, which interestingly affect 35% of adults by aged 40:
button {
@media (prefers-reduced-motion: no-preference) {
/* Continuously pulsate */
animation: pulse 1s infinite;
}
@media (prefers-reduced-motion: reduce) {
/* Pulsate once and not so intensely */
animation: scaleUpDown 1s;
}
}
prefers-reduced-transparency
Same here â your options are no-preference and reduce.
Iâd normally consider this one to be an edge case, but with Appleâs new Liquid Glass aesthetic being available to everybody now, I expect it to surge in popularity. If you must implement it, at least reduce transparency for those that donât like it or find it to be inaccessible:
div {
@media (prefers-reduced-transparency: no-preference) {
background: hsl(0 0 0 / 60%);
}
@media (prefers-reduced-transparency: reduce) {
background: hsl(0 0 0 / 80%);
}
}
As of September 2025, only Chrome and Edge support this.
resolution
resolution, min-resolution, and max-resolution accept values of the <resolution> data type (dpi, dpcm, dppx/x). A fantastic use-case for this is to serve higher-resolution images to higher-resolution devices, as you would when using the srcset and sizes HTML attributes. And remember, we can use the new range syntax here, so resolution > 1x instead of min-resolution: 1.01x:
div {
background-image: url("/image-1x.jpg");
@media (resolution > 1x) {
background-image: url("/image-2x.jpg");
}
@media (resolution > 2x) {
background-image: url("/image-3x.jpg");
}
@media (resolution > 3x) {
background-image: url("/image-4x.jpg");
}
}
Nesting media queries
CSS nesting has had full browser support since August 2023, enabling us to nest CSS rules inside other CSS rules, and that includes at-rules such as media queries. If youâve been using a CSS preprocessor such as LESS, Scss, or Sass for this, wellâŠyou donât need to anymore. Instead, to use an earlier example, you can nest media queries like this:
.box-shadow {
@media (inverted-colors: none) {
box-shadow: 0 0 8px limegreen;
}
@media (inverted-colors: inverted) {
box-shadow: 0 0 8px hsl(from limegreen calc(h + 180) s l);
}
}
This enables you to write media queries more contextually.
All in all
If you were anxious about exploring the now-many different types of media queries and their syntaxes, Iâd say thatâs fair. However, while catering to todayâs diversity of devices and user preferences is without a doubt getting more complex, these media queries are actually pretty easy to use once you know how.
Besides, WCAG 3.0, which could set the standard for the design of even more inclusive patterns, will be here eventually. Thisâd mean having the legal requirement of catering for more user preferences regardless of how edge case-y they are, and as weâve seen in recent years, new and amended accessibility laws (and similar laws such as GDPR) tend to cause a last-minute frenzy as the deadline for compliance draws closer. My advice? Get ahead of the curve and start putting those media queries to use!