Media Queries
Demo starter files
Media queries allow us to conditionally apply different CSS rules, or even entirely separate style sheets, based on certain conditions.
A media query basically says "Apply these styles if..."
Here's a very basic example.
Viewport Setup
First thing's first. Mobile browsers use zooming to display web sites by default. It's the safest assumption the browser can make, since not all sites are adequately designed for display on tiny screens. Zooming out is crude but it makes non-mobile web designs navigable.
In order to prep a site for mobile styling, we need to add a custom meta tag to 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 not to scale our page down and make everything tiny, because we're properly designing for the screen size. initial-scale=1
tells the browser not to do any zooming when the device's orientation changes between portrait and landscape.
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.
Syntax
Media queries can be written as a section in a style sheet, or directly in HTML to load a separate style sheet altogether. Most commonly they're written within the same style sheet, like this:
@media screen and (min-width: 900px) {
/* media-specific rules, usually indented one level */
}
Although less common, they can also be written in HTML, like this:
<link rel="stylesheet" media="screen and (min-width: 900px)" href="styles/wide-displays.css">
A media query can have up to 3 parts: media types, media features, and logical operators.
Media Types
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
, but most have been removed from the spec.
Why? Well, the market for internet connected devices moves very fast. Phones have gotten bigger. Tablets are as fast and capable as laptops. TVs not only have keyboards for input, but voice and motion capture as well. The lines between different categories of devices are too blurry to make these queries useful.
With that said, there are only three media types you should know about.
Name | Summary |
---|---|
screen |
Intended for computer screens of any kind |
print |
Intended for printing and paged media |
all |
Suitable for all media types |
The print
and screen
types are distinct categories, so they haven't been removed (yet). If you skip the media type in your query, and just test for features, the media type is usually assumed to be all
.
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 |
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.
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.
Overlapping vs. Stacking
Overlapping queries:
- Pro: Styles cascade beautifully creating a nice clear structure of inheritance.
- Con: Additional lines are needed to cancel out inherited settings (ex. float on, float off).
- Con: Changing a base style affects all queries, often meaning all queries need to be updated.
Stacking queries:
- Pro: Modular approach to targeting different screen sizes. Less "trickle-down" affect for updating base styles, etc.
- Con: Designing for each query as a separate layout can be at least as complicated.
- Con: See that little gap between queries? Sometimes that's not a gap but an overlap, and either way it's a pain to deal with. It's hard to make media queries that are completely mutually exclusive. And what happens if you're smart and use em breakpoints instead of pixels? At what decimal does rounding occur?
Our demo uses overlapping queries, but there are compelling arguments for both approaches as style sheets become larger and more advanced.
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 rule appears twice - or two different rules that target the same element - the latest rule will take affect and override the previous one(s). This means that regardless of which approach you use, your style sheet should always start with base rules, and get into more specific rules and conditions (i.e. media queries) as you go.
Finally, despite the way the figures above may make it look, media queries must be listed one after another in the style sheet. They cannot be nested in CSS the way that HTML tags are.
Good:
@media (min-width: 600px) {
}
@media (min-width: 900px) {
}
Bad:
@media (min-width: 600px) {
@media (min-width: 900px) {
}
}
Target For Design, Not Screen Size
Building a web site 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 design for a few target display sizes (for example, "small", "medium", and "large"). The exact breakpoints between these however, will depend on the design. You'll need to use a process of testing and playing (especially on a desktop where you can resize the window) to see where logical breakpoints exist in your design.
Unless you're designing primarily for a desktop experience, it's recommended to write your base styles to be mobile first, and work up from there.
Testing
This demo is meant to be an introduction to building basic media queries. In reality, media queries become a large, complex, and integral part of designing a production web site.
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.
Wrapping Up
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 when you come back six months later makes everybody happy!