Adaptive Layouts with Media queries

We made a big change to Ottergram when we changed to flexbox. We now have a detail picture, with a nice bit of text overlapping the lower left corner, and a row of thumbnails along the bottom. However, if the screen is wide enough, it might be nice to put the thumbnails along the left. Try it on the following, resizing the window to be both wide (wider than 768 pixels) and narrow:

adaptive Ottergram

We'll learn how to do that using media queries. Pages like this that adapt or respond to the device size are called responsive (though that word is vague and overused and adaptive is probably a better word).

Visual Viewport versus Layout Viewport

Unfortunately, we have some work to do first. When small mobile devices took off and websites started to be designed for them, the first idea was to distinguish between:

  • the layout viewport, which determines how elements are arranged on a canvas, and
  • the visual viewport, which is a little window through which you can look at part of the layout viewport.

Think of this as looking at a newspaper though a keyhole.

The browser lays out the newspaper page but you can only see a bit of it at a time through your mobile device. You can scroll around to see other portions of the page. Or you can zoom out (to enlarge the visual viewport). If the visual viewport is large enough, it might match the dimensions of the layout viewport and you can see the whole newspaper page, but the text is almost certainly too small to read.

These two viewports can be confusing. This article does a reasonably good job explaining the difference, and there's a nice picture:

visual viewport versus layout viewport

You don't have to read all of it; just enough to understand the difference between the two viewports.

Many mobile browsers start with the visual viewport all zoomed out, so you can see the whole page, but can't read anything without zooming in.

Setting the Viewport

Now that we're good at designing for mobile first (keeping things narrow, setting widths using percentages rather than pixels, having images scale), it's better to make the visual viewport the same size as the layout viewport. We can do that with the following incantation, which we put in the head of our document:

<meta name="viewport"
      content="width=device-width, initial-scale=1">

Using Extra Width

We can design for mobile, but if we have a wider device, like a desktop computer, do we just make the page really wide? No, that's ugly, and it turns out people aren't comfortable reading very wide text.

Therefore, we'd like to change the layout when the device gets wider. In Ottergram, we're going to put the thumbnails on the left. We can do that with a media query. (In this case, the word "media" is being used as a synonym for "device".)

You can read more about media queries: beginner's guide and media queries

You can also also have different CSS rules for printouts and other media. For example, I often change the color scheme to black & white for printing, because otherwise red text becomes light gray when it's printed, which means that instead of standing out, it fades away.

The full media query language is big and complex, but we'll just use it in one form:

@media screen and (min-width: 768px) {
    ...
}

The 768px is called a "breakpoint". The rules are applied only if the device is at least 768px wide (typically landscape tablets, desktop computers and such). This page gives some common breakpoints

Example 1

Here is a simple example that modifies the background color and header color based on whether the device is:

  • small (a phone is typically less than 600px wide), greenish background
  • medium (between 600 and 768px wide) yellowish background
  • large (above that) reddish background

Try resizing the window. Here's the complete CSS:

        body {
            background-color: rgb(50%, 100%, 50%);
        }

        h1 {
            color: rgb(100%, 50%, 100%);
        }

        @media screen and (min-width: 600px) {
            body {
                background-color: rgb(100%, 100%, 50%);
            }

            h1 {
                color: rgb(50%, 50%, 100%);
            }
        }

        @media screen and (min-width: 768px) {
            body {
                background-color: rgb(100%, 50%, 50%);
            }

            h1 {
                color: rgb(50%, 100%, 100%);
            }
        }

Some important points:

  • The contents of the media query is a set of CSS rules, so we have curly braces and extra indentation around the whole set.
  • Here, I just had two rules (for body and for h1) but you can have as many as you want
  • Later rules, if applicable, override earlier rules, so these are in increasing order of width.
  • the default is for the narrowest device, because we design for mobile, first.
  • You should almost always use min-width. The default should be for the narrow device, with overriding rules if the device is wide

Developer Tools

Firefox has some cool developer tools to help with designing pages for mobile and testing responsive (adaptive) designs. You can read more at the MDN Responsive Design Mode, which has nice pictures and even a video. It has a menu where you can say "set the dimension for an iphone 6" or other known device.

Other browsers have similar features, but we're using Firefox in CS 204. Right?

Responsive Ottergram

In adaptive Ottergram, we modified the HTML to add the viewport meta tag, but otherwise didn't change the HTML at all.

Next, we kept all the CSS the same (since it works for narrow mobile devices), but we added the following for wider screens:

@media screen and (min-width: 768px) {
  main {
    flex-direction: row;
    overflow: hidden;
  }

  .thumbnail-list {
    flex-direction: column;
    order: 0;
    margin-left: 2em;
  }

  .thumbnail-item {
    max-width: 260px;
  }

  .thumbnail-item + .thumbnail-item {
    margin-top: 2em;
  }
}

We put that at the bottom of the styles.css file. Note the following:

  • the main element is now a row (thumbnails then detail), not a column
  • the thumbnails is now a column, not a row.
  • we used order:0 to put the thumbnails before the detail, instead of after (order:2)

Summary

  • standard term is responsive web pages
  • viewports:
    • layout viewport: what the developer (and the browser) uses for layout (where elements go and how big they are: think "newspaper layout")
    • visual viewport: what the user sees
  • on phones, the visual viewport is narrower than the layout viewport, requiring
    • zooming out
    • scrolling
  • make the visual and layout viewport coincide with this:
<meta name="viewport"
      content="width=device-width, initial-scale=1">
  • media queries using (in CSS) like this:
@media screen and (min-width: 768px) {
    selector {
        flex-direction: row;
    }
    /* other CSS rules */
}
  • setting print styles:
@media print {
    body {
        /* Note "pt" (points) not "px" (pixels).
           72.27pt = 1 inch */
        font-size: 12pt "Times New Roman";
    }
    /* other CSS rules */
}