Media Queries
Demo starter files
Background
Way back in 2010, media queries became the cornerstone that allowed the responsive web design revolution to take place. As mobile devices lit up and came online at an explosive rate, web designers were grappling with how to handle this completely different kind of browser client and viewing experience. The screens were so tiny! The internet was even slower! Metered data!
Early solutions were very clunky. They often involved building your whole website twice—a version for desktops and a version for mobile. This meant maintaining two copies of the design and the code, keeping them in sync, and attempting to detect and redirect viewers to the correct version of the website based on request headers and other heuristics. It was... not great.
Responsive Web Design changed all that, rather swiftly. Ethan Marcotte, a designer and writer, gave a talk at a web design conference and wrote an article coining the term.
Rather than designing completely different layouts or trying to deploy entirely separate websites for different devices, this "little" and relatively new feature in CSS was brought to light, and he showed us how it could make a single design and style sheet adapt to almost any size.
Use of media queries quickly grew to become a huge, integral part of most website designs.
Today, media queries still serve an important role in almost all website designs. But with more modern layout tools like flexbox and grid that have responsiveness built in, media queries are no longer as necessary.
What is a Media Query?
A media query is a CSS feature that allows us to conditionally apply a set of CSS rules, or even entirely separate style sheets, based on certain conditions.
A media query basically says "If these conditions are true, apply these styles."
Here's a very basic example.
Like this example, most of the time media queries are simply used to adjust for different screen sizes, though they can do a lot more. Increasingly, media queries are used to detect accessibility settings and user preferences. This CSS Tricks article (also linked in the Resources below) explains this very well, so I won't reiterate here.
Viewport Setup
First thing's first. We need to tell the browser that our website is indeed designed to accommodate all screen sizes. In order to prep a site for mobile styling, this meta tag must be included in the <head>
of every HTML document that uses media queries:
<meta name="viewport" content="width=device-width, initial-scale=1">
width=device-width
tells the browser that the width of our page should match the width of the device. initial-scale=1
tells the browser not to do any zooming when the page is first loaded.
It's a good idea to create your own template for new HTML documents, so you don't forget things like the <!DOCTYPE html>
and <meta charset>
. If you do this, just add this <meta viewport>
tag to your template so it's always included. VSCode's html:5
emmet abbreviation includes this tag already.
Why do we need this? Mobile browsers use zooming to display websites by default. When smart phones were a new phenomenon, most websites weren't designed for a such a small screen. To deal with this, mobile browsers did the only logical thing they could—they simply shrank everything down.
It's the safest assumption the browser can make since, even today, not all sites are adequately designed for display on tiny screens. Pinching and zooming to navigate around is crude, but it makes non-mobile web designs navigable.
Syntax
Most of the time media queries are written within the main style sheet, like this:
@media screen and (min-width: 56em) {
/* media-specific rules, usually indented one level */
}
They can also be written in HTML <link>
tags where the style sheets are loaded, but this is much less common.
A media query can have up to three parts:
- media types
- media features
- logical operators
Media Types
There are currently only four media types.
Name | Summary |
---|---|
all |
Suitable for all media types |
screen |
Intended for computer screens of any kind |
print |
Intended for printing and paged media |
speech |
Screenreaders |
If you skip the media type in your query and just test for features (which is allowed), the media type is usually assumed to be all
.
When media queries were first introduced, the spec included several media types for different genres of devices. These included things like handheld
, projection
, and tv
. Most of these have been removed from the spec and browsers ignore them. Phones can be many sizes. Tablets are as fast and capable as laptops. TVs can have keyboards connected. The lines between different categories of devices are too blurry to make these different designations useful. The categories that remain, above, are distinct types of devices and mediums that warrant their own categories.
Media Features
Beyond general categories, what we really want to test for are the features a device supports.
Each media feature must be wrapped in parentheses. There are many media features, but here are some of the most common:
Name | Summary |
---|---|
width |
Viewport (browser window) width, commonly measured in px, em, or rem. For simple queries this is all we need. |
height |
Viewport (browser window) height |
aspect-ratio |
Viewport aspect ratio, always expressed as a fraction: 16/9 |
orientation |
Orientation of the device, either portrait or landscape |
pointer / any-pointer |
Whether the device has a fine pointing device like a mouse or stylus, whether the interaction mode is coarse like touch, or none . |
hover / any-hover |
Whether the device has a pointing device that supports hover like a mouse or stylus, or none like touch. |
resolution |
Resolution of the device in dpi . Caveats and more info on caniuse |
Many of these features can also have a min-
and max-
prefix! (min-resolution: 192dpi)
, (max-width: 800px)
, and so on.
Preferences and Accessibility
Several new media features are being introduced with the Media Queries Level 5 spec. Some of these include:
prefers-reduced-motion
(usually for turning off animations and moving effects)prefers-reduced-transparency
prefers-contrast
prefers-color-scheme
(dark mode themes!)
Support is very mixed as these properties are quite new. Consult Caniuse, MDN, and search Google before using.
Combining Queries
We can build more complex media queries by combining media types and features using logical operators.
and
Links two or more features together, so that all of them must be true for the query to match.
@media screen and (orientation: landscape) and (min-width: 800px)
or
The word "or" never appears in media queries, but commas often do, and they serve the same purpose.
@media (min-width: 800px), (orientation: landscape)
This query is true if either condition before or after the comma is met.
not
Negates the entire query. When using not
, you must always list a media type (not just a feature). All of the conditions must be false for the query to take effect. not
must appear at the beginning of the query or the entire query will be ignored. In other words, you can't use it to negate part of a query.
Good:
@media not screen and (orientation: landscape) and (min-width: 800px)
Query is true if the device is not landscape, and not wider than 800px.
Bad:
@media screen and (orientation: landscape) and not (min-width: 800px)
This query is ignored. This does not mean "yes landscape, but not wider than 800px". A proper and less confusing way to express this would be without "not", because we're essentially creating a double-negative.
@media screen and (orientation: landscape) and (max-width: 800px)
Let's break here and do the demo, so you can see some media queries in action before we talk about best practices.
Target For Design, Not Screen Size
Building a website using media queries to target specific devices or screen sizes is futile. There are far too many devices to fit them into nice neat categories.
When designing a page layout, you'll likely have a few target display sizes in mind (i.e. "mobile" and "desktop"). The exact breakpoints between these however, will depend on the design. You'll need to use a process of testing and experimentation (especially on a desktop where you can resize the window) to see where logical breakpoints exist in your design.
Unless you're designing exclusively for a desktop experience, it's recommended to write your base styles to be mobile first, and work up from there.
Keep It Simple
The bottom line to keep in mind when crafting media queries is keep it simple. Stick to basic queries unless you need to do something special, and keep the number of queries to a minimum. With flexbox and grid, you may not need queries at all.
Remember... code is read, updated and maintained much more than it is written. And code that's easy to understand makes everybody happy!
Order Matters
We haven't discussed order and specificity in great detail, but you should know that style sheets are processed in the order that they are written. This means that if the same element is targeted more than once with the same rule, the latest rule will take affect and override the previous one(s).
When working with media queries, always define your base rules first, then write your media queries after.
Testing
When working on responsive designs, developers spend a lot of time testing on real devices.
Without access to a large collection of random devices like this, we still have a few options:
- Resizing the browser window (obviously).
- Browser developer tools. Chrome, Firefox, and Safari have excellent features built into their developer tools for simulating mobile devices. These include both "responsive design mode", and remote debugging directly on devices.
- Xcode's iOS simulator. Xcode > Open Developer Tool > iOS Simulator
- Online tools like BrowserStack
Remote debugging on iOS can be enabled on your device through iOS Settings > Safari > Advanced > Web Inspector. Here are instructions for Android setup.