CSS Media queries are a way to target browser by certain characteristics, features, and user preferences, then apply styles or run other code based on those things. Perhaps the most common media queries in the world are those that target particular viewport ranges and apply custom styles, which birthed the whole idea of responsive design.
/* When the browser is at least 600px and above */
@media screen and (min-width: 600px) {
.element {
/* Apply some styles */
}
}
There are lots of other things we can target beside viewport width. That might be screen resolution, device orientation, operating system preference, or even more among a whole bevy of things we can query and use to style content.
Looking for a quick list of media queries based on the viewports of standard devices, like phones, tablets and laptops? Check out our collection of snippets.
Using media queries
Media queries are commonly associated with CSS, but they can be used in HTML and JavaScript as well.
HTML
There are a few ways we can use media queries directly in HTML.
There’s the <link>
element that goes right in the document <head>
. In this example. we’re telling the browser that we want to use different stylesheets at different viewport sizes:
<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>
Why would you want to do that? It can be a nice way to fine-tune the performance of your site by splitting styles up in a way that they’re downloaded and served by the devices that need them.
But just to be clear, this doesn’t always prevent the stylesheets that don’t match those media queries from downloading, it just assigns them a low loading priority level.
So, if a small screen device like a phone visits the site, it will only download the stylesheets in the media queries that match its viewport size. But if a larger desktop screen comes along, it will download the entire bunch because it matches all of those queries (well, minus the print query in this specific example).
That’s just the <link>
element. As our guide to responsive images explains, we can use media queries on <source>
element, which informs the <picture>
element what version of an image the browser should use from a set of image options.
<picture>
<!-- Use this image if the screen is at least 800px wide -->
<source srcset="cat-landscape.png" media="(min-width: 800px)">
<!-- Use this image if the screen is at least 600px wide -->
<source srcset="cat-cropped.png" media="(min-width: 600px)">
<!-- Use this image if nothing matches -->
<img src="cat.png" alt="A calico cat with dark aviator sunglasses.">
</picture>
Again, this can be a nice performance win because we can serve smaller images to smaller devices — which presumably (but not always) will be low powered devices that might be limited to a data plan.
And let’s not forget that we can use media queries directly on the <style>
element as well:
<style>
p {
background-color: blue;
color: white;
}
</style>
<style media="all and (max-width: 500px)">
p {
background-color: yellow;
color: blue;
}
</style>
CSS
Again, CSS is the most common place to spot a media query in the wild. They go right in the stylesheet in an @media
rule that wraps elements with conditions for when and where to apply a set of styles when a browser matches those conditions.
/* Viewports between 320px and 480px wide */
@media only screen and (min-device-width: 320px) and (max-device-width: 480px) {
.card {
background: #bada55;
}
}
It’s also possible to scope imported style sheet but as a general rule avoid using @import since 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;
JavaScript
We can use media queries in JavaScript, too! And guess, what? They’re work a lot like they do in CSS. The difference? We start by using the window.matchMedia()
method to define the conditions first.
So, say we want to log a message to the console when the browser is at least 768px wide. We can create a constant that calls matchMedia()
and defines that screen width:
// Create a media condition that targets viewports at least 768px wide
const mediaQuery = window.matchMedia( '( min-width: 768px )' )
Then we can fire log to the console when that condition is matched:
// 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!')
}
Unfortunately, this only fires once so if the alert is dismissed, it won’t fire again if we change the screen width and try again without refreshing. That’s why it’s a good idea to use a listener that checks for 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)
Check out Marko Ilic’s full post on “Working with JavaScript Media Queries” for a deeper dive on this, including a comparison of using media queries with an older JavaScript approach that binds a resize
event listener that checks window.innerWidth
or window.innerHeight
to fire changes.
Anatomy of a Media Query
Now that we’ve seen several examples of where media queries can be used, let’s pick them apart and see what they’re actually doing.
@media
@media [media-type] ([media-feature]) {
/* Styles! */
}
The first ingredient in a media query recipe is the @media
rule itself, which is one of many CSS at-rules. Why does @media
get all the attention? Because it’s geared to the type of media that a site is viewed with, what features that media type supports, and operators that can be combined to mix and match simple and complex conditions alike.
Media types
@media screen {
/* Styles! */
}
What type of media are we trying to target? In many (if not most) cases, you’ll see a screen
value used here, which makes sense since many of the media types we’re trying to match are devices with screens attached to them.
But screens aren’t the only type of media we can target, of course. We have a few, including:
all
: Matches all devicesprint
: Matches documents that are viewed in a print preview or any media that breaks the content up into pages intended to print.screen
: Matches devices with a screenspeech
: Matches devices that read the content audibly, such as a screenreader. This replaces the now deprecatedaural
type since Media Queries Level 4.
To preview print styles in a screen all major browsers can emulate the output of a print stylesheet using DevTools. Other media types such as tty
, tv
, projection
, handheld
, braille
, embossed
and aural
have been deprecated and, while the spec continues to advise browsers to recognize them, they must evaluate to nothing. If you are using one of these consider changing it for a modern approach.
Media features
Once we define the type of media we’re trying to match, we can start defining what features we are trying to match it to. We’ve looked at a lot of examples that match screens to width, where screen
is the type and both min-width
and max-width
are features with specific values.
But there are many, many (many!) more “features” we can match. Media Queries Level 4 groups 18 media features into 5 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. | portrait landscape | |
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). | scroll optional-paged paged | 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). | scroll none | 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). | interlace progressive | |
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 . | slow fast none | Media Queries Level 4 |
environment-blending | A method for determining the external environment of a device, such as dim or excessively bright places. | opaque additive subtractive | |
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) | fullscreen standalone minimal-ui browser | 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 | srgb p3 rec2020 | 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. | standard high | |
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) | inverted none | 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). | coarse fine none | 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 | hover none | 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) | coarse fine none | 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
The spec references user agents, including TVs, that render video and graphics in two separate planes that each have their own characteristics. The following features describe those planes.
Feature | Summary | Values | Added |
---|---|---|---|
video-color-gamut | Describes the approximate range of colors supported by the video plane of the browser and user device | srgb p3 rec2020 | 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. | standard high | 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 | enabled initial-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-preference reduce | Media Queries Level 5 |
prefers-reduced-transparency | Detects if the user’s system settings prevent transparent across elements. | no-preference reduce | 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-preference high low forced | 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. | light dark | Media Queries Level 5 |
forced-colors | Tests whether the browser restricts the colors available to use (which is none or active ) | active none | Media Queries Level 5 |
prefers-reduced-data | Detects if the user prefers to use less data for the page to be rendered. | no-preference reduce | 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 queries support logical operators like many programming languages so that we can match media types based on certain conditions. The @media
rule is itself a logical operator that is basically stating that “if” the following types and features are matches, then do some stuff.
and
But we can use the and
operator if we want to target screens within a range of widths:
/* Matches screen between 320px AND 768px */
@media screen (min-width: 320px) and (max-width: 768px) {
.element {
/* Styles! */
}
}
or
(or comma-separated)
We can also comma-separate features as a way of using an or
operator to match different ones:
/*
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! */
}
}
not
Perhaps we want to target devices by what they do not support or match. This declaration removes the body’s background color when the device is a printer and can only show one color.
@media print and ( not(color) ) {
body {
background-color: none;
}
}
Want to go deeper? Check out “CSS Media Queries: Quick Reference & Guide” from the DigitalOcean community for more examples that follow the syntax for media quieries.
Do you really need CSS media queries?
Media queries are a powerful tool in your CSS toolbox with exciting hidden gems. But if you accomodate your design to every possible situation you’ll end up with a codebase that’s too complex to maintain and, as we all know, CSS is like a bear cub: cute and inoffensive but when it grows it will eat you alive.
That’s why I recommend following Ranald Mace’s concept of Universal Design which is “the design of products to be usable by all people, to the greatest extent possible, without the need for adaptation or specialized design.”
In “Accessibility for Everyone” Laura Kalbag explains that the difference between accessible and universal design is subtle but important. An accessible designer would create a large door for people on a wheel chair to enter, while a universal designer would produce an entry that anyone would fit disregarding of their abilities.
I know that talking about universal design on the web is hard and almost sound utopian, but think about it, there are around 150 different browsers, around 50 different combinations of user preferences, and as we mentioned before more than 24000 different and unique Android devices alone.
This means that there are at least 18 million possible cases in which your content might be displayed. In the words of the fantastic Miriam Suzanne, “CSS out here trying to do graphic design of unknown content on an infinite and unknown canvas, across operating systems, interfaces, & languages. There’s no possible way for any of us to know what we’re doing.”
That’s why assuming is really dangerous, so when you design, develop and think about your products leave assumptions behind and use media queries to make sure that your content is displayed correctly in any contact and before any user.
Matching value ranges
Many of the media features outlined in the previous section — including width
, height
, color
and color-index
— can be prefixed with min-
or max-
to express minimum or maximum constraints. We’ve already seen these in use throughout many of the examples, but the point is that we can create a range of value to match instead of having to declare specific values.
In the following snippet, we’re painting the body’s background purple when the viewport width is wider than 30em and narrower than 80em. If the viewport width does not match that range of values, then it will fallback to white.
body {
background-color: #fff;
}
@media (min-width: 30em) and (max-width: 80em) {
body {
background-color: purple;
}
}
Media Queries Level 4 specifies a new and simpler syntax using less then (<
), greater than (>
) and equals (=
) operators. So, that last example can be converted to the new syntax, like so:
@media (30em <= width <= 80em) {
/* ... */
}
Nesting and complex decision making
CSS allows you to nest at-rules or group statements using parentheses, making it possible to go as deep as we want to evaluate complex operations.
@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! even thought it’s possible to create powerful and complex expressions, you might end up with a very opinionated, hard to maintain query. As Brad Frost puts it: “The more complex our interfaces are, the more we have to think to maintain them properly.”
Accessibility
Many of the features added in Media Queries Level 4 are centered around accessibility.
prefers-reduced-motion
prefers-reduced-motion
detects if the user has the reduced motion preference activated to minimize the amount of movements and animations. It takes two values:
no-preference
: Indicates that the user has made no preference known to the system.reduce
: Indicates that user has notified the system that they prefer an interface that minimizes the amount of movement or animation, preferably to the point where all non-essential movement is removed.
This preference is generally used by people who suffer from vestibular disorder or vertigo, where different movements result in loss of balance, migraine, nausea or hearing loss. If you ever tried to spin quickly and got dizzy, you know what it feels like.
In a fantastic article by Eric Bailey, he suggests stopping all animations with this code:
@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;
}
}
Popular frameworks like Bootstrap have this feature on by default. In my opinion there is no excuse not to use prefers-reduced-motion
— just use it.
prefers-contrast
The prefers-contrast
feature informs whether the user has chosen to increase or reduce contrast in their system preferences or the browser settings. It takes three values:
no-preference
: When a user has made no preference known to the system. If you use it as a boolean it’ll evaluate false.high
: When a user has selected the option to display a higher level of contrast.low
: When a user has selected the option to display a lower level of contrast.
At the moment of writing this feature is not supported by any browser. Microsoft has done a non-standard earlier implementation with the -ms-high-contrast
feature that works only on Microsoft Edge v18 or earlier (but not Chromium-based versions).
.button {
background-color: #0958d8;
color: #fff;
}
@media (prefers-contrast: high) {
.button {
background-color: #0a0db7;
}
}
This example is increasing the contrast of a the class button from AA to AAA when the user has high contrast on.
inverted-colors
The inverted-colors
feature informs whether the user has chosen to invert the colors on their system preferences or the browser settings. Sometimes this option is used as an alternative to high contrast. It takes three values:
none
: When colors are displayed normallyinverted
: When a user has selected the option to invert colors
The problem with inverted colors is that it’ll also invert the colors of images and videos, making them look like x-ray images. By using a CSS invert filter you can select all images and videos and invert them back.
@media (inverted-colors) {
img, video {
filter: invert(100%);
}
}
At the time of writing this feature is only supported by Safari.
prefers-color-scheme
Having a “dark mode” color scheme is something we’re seeing a lot more of these days, and thanks to the prefers-color-scheme
feature, we can tap into a user’s system or browser preferences to determine whether we serve a “dark” or a “light” theme based on the ir preferences.
It takes two values:
light
: When a user has selected that they prefer a light theme or has no active preferencesdark
: When a user has selected a dark display in their settings
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;
}
}
As Adhuham explains in the complete guide to Dark Mode there is way more to it than just changing the color of the background. Before you jump into doing dark mode remember that if you don’t have a very smart implementation strategy you might end up with a code base that’s really hard to maintain. CSS variables can do wonders for it but that’s a subject for another article.
What lies ahead?
Media Queries Level 5 is currently in Working Draft status, which means a lot can change between now and when it becomes a recommendation. But it includes interesting features that are worth mentioning because they open up new ways to target screens and adapt designs to very specific conditions.
User preference media features
Hey, we just covered these in the last section! Oh well. These features are exciting because they’re informed by a user’s actual settings, whether they are from the user agent or even at the operating system level.
Detecting a forced color palette
This is neat. Some browsers will limit the number of available colors that can be used to render styles. This is called “forced colors mode” and, if enabled in the browser settings, the user can choose a limited set of colors to use on a page. As a result, the user is able to define color combinations and contrasts that make content more comfortable to read.
The forced-colors
feature allows us to detect if a forced color palette is in use with the active
value. If matched, the browser must provide the required color palette through the CSS system colors. The browser is also given the leeway to determine if the background color of the page is light or dark and, if appropriate, trigger the appropriate prefers-color-scheme
value so we can adjust the page.
Detecting the maximum brightness, color depth, and contrast ratio
Some devices (and browsers) are capable of super bright displays, rendering a wide range of colors, and high contrast ratios between colors. We can detect those devices using the dynamic-range
feature, where the high
keyword matches these devices and standard
matches everything else.
We’re likely to see changes to this because, as of right now, there’s still uncertainty about what measurements constitute “high” levels of brightness and contrast. The browser may get to make that determination.
Video prefixed features
The spec talks about some screens, like TVs, that are capable of displaying video and graphics on separate “planes” which might be a way of distinguishing the video frame from other elements on the screen. As such, Media Queries Level 5 is proposing a new set of media features aimed at detecting video characteristics, including color gamut and dynamic range.
There are also proposals to detect video height, width and resolution, but the jury’s still out on whether those are the right ways to address video.
Browser support
Browsers keep evolving and since by the time you are reading this post chances are that browser support for this feature might change, please check MDN updated browser compatibility table.
A note on container queries
Wouldn’t be cool if components could adapt themselves on their own size instead of the browser’s? That’s what the concept of CSS Container Queries is all about. We currently only have the browser screen to make those changes via media queries.
That’s unfortunate, as the viewport isn’t always a direct relationship to how big the element itself is. Imagine a widget that renders in many different contexts on a site: sometimes in a sidebar, sometimes in a full-width footer, sometimes in a grid with unknown columns.
This is the problem that container queries try to solve. Ideally we could adapt styles of an element according to the size of itself instead of of the size of the viewport. Chrome 105 released support for CSS Container Queries. Same deal with Safari 16.1. Firefox is all we’re really waiting at the time of writing to get broad support.
This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.
Desktop
Chrome | Firefox | IE | Edge | Safari |
---|---|---|---|---|
106 | 110 | No | 106 | 16.0 |
Mobile / Tablet
Android Chrome | Android Firefox | Android | iOS Safari |
---|---|---|---|
115 | 115 | 115 | 16.0 |
Examples
Let’s look at a bunch of media query examples. There are so many combinations of media types, features, and operators that the number of possibilities we could show would be exhaustive. Instead, we’ll highlight a handful based on specific media features.
Adjust layout at different viewport widths
More info
This is the probably the most widely used media feature. It informs the width of the browser’s viewport including the scrollbar. It unlocked the CSS implementation of what Ethan Marcotte famously coined responsive design: a process by which a design responds to the size of the viewport using a combination of a fluid grid, flexible images, and responsive typesetting.
Later, Luke Wroblewski evolved the concept of responsive design by introducing the term mobile-first, encouraging designers and developers to start with the small-screen experience first then progressively enhance the experience as the screen width and device capabilities expand.
A mobile-first can usually be spotted by it’s use of min-width
instead of max-width
. If we start with min-width
, we’re essentially saying, “hey, browser, start here and work up.” On the flip side, max-width
is sort of like prioritizing larger screens.
One approach for defining breakpoints by width is using the dimensions of standard devices, like the exact pixel width of an iPhone. But there are many, many (many), many different phones, tables, laptops, and desktops. Looking at Android alone, there are more than 24,000 variations of viewport sizes, resolutions, operating systems, and browsers, as of August 2015.
So, while targeting the precise width of a specific device might be helpful for troubleshooting or one-off fixes, it’s probably not the most robust solution for maintaining a responsive architecture. This isn’t a new idea by any stretch. Brad Frost was already preaching the virtues of letting content — not devices — determine breakpoints in his post “7 habits of highly effective media queries” published back in 2013.
And even though media queries are still a valid tool to create responsive interfaces, there are many situations where it’s possible to avoid using width at all. Modern CSS allow us to create flexible layouts with CSS grid and flex that adapts our content to the viewport size without a need to add breakpoints. For example, here is a grid layout that adapts how many columns it will have without any media queries at all.
.container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
There are many articles about thinking beyond width, I wrote about it a few years ago and I recommend checking out Una Kravet’s Ten modern layouts in one line of CSS.
Dark mode
More info
This example is pulled straight from our Guide to Dark Mode on the Web. The idea is that we can detect whether a user’s system settings are configured to light or dark mode using the prefers-color-scheme
feature and then define an alternate set of colors for the rendered UI.
Combining this technique with CSS custom properties makes things even easier because they act like variables that we only need to define once, then use throughout the code. Need to swap colors? Change the custom property value and it updates everywhere. That’s exactly what prefers-color-scheme
does. We define a set of colors as custom properties, then redefine them inside a media query using the prefer-color-scheme
feature to change colors based on the user’s settings.
Detecting orientation, hover and motion on a responsive card gallery
More info
This gallery is responsive without using the width
feature.
It detects the orientation
of the viewport. If it’s a portrait
viewport, the sidebar will became a header; if it’s landscape
it stays off to the side.
Using the pointer
media feature, it decides if the main input device is coarse
— like a finger — or fine
— like a mouse cursor — to set the size of the clickable areas of the checkboxes.
Then, by using the hover
media feature, the example checks if the device is capable of hovering (like a mouse cursor) and display a checkbox in each card.
The animations are removed when prefers-reduced-motion
is set to reduce
.
And did you notice something? We’re actually not using media queries for the actual layout and sizing of the cards! That’s handled using the minmax()
function on the .container
element to show how responsive design doesn’t always mean using media queries.
In short, this is a fully responsive app without ever measuring width
or making assumptions.
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! */
}
More info
The orientation
media feature tests whether a device is rotated the wide way (landscape) or the tall way (portrait).
While media queries are unable to know exactly which device is being used, we can use the exact dimensions of a specific device. The snippet above is targets the iPhone X.
Apply a sticky header for large viewports
More info
In the example above, we’re using height
to detached fixed elements and avoid taking up too much screen real estate when the screen is too short. A horizontal navigation bar is in a fixed position when the screen is tall, but detaches itself on shorter screens.
Like the width
feature, height
detects the height of the viewport, including the scrollbar. Many of us browse the web on small devices with narrow viewports, making designing for different heights more relevant than ever. Anthony Colangelo describes how Apple uses the height
media feature in a meaningful way to deal with the size of the hero image as the viewport’s height changes.
Responsive (fluid) typography
More info
A font can look either too big or too small, depending on the size of the screen that’s showing it. If we’re working on a small screen, then chances are that we’ll want to use smaller type than what we’d use on a much larger screen.
The idea here is that we’re using the browser’s width to scale the font size. We set a default font size on the <html>
that acts as the “small” font size, then set another font size using a media query that acts as the “large” font size. In the middle? We set the font size again, but inside another media query that calculates a size based on the browser width.
The beauty of this is that it allows the font size to adjust based on the browser width, but never go above or below certain sizes. However, there is a much simpler way to go about this that requires no media queries at all, thanks to newer CSS features, like min()
, max()
, and clamp()
.
course
pointer
Provide bigger touch targets when devices have a More info
Have you ever visited a site that had super tiny buttons? Some of us have fat fingers making it tough to tap an object accurately without inadvertently tapping something else instead.
Sure, we can rely on the width
feature to tell if we’re dealing with a small screen, but we can also detect if the device is capable of hovering over elements. If it isn’t then it’s probably a touch device, or perhaps a device that supports both, like the Microsoft Surface.
The demo above uses checkboxes as an example. Checkboxes can be a pain to tap on when viewing them on a small screen, so we’re increasing the size and not requiring a hover if the device is incapable of hover
events.
Again, this approach isn’t always accurate. Check out Patrick Lauke’s thorough article that details potential issues working with hover
, pointer
, any-hover
and any-pointer
.
Specifications
- Media Queries Level 4 (Candidate Recommendation)
- Media Queries Level 5 (Working Draft)
Special thanks to Sarah Rambacher who helped to review this guide.
I recently learnt that add Listener is deprecated, and we should instead use addeventListener.
Thank you for your excellent explanation. On implementing I encountered a missing ‘.matches’ after ‘if ( mediaQuery’ in your Javascript example:
// Check if the media query is true
if ( mediaQuery ) {
// Then log the following message to the console
console.log(‘Media Query Matched!’)
}
This comment-editor does not allow me to format my comment as I would like.
This worked for me:
// Check if the media query is true
if ( mediaQuery.matches ) {
// Then log the following message to the console
console.log(‘Media Query Matched!’)
}
if (mediaQuery.matches)
Thanks for this!
The subtitle
“@media screen (min-width: 320px) and (max-width: 768px)”
in “Anatomy of a Media Query” is misleading.
According to the syntax at MDN there should be an “and” after “screen” so you may want to correct it to: “@media screen and (min-width: 320px) and (max-width: 768px)”.
you can add a link to https://caniuse.com/mdn-css_at-rules_media_range_syntax to the end of the section https://css-tricks.com/a-complete-guide-to-css-media-queries/#using-min-and-max-to-match-value-ranges
Thanks I was actually thinking the Media query was starting to not matter..since in web development the ideal is to always follow the next big trend..