Welcome to our free Advanced CSS tutorial. This tutorial is based on Webucator's Advanced CSS Training course.
CSS3 media queries allow us to tailor the style of our pages to visitors' media features, offering specific views for width, height, color or other aspects of users' device or browser.
Lesson Goals
With the advent of mobile devices - smartphones like Android or iPhone devices, or tablets like the iPad - responsive design has become a key feature of front-end web development: crafting our sites to present the same content differently depending on some aspect or feature of our visitors' viewing device. We might, for instance, show desktop users a sidebar/main-column page, with a sidebar column 1/3 of the total width on the left and a main column 2/3 of the total width on the right. Phone users might see the same content, but with each column spanning the full width of the page (which is, of course, small on a phone) but stacked with the main content on top of the sidebar content.
Media queries are the CSS tool that give us that power - the ability to apply style rules selectively, based on some feature of the user's device or browser. If a media query returns true - that is, if the maximum or minimum width, height, device width, or other feature we are querying falls within our stated value - then all of the CSS rules we include in that query will apply.
Before we look at some examples, let's check out some of the features we can target with media queries. In CSS code, all media queries take the following form:
@media screen and (max-width: 700px) { footer { display: none; } }
The line @media screen and (max-width: 700px) is the media query: here, we are saying "browser, please apply the following CSS only when the width of the rendering surface is a maximum of 700 pixels." (The "screen" keyword means that we are applying this rule only for the "screen" media type, as opposed to "print", "braille", or some other media type.) Inside our query can be any valid CSS code, possibly lots of CSS code. In the simple example above, we have only one CSS rule: do not display any footer element. Thus our media query would hide any footer element when the width of the browser is 700px or less.
The table below lists the more important media features that we can target:
Feature | Possible Values | Min/Max? | Explanation |
---|---|---|---|
color | int | yes | bits per color component |
color-index | int | yes | number of entries in color lookup table |
device-aspect-ratio | int/int | yes | aspect ratio |
device-height | length (pixels) | yes | height of the output device |
device-width | length (pixels) | yes | width of the output device |
grid | int | no | true for a grid-based device |
height | length (pixels) | yes | height of the rendering surface |
monochrome | int | yes | bits per pixel in a monochrome frame buffer |
resolution | "dpi" or "dpcm" | yes | resolution |
scan | "progressive" or "interlaced" | no | scanning process of "tv" media types |
width | length (pixels) | yes | width of the rendering surface |
Most often we will use width and device-width. The width property targets the current width of the viewing pane (so that a desktop browser dragged to a narrower width would trigger a query for max-width: 700px, if the browser were changed to be less wide than 700 pixels). The device-width property is useful if we want to target the device itself - that is, a device whose viewing pane is inherently less than 700 pixels wide.
While not CSS (but, rather, HTML), the viewport meta tag is almost always used when implementing responsive design with CSS3 media queries:
<meta name="viewport" content="width=device-width">
The viewport tag allows us to scale the width of our pages to match the screen width of the device. We can also set the initial scale of the page - that is, how zoomed-in or -out the page appears, with the initial-scale attribute:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
The initial-scale=1.0 value indicates that we want the page to be scaled to 100% when loaded; we could, of course, change this to 1.5 (for scaling of 150%) or some other value.
The viewport meta tag should be placed in the head of the page.
Let's look at a simple example. Open ClassFiles/CSS101MediaQueries/Demos/index.html in a browser to check it out.
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>Media Queries Example</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> div#main { width: 70%; margin: 0 auto; } div#item1 { width: 48%; float: left; background-color: #f00; } div#item2 { width: 48%; float: right; background-color: #00f; } p { color: #fff; padding: 1em 0; text-align: center; font-size: 2em; } @media screen and (max-width: 900px) { div#item1 { float: right; background-color: #f88; } div#item2 { float: left; background-color: #88f; } p { font-size:1rem; } } </style> </head> <body> <div id="main"> <div id="item1"> <p>I am item <em>one</em></p> </div> <div id="item2"> <p>I am item <em>two</em></p> </div> </div> </body> </html>
The HTML markup for our page is simple: a div
(with id
main
) wraps two div
s with id
s item1
and item2
respectively. The CSS code on our page styles those two div
s to present item 1 as a red-background rectangle on the left (via the CSS code float: left
) and item 2 as a blue-background rectangle on the right. We style the paragraph inside of each div
with some padding, color (white), and a fairly-large font size.
Here's the page with a browser width of about 1200 pixels:
Our media query targets a max-width
of 900 pixels; that is, the two CSS rules contained within our media query will apply for only viewing panes less wide than 900 pixels. In this case, that means that, when the browser is narrower than 900 pixels, the original rules for the two div
elements are overwritten by the rules inside the media query: the float
is swapped (item 2 is floated left, and item 1 is floated right), and the background colors are changed to a more muted red and blue:
Media queries really come in handy when we are styling our pages to present content appropriately for different devices. By targeting the browser width, we can style content to look appropriate for a really wide desktop browser, a medium-sized tablet browser, or a really-small phone browser.
To view our next example, open up ClassFiles/CSS101MediaQueries/Demos/twobreakpoints.html in a browser.
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>Media Queries Example</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> body { background-color: #ccc; } #main { background-color: #fff; width: 80%; margin: 0 auto; padding: 2em; } article { float: right; width: 64.6666666%; padding: 1%; background-color: #ffaaaa; } aside { float: left; width: 31.3333333%; padding: 1%; background-color: #ffaaff; } footer { clear: both; } @media screen and (max-width: 700px) { article { float: none; width: 98%; padding: 1%; background-color: #ffaaaa; } aside { float: none; width: 98%; padding: 1%; background-color: #ffaaff; } footer { display: none; } } </style> </head> <body> <div id="main"> <header> <h1>Media Queries</h1> </header> <article> <h2>Main Content</h2> <p>This is main content - it shows on right on desktops, on top on phones</p> <p>This is main content - it shows on right on desktops, on top on phones</p> <p>This is main content - it shows on right on desktops, on top on phones</p> <p>This is main content - it shows on right on desktops, on top on phones</p> <p>This is main content - it shows on right on desktops, on top on phones</p> </article> <aside> <h2>Sidebar Content</h2> <p>This is sidebar content - it shows on left on desktops, on bottom on phones</p> <p>This is sidebar content - it shows on left on desktops, on bottom on phones</p> <p>This is sidebar content - it shows on left on desktops, on bottom on phones</p> </aside> <footer> <p>This is the footer - it shows only on desktops</p> </footer> </div> </body> </html>
The markup for our page comprises four structural elements:
header
, which displays an h1
title.article
, which displays the main content for the page.aside
, which displays the sidebar content for the page.footer
, which (for desktop visitors) displays at the bottom of the page.We use CSS to style the page by default, before our media query. We give some background color to the body
, the container (#main
) element, and the various structural elements. We float the article
and aside
so that the main content sits right in a large column and the sidebar content sits left in a smaller column.
Here is a screenshot of the page when viewed at a browser width of about 1200 pixels:
Farther down among our CSS code you see the media query: here, we target a maximum width of 700 pixels. For any browser (desktop or mobile device) with a viewing pane of less than 700 pixels, we change things up:
article
element and give it a width of 100% (98% plus 1% of right and left padding).aside
element and give it a width of 100%. This has the effect of stacking the main content and sidebar content when viewed from a smaller (i.e. less than 700-pixels-wide) viewing pane.footer
for smaller screens.Here is a screenshot of the page when viewed at a browser width of about 500 pixels, with the media-query CSS applied:
Note that we also use the viewport
meta tag to scale the page when viewed on a mobile device. If possible, try viewing the page on a mobile device with the viewport
meta tag in place, and then after removing the viewport
tag to see the difference.
Let's have you try out some of the concepts we've presented so far with a brief exercise:
In this exercise, you will use CSS3 media queries to render layouts for a blog homepage for both desktop and mobile views.
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>Media Queries Example</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> body { font-family: Helvetica, 'Helvetica Neue', sans-serif; background-color: #ccc; } #main { background-color: #fff; width: 80%; margin: 0 auto; padding: 2em; } h2 { margin: 0 0 10px 0; } section { float: right; width: 78%; padding: 0 1%; } article { float: left; width: 44%; padding: 2%; background-color: #ffff99; margin: 0 1% 1% 0; } article:nth-child(even) { clear: left; } aside { float: left; width: 18%; padding: 1%; background-color: #ddd; } footer { clear: both; } @media screen and (max-width: 700px) { section { float: none; width: 100%; padding: 0; } article { float: none; width: 96%; padding: 2%; background-color: #ffff99; margin: 1% 0; } aside { margin-top: 20px; float: none; width: 96%; padding: 2%; background-color: #ddd; } } </style> </head> <body> <div id="main"> <header> <h1>My Blog</h1> </header> <section> <h2>Recent Articles</h2> <article> <h3>Headline 1</h3> <p>Ea delenit ut iaceo quidem. Duis transverbero blandit tation te minim commoveo t.</p> </article> <article> <h3>Headline 2</h3> <p>Hendrerit secundum minim incassum valde vicis autem te. Comis capto feugiat wisi.</p> </article> <article> <h3>Headline 3</h3> <p>Te iriure in indoles blandit et epulae erat mos delenit nisl. Wisi aliquip susci.</p> </article> <article> <h3>Headline 4</h3> <p>Ea delenit ut iaceo quidem. Duis transverbero blandit tation te minim commoveo e.</p> </article> </section> <aside> <h2>Subscribe</h2> <p>Sign up now to receive updates as we post new content:</p> <input type="email" name="join" placeholder="email"> </aside> <footer> <p>Best. Blog. Ever.</p> </footer> </div> </body> </html>
For the desktop (wide) view, we float the section
right and the aside
left, and float each individual blog post (the article
s) left with an effective (width
+ padding
+ margin
) width of 50%. We add a font-family
rule and some background colors, and use article:nth-child(even)
to ensure that article
elements of different height don't throw off the tiled appearance of our blog items.
For the mobile (narrow) view, we use a media query, targeting a maximum-width
of 700 pixels. We un-float the elements, both the aside
/section
columns and the tiled blog posts (the article
s) inside the main column. This produces the layout we want: stacked content spanning the full width of the page, for easier viewing on smaller screen.
We can use media queries to target more than one feature, or more than one value of a given feature. Responsive frameworks (like Bootstrap, for instance, the popular HTML/CSS/JavaScript "mobile first" framework) often render sites with three or four different width breakpoints: a view for very wide desktop screens, a view for less-wide desktop screens, a tablet view, and a phone view. The next example extends the previous example to demonstrate this concept; open ClassFiles/CSS101MediaQueries/Demos/threebreakpoints.html in a browser to view the page.
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>Media Queries Example</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> body { background-color: #ccc; } #main { background-color: #fff; width: 80%; margin: 0 auto; padding: 2em; } section { float: right; width: 68%; padding: 1%; background-color: #ffaaaa; } article { float: left; width: 71%; padding: 0 2%; } aside#calltoaction { float: right; width: 23%; padding: 1%; background-color: #ffffaa; } aside#sidebar { float: left; width: 28%; padding: 1%; background-color: #ffaaff; } footer { padding: 20px 0; clear: both; } @media screen and (max-width: 960px) { article { float: none; width: 98%; padding: 1%; } aside#calltoaction { float: none; width: 98%; padding: 1%; } } @media screen and (max-width: 600px) { section { float: none; width: 98%; padding: 1%; background-color: #ffaaaa; } aside#calltoaction { float: none; width: 98%; padding: 1%; background-color: #ffffaa; } aside#sidebar { float: none; width: 98%; padding: 1%; background-color: #ffaaff; } footer { display: none; } } </style> </head> <body> <div id="main"> <header> <h1>Media Queries</h1> </header> <section> <article> <h2>Main Content</h2> <p>This is main content - it shows on right on desktops, on bottom on phones This is main content - it shows on right on desktops, on bottom on phones This is main content - it shows on right on desktops, on bottom on phones This is main content - it shows on right on desktops, on bottom on phones This is main content - it shows on right on desktops, on bottom on phones</p> </article> <aside id="calltoaction"> <h2>Contact Us</h2> <p>Email and phone to go here</p> </aside> </section> <aside id="sidebar"> <h2>Sidebar Content</h2> <p>This is sidebar content - it shows on left on desktops, on bottom on phones</p> </aside> <footer> <p>This is the footer - it shows only on desktops</p> </footer> </div> </body> </html>
We've added two new structural elements to our page since the first example: a new aside
(with id
calltoaction
, the idea here being that we display a contact email and phone) and a section
which wraps the article
(main content) and the new aside
element.
We added a new media query so that we now target three different viewing-pane widths:
For large screens (wider than 960 pixels), we display a three-column layout: the sidebar content sits at left, the main content sits at right (in a wider column), and the call-to-action content floats to the right of the main content. Here's how the page looks:
For medium screens (wider than 600 pixels but narrower than 960 pixels), we display a two-column layout: "sidebar" sits left and "main" sits right, but "call to action" falls below the main content; this happens because we "un-floated" the #calltoaction
element in the middle (@media screen and (max-width: 960px)
media query. Here's a screenshot of the medium-sized layout:
For small screens (a maximum width of 600 pixels), we display the content stacked: the main content is at top, call-to-action content below it, and sidebar content at bottom. As in the earlier example, we hide (display: none
) the footer for smaller screens. And here's the small-layout version of the page:
As you can imagine, the presentation of our sites can get arbitrarily complex. You might target media features other than width - aspect ratio, for example, presenting content differently for a phone rotated 90 degrees on its side. Or you might target a wider range of width breakpoints, styling your pages to present different page layouts for four, five, or more different width ranges.
As you have seen, our responsive-design work with media queries often relies on percentage-based widths. As such, images can present a problem: as a screen narrows, an image - which has its own inherent width - can "break" our designs, poking out of its container div
(or other) element. A handy solution to this problem is the CSS rule max-width: 100%
. Here's a quick example:
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>Max Width</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> div#main { width: 70%; margin: 0 auto; } div#item { padding: 5%; margin: 0 auto; width: 50%; background-color: #f00; } img { max-width: 100%; } </style> </head> <body> <div id="main"> <div id="item"> <img src="building.jpg" alt="building" /> </div> </div> </body> </html>
Our simple page contains a div
with red background, which in turn contains an image. The image's size makes it too wide to fit into the red div
; without the max-width: 100%
CSS rule, the page would look like this:
When we include the max-width: 100%
rule, the image now scales to not exceed the width of its parent container element:
In the next exercise, you'll add another (a third) view to the blog on which you worked earlier, so that the blog displays differently for large, medium-sized, and small screens, and also implement the max-width: 100%
rule.
In this exercise, you will use CSS3 media queries to render a third layout for the blog from your earlier work.
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>Media Queries Example</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> body { font-family: Helvetica, 'Helvetica Neue', sans-serif; background-color: #ccc; } img { max-width: 100%; } #main { background-color: #fff; width: 80%; margin: 0 auto; padding: 2em; } h2 { font-size: 24px; margin: 0 0 10px 0; padding: 0; } section h2 { height: 30px; } section { float: right; width: 78%; padding: 0 1%; position: relative; } article.ordinary { float: left; width: 24%; padding: 2%; background-color: #ffff99; margin: 0 1% 1% 0; clear: right; } article.featured { position: absolute; top: 40px; right: 0; width: 38%; padding: 2% 2% 10% 2%; background-color: #ffff00; } article.featured img { display: none; } article.odd { clear: left; } aside { float: left; width: 18%; padding: 1%; background-color: #ddd; } footer { clear: both; } @media screen and (max-width: 1000px) { article.ordinary { width: 52%; } article.featured { width: 39%; } } @media screen and (max-width: 600px) { section { float: none; width: 100%; padding: 0; } article.featured { position: static; } article.featured img { display: block; } article.ordinary, article.featured { float: none; width: 96%; padding: 2%; margin: 1% 0; } aside { margin-top: 20px; float: none; width: 96%; padding: 2%; background-color: #ddd; } } </style> </head> <body> <div id="main"> <header> <h1>My Blog</h1> </header> <section> <h2>Recent Articles</h2> <article class="ordinary odd"> <h3>Headline 1</h3> <img src="building.jpg"> <p>Ea delenit ut iaceo quidem. Duis transverbero blandit tation te minim commoveo t.</p> </article> <article class="ordinary even"> <h3>Headline 2</h3> <img src="building.jpg"> <p>Hendrerit secundum minim incassum valde vicis autem te. Comis capto feugiat wisi.</p> </article> <article class="ordinary odd"> <h3>Headline 3</h3> <img src="building.jpg"> <p>Te iriure in indoles blandit et epulae erat mos delenit nisl. Wisi aliquip susci.</p> </article> <article class="featured"> <h3>Headline 4</h3> <img src="building.jpg"> <p>Ea delenit ut iaceo quidem. Duis transverbero blandit tation te minim commoveo.</p> </article> <article class="ordinary even"> <h3>Headline 5</h3> <img src="building.jpg"> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur sed orci sollicit.</p> </article> </section> <aside> <h2>Subscribe</h2> <p>Sign up now to receive updates as we post new content:</p> <input type="email" name="join" placeholder="email"> </aside> <footer> <p>Best. Blog. Ever.</p> </footer> </div> </body> </html>
For the wide layout, we use absolute positioning to display the "featured" post at right, with a brighter background yellow color. The "ordinary" posts float left with a width small enough to allow them to fit two-to-a-row. We target the odd
class to clear every other post.
For the medium layout, we increase the width of the "ordinary" posts to make sure that only one item fits per row.
For the small layout, we unfloat all of the blog-post items, remove the absolute positioning for the "featured" item, and un-hide the "featured" item's image.