Welcome to our free Responsive Web Design Training tutorial. This tutorial is based on Webucator's Responsive Web Design Training course.
In this lesson, we'll look first at the viewport tag, which allows us to scale content for various viewport widths. We will also learn how CSS3 media queries offer fine control over style rules, allowing us to target differing browser widths and device widths to change the manner in which we present content.
Lesson Goals
viewport
tag is.Our Jazz Calendar site is getting better and better - our liquid design and flexible-image handling now makes the site look better on smaller viewports. But the scale of the site, when viewed on a mobile device, leaves a bit to be desired.
We'll use the viewport meta tag to control the initial zoom of the page and, more importantly, the width of the viewport in pixels.
For this demo, you will need to publish your code to a Web server to be able to view on a smartphone device or emulator. Please refer to the setup instructions for more information.
Try viewing the file MediaQueries/Demos/viewport.html on an iPhone or other smartphone device. Without the viewport tag, the size of text and other elements, when viewed on a smartphone, is pretty small:
Open up the file in a code editor to inspect the code. Note the presence of the line
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
in the head of the .html file and notice that it is commented out.
Now remove the HTML comment from that line and view the page again from a smartphone or emulator. With the viewport tag in place, we're now starting to get a version of the page that feels like it's designed for mobile use. Note that the page is scaled nicely to fit the mobile screen:
The initial-scale=1.0 code sets the initial zoom for the page at 100%. width=device-width forces the page to render at the device's width - effectively scaling the page appropriately for our Android, iPhone, or other device.
The presence of the single line
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
in the head of the .html file accomplishes this. The initial-scale=1.0 code sets the initial zoom for the page at 100%. width=device-width forces the page to render at the device's width - effectively scaling the page appropriately for our Android, iPhone, or other device.
We can control more than just the initial scale of the page with the viewport tag:
Option | Use | Values |
---|---|---|
width | width of viewport | 200 - 10000 pixels |
height | height of viewport | 223 - 10000 pixels |
minimum-scale | smallest zoom | 0.0 - 10.0 |
maximum-scale | largest zoom | 0.0 - 10.0 |
user-scalable | can user scale the page? | yes/no |
Our Jazz Calendar site is more and more flexible: we've used a percentage-based layout to scale the site independent of the browser window's size, and we've applied CSS to scale images and other fixed-width elements. But the design still doesn't hold up when the browser window gets really narrow: elements squash together, some text might need to scale or pad differently, etc. And we've not yet addressed the specific needs of users accessing our site on a mobile device - even a scaled-down version probably won't work too well on a phone.
It would be nice not just to scale the site, but to make qualitative changes to the design based on the inherent properties of the device being used to view it, and also to adjust the design at specific browser-window widths. To address this issue, we turn to the media query.
You may already be familiar with CSS media types - a CSS2 specification that offered a way to include different stylesheets based on the media type (screen, print, etc.) being employed. A print-specific stylesheet, for example, might present a page without background images, with a print-friendly font face, and with margins appropriate for printing.
CSS3 extends this concept with the media query - a way to apply CSS rules selectively based on both the type of media and the physical properties of the device (browser, phone) being used to access the page.
Each media query comprises a media type (e.g., "screen") and zero or more logical expressions - a condition evaluating to true or false based on the conditions of particular media features. We can test our user's device for viewport width, device width, orientation ("portrait" or "landscape"), and other 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 |
Consider a revised version of the Jazz Calendar home page: open MediaQueries/Demos/noborder.html and MediaQueries/Demos/css/noborder.css in your file editor. Employing the min-width query, we create a page where:
The key here are the media queries. The following code says "include the background image only if the width of the browser is at least 768 pixels."
@media screen and (min-width: 768px) { #main { width:93.75%; margin:0 auto 5.888888% auto; background: #c0cbd0 url('../images/bg_main.png') repeat-y 62.888888% 0; } }
The screenshot below shows the Jazz Calendar site at a browser width of 800 pixels. Note that the graphic background shows:
The next screenshot shows the Jazz Calendar site at a browser width of 765 pixels. The graphic background is no longer visible:
Let's use media queries to fix a few things on the Pickup Soccer site. The top (white) nav has never worked well once the browser window is resized too small (it slides under the logo when the container runs out of room to float it right). Let's change the top nav as such:
Also, the game listings get a bit difficult to read when the browser gets too narrow. Let's change this to move the titles under the images when the browser gets narrow:
body { font-family:Georgia,serif; background-image:url('../images/bg_grass.jpg'); } a { text-decoration:none; color:#4040AD; } a:hover { text-decoration:underline; } #container { width:85%; margin:0 auto; background: #fff url('../images/bg_soccerballs.png') repeat-y 65.520833% 0; } header { background-color:#d0b462; } header #logo { margin:1.0416666% 0 1.0416666% 3.75%; } nav { padding:1.0416666% 3.75% 1.0416666% 3.75%; float:right; } @media screen and (max-width: 600px) { nav { float:none; border-top:1px solid #333; border-bottom:1px solid #333; } } nav ul { display:inline; } nav ul li { display:inline; } nav ul li a { color:#fff; text-decoration:none; margin-left:20px; } #maincontent { width:61.7708333333%; padding:1.0416666% 0 3.75% 3.75%; float:left; } #maincontent h1 { font-size:36px; margin:5px 0 20px 0; } #maincontent .game { float:left; width:47.892074199%; line-height:14px; font-size:14px; margin:0.8431703204%; } #maincontent .game:nth-of-type(odd) { clear:both; } @media screen and (max-width: 960px) { #maincontent .game { padding-bottom:20px; } } #maincontent .game img { float:left; margin:0 2.86% 0 0; max-width:100%; } @media screen and (max-width: 960px) { #maincontent .game img { float:none; } } @media screen and (max-width: 960px) { #maincontent .game h3 { font-size:20px; margin:3px 0; line-height:22px; } } #maincontent iframe { max-width:100%; } #sidebar { width:26.9791666667%; float:right; padding:3.75%; color:#333; line-height:20px; } #sidebar h3 { font-style:italic; font-size:20px; margin:0 0 5px 0; } #footerinfo { clear: both; background-color:#151570; padding:1.0416666% 0 1.0416666% 3.75%; } #footerinfo p { color:#fff; }
We use media queries to effect these changes. A query targeting a max-width
of 600 pixels unfloats the top nav and adds borders. Another set of queries, targeting a browser width of 960 pixels, unfloats the .game
images and modifies the h3
titles' size.
Also, notice that we added an extra CSS rule to clear every other floated .game
element:
#maincontent .game:nth-of-type(odd) { clear:both; }
Because each game element has a different height, we might find that the single, last item is pushed right - instead of, as we would expect, aligning left as the bottom-most element - because the item above it is too tall. We use nth-of-type(odd)
to clear the floating for each odd element; for CSS nth-of-type
, the first element has index 1.
As the browser narrows, it might make sense to slide the right sidebar under the left main column - a common strategy for shrinking a two-column design into the more narrow confines of a smartphone. As the screenshot below shows, the columns get pretty slim when the browser is narrowed too much, to the point where just a few words show on each line of the right sidebar:
Let's add a few more media queries to handle browser widths of less than 520px - an arbitrary breakpoint, chosen here because it seems to make sense in the context of our page.
The page MediaQueries/Demos/linearize.html now behaves in the same manner as the previous example (graphic border and two-across gig listings disappear at browser-window widths less than 768px), but now the sidebar stops floating right (and thus slides under the main content) at widths less than 520px. Open the page in a browser and change the browser's width to check it out. Be sure to examine the media queries in MediaQueries/Demos/css/linearize.css - specifically the media queries that govern the #maincontent and #sidebar elements at widths less than 520 pixels:
@media screen and (max-width: 520px) { #main #maincontent { float:none; width:92%; } } @media screen and (max-width: 520px) { #main #sidebar { width:92%; float:none; } }
In this exercise, you'll update the Pickup Soccer site again, this time presenting the sidebar under the main column at widths under 520px, as shown:
<!DOCTYPE html> <html> <head> <title>Soccer Pickup</title> <link rel="stylesheet" type="text/css" href="css/reset.css" /> <link rel="stylesheet" type="text/css" href="css/style.css" /> <meta name="viewport" content="initial-scale=1.0, width=device-width" /> </head> <body> <div id="container"> <header> <a href="index.html"><img src="images/logo.png" alt="Soccer Pickup" id="logo" /></a> <nav id="mainnav"> <ul> <li><a href="index.html">Home</a></li> <li><a href="find-a-game.html">Find a Game</a></li> <li><a href="about.html">About</a></li> <li><a href="contact.html">Contact</a></li> </ul> </nav> </header> <div id="maincontent"> <h1>Upcoming Games</h1> <div class="game"> <a href="#"><img src="images/tn/ball.jpg" /></a> <h3><a href="#">Barry Park</a></h3> <p>Sundays, around 2pm; near the back of the largest field</p> </div> <div class="game"> <a href="#"><img src="images/tn/feetball.jpg" /></a> <h3><a href="#">Lakeside Park</a></h3> <p>Sundays, around 2pm; near the back of the largest field</p> </div> <div class="game"> <a href="#"><img src="images/tn/goal.jpg" /></a> <h3><a href="#">Schiller Elementary School</a></h3> <p>Sundays, around 2pm; near the back of the largest field</p> </div> <div class="game"> <a href="#"><img src="images/tn/goalframe.jpg" /></a> <h3><a href="#">Central High</a></h3> <p>Sundays, around 2pm; near the back of the largest field</p> </div> <div class="game"> <a href="#"><img src="images/tn/twolegsball.jpg" /></a> <h3><a href="#">Adams Park</a></h3> <p>Sundays, around 2pm; near the back of the largest field</p> </div> </div> <aside id="sidebar"> <h3>About Us</h3> <p>Soccer Pickup is your source for finding local recreational soccer games for adults. Leagues are great - but sometimes you just want to lace up your boots and find an informal game. Check back often to find a game near you!</p> </aside> <footer id="footerinfo"> <p>Soccer Pickup: Your guide to finding a game.</p> </footer> </div> </body> </html>
body { font-family:Georgia,serif; background-image:url('../images/bg_grass.jpg'); } a { text-decoration:none; color:#4040AD; } a:hover { text-decoration:underline; } #container { width:85%; margin:0 auto; background: #fff url('../images/bg_soccerballs.png') repeat-y 65.520833% 0; } @media screen and (max-width: 520px) { #container { background: #fff; } } header { background-color:#d0b462; } header #logo { margin:1.0416666% 0 1.0416666% 3.75%; } nav { padding:1.0416666% 3.75% 1.0416666% 3.75%; float:right; } @media screen and (max-width: 520px) { nav { float:none; background:#ccc; } } nav ul { display:inline; } nav ul li { display:inline; } nav ul li a { color:#fff; text-decoration:none; margin-left:20px; } @media screen and (max-width: 520px) { nav ul li a { color:#000; } } #maincontent { width:61.7708333333%; padding:1.0416666% 0 3.75% 3.75%; float:left; } @media screen and (max-width: 520px) { #maincontent { width:96.25%; padding:1.0416666% 0 3.75% 3.75%; float:none; } } #maincontent h1 { font-size:36px; margin:5px 0 20px 0; } #maincontent .game { float:left; width:47.892074199%; line-height:14px; font-size:14px; margin:0.8431703204%; } #maincontent .game:nth-of-type(odd) { clear:both; } #maincontent .game img { float:left; margin:0 2.86% 0 0; max-width:100%; } #maincontent iframe { max-width:100%; } #sidebar { width:26.9791666667%; float:right; padding:3.75%; color:#333; line-height:20px; } @media screen and (max-width: 520px) { #sidebar { width:96.25%; padding:3.75% 0 3.75% 3.75%; float:none; clear:left; background:#ccc; } } #sidebar h3 { font-style:italic; font-size:20px; margin:0 0 5px 0; } #footerinfo { clear: both; background-color:#151570; padding:1.0416666% 0 1.0416666% 3.75%; } #footerinfo p { color:#fff; }
We use a media query to unfloat the main nav at widths under 520 pixels:
@media screen and (max-width: 520px) { nav { float:none; background:#ccc; } }Similarly, we use several more media queries to prevent the floating left and right of the main content and sidebar elements, respectively, forcing the sidebar to fall under the main content when the browser resizes to less than 520 pixels.
@media screen and (max-width: 520px) { nav { float:none; background:#ccc; } } @media screen and (max-width: 520px) { #maincontent { width:96.25%; padding:0.96% 0 3.75% 3.75%; float:none; } }
max-device-width
We'll now use media queries to target the properties of the device being used to view our page. The property max-device-width allows us to target the physical properties of the viewport - max-device-width: 480px, for instance, applies not when the browser window is resized to less than 480px, but rather when the device itself - a smartphone, say - has a viewport less than 480px wide.
We have a set of the files used in this class hosted at http://www.webucator.net/MBL101/ as it is helpful to view some of the files used in this class from a remote server, especially to see how they work on a mobile device.
Consider the file MediaQueries/Demos/devicewidth.html. View this latest version of the Jazz Calendar home page on an iPhone, Android, or other smartphone device or on an emulator - you'll see that we've used media queries to selectively apply style rules just for devices with a maximum viewport width less than 480px:
In this exercise, you will update the Pickup Soccer site for mobile devices - we'll use media queries to display the site like this on a phone:
body { font-family:Georgia,serif; background-image:url('../images/bg_grass.jpg'); } a { text-decoration:none; color:#4040AD; } a:hover { text-decoration:underline; } #container { width:85%; margin:0 auto; background: #fff url('../images/bg_soccerballs.png') repeat-y 65.520833% 0; } @media screen and (max-device-width: 480px) { #container { background: #fff; } } header { background-color:#d0b462; } header #logo { margin:1.0416666% 0 1.0416666% 3.75%; } nav { padding:1.0416666% 3.75% 1.0416666% 3.75%; float:right; } @media screen and (max-device-width: 480px) { nav { float:none; background:#ccc; } } nav ul { display:inline; } nav ul li { display:inline; } nav ul li a { color:#fff; text-decoration:none; margin-left:20px; } @media screen and (max-device-width: 480px) { nav ul li a { color:#000; margin-left:10px; } } #maincontent { width:61.7708333333%; padding:1.0416666% 0 3.75% 3.75%; float:left; } @media screen and (max-device-width: 480px) { #maincontent { width:96.25%; padding:1.0416666% 0 3.75% 3.75%; float:none; } } #maincontent h1 { font-size:36px; margin:5px 0 20px 0; } #maincontent .game { float:left; width:47.892074199%; line-height:14px; font-size:14px; margin:0.8431703204%; } #maincontent .game:nth-of-type(odd) { clear:both; } #maincontent .game img { float:left; margin:0 2.86% 0 0; max-width:100%; } #maincontent iframe { max-width:100%; } #sidebar { width:26.9791666667%; float:right; padding:3.75%; color:#333; line-height:20px; } @media screen and (max-device-width: 480px) { #sidebar { width:96.25%; padding:3.75% 0 3.75% 3.75%; float:none; clear:left; background:#ccc; } } #sidebar h3 { font-style:italic; font-size:20px; margin:0 0 5px 0; } #footerinfo { clear: both; background-color:#151570; padding:1.0416666% 0 1.0416666% 3.75%; } #footerinfo p { color:#fff; }
We construct queries with max-device-width: 480px
to target narrow devices, unfloating the nav
:
@media screen and (max-device-width: 480px) { nav { float:none; background:#ccc; } }
#maincontent
:
@media screen and (max-device-width: 480px) { #maincontent { width:96.25%; padding:0.96% 0 3.75% 3.75%; float:none; } }
and #sidebar
elements:
@media screen and (max-device-width: 480px) { #sidebar { width:96.25%; padding:3.75% 0 3.75% 3.75%; float:none; clear:left; background:#ccc; } }