facebook google plus twitter
Webucator's Free Advanced CSS Tutorial

Lesson: Media Queries

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

  • Understand what media queries are.
  • Understand how can we use media queries to modify our sites for various device and browser widths or other properties.

Media Queries

What Are Media Queries?

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:

Media Query Features
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.

The Viewport Meta Tag

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.

A First Example

Let's look at a simple example. Open ClassFiles/CSS101MediaQueries/Demos/index.html in a browser to check it out.

Code Sample:

CSS101MediaQueries/Demos/index.html
<!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>

Code Explanation

The HTML markup for our page is simple: a div (with id main) wraps two divs with ids item1 and item2 respectively. The CSS code on our page styles those two divs 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:

wide

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:

narrow

Responsive Design

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.

Code Sample:

CSS101MediaQueries/Demos/twobreakpoints.html
<!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>

Code Explanation

The markup for our page comprises four structural elements:

  • A header, which displays an h1 title.
  • An article, which displays the main content for the page.
  • An aside, which displays the sidebar content for the page.
  • A 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:

wide

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:

  • We unfloat the article element and give it a width of 100% (98% plus 1% of right and left padding).
  • Similarly, we unfloat the 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.
  • Lastly, we hide the 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:

wide

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:

Responsive Design for a Simple Blog

Duration: 10 to 20 minutes.

In this exercise, you will use CSS3 media queries to render layouts for a blog homepage for both desktop and mobile views.

  1. Open ClassFiles/CSS101MediaQueries/Exercises/two-col.html in a browser and in a code editor.
  2. The HTML markup is provided for you, with the following elements:
    • A header to display the page title.
    • A section, which contains an h2 title ("Recent Articles") and a series of blog posts, each of which is wrapped in an article tag.
    • An aside, which presents the user with a subscribe-by-email form.
    • A footer.
  3. Write CSS code to render the page as shown for desktop views, at browser widths greater than 700 pixels (or whatever value you deem appropriate): wide
  4. Write CSS code - including, of course, a media query - to render the page as shown for browser widths less wide than the width threshold you choose: narrow

Solution:

CSS101MediaQueries/Solutions/two-col.html
<!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>

Code Explanation

For the desktop (wide) view, we float the section right and the aside left, and float each individual blog post (the articles) 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 articles) 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.

Responsive Design with Multiple Breakpoints

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:

  • By default, our "regular" CSS code works for any browser greater than 960 pixels.
  • A new media query targets browsers with a maximum width of 960 pixels. Since there is another media query farther down in our code, this media query will apply for browser widths between 600 pixels and 960 pixels.
  • Our "small screen" media query we changed to work for widths of less than 600 pixels.

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:

wide

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:

medium

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:

small

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.

Image Widths

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:

bad

When we include the max-width: 100% rule, the image now scales to not exceed the width of its parent container element:

good

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.

Responsive Design for a Simple Blog (Three Views)

Duration: 15 to 30 minutes.

In this exercise, you will use CSS3 media queries to render a third layout for the blog from your earlier work.

  1. Open ClassFiles/CSS101MediaQueries/Exercises/three-col.html in a browser and in a code editor.
  2. The HTML markup is again provided for you, and is the same as in the previous exercise except for classes on the blog articles: all blog posts have a class of ordinary but for one featured post, with class featured.
  3. The ordinary blog post items have alternating classes of odd and even.
  4. Write CSS code to render the page as shown for large desktop views, at browser widths greater than 1000 pixels (or whatever value you deem appropriate): wide The "featured" post displays as a right-most column; use absolute positioning for this item. The other posts tile, two-across, in the large center column. The image (present for each post item) should not display for the "featured" post; be sure to use max-width: 100% to ensure that the image doesn't break the design.
  5. Use a media query to render a layout for browser widths between 600 pixels and 1000 pixels, as shown: medium This layout is similar to the wider layout, but the "ordinary" posts stack vertically, one on top of the other.
  6. Finally, create a layout - with another media query - for widths below 600 pixels, with all of the blog-post items ("featured" included) stack vertically; the "featured" item should now appear in whatever order it falls among the posts, and its image should display: small

Solution:

CSS101MediaQueries/Solutions/three-col.html
<!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>

Code Explanation

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.