Saturday, April 14, 2012

Outside-the-Box Pizza, Part 3: Mobility & Responsive Web Design

In this series, we’re looking at Outside-the-Box Pizza, a web-mobile-social-cloud demo, from a variety of perspectives that include design, technologies, and techniques. The online demo is at http://outsidetheboxpizza.com. Here in Part 3, we’ll be looking at how the app supports mobile devices, primarily through the technique of Responsive Web Design. We’ve seen the desktop browser experience, but how does Outside the Box Pizza render on smartphones and tablets? We’ll look at four things:
·         Responsive Web Design and CSS3 Media Queries
·         Font Size and Responsive Text
·         Running Full-Screen on the iPad
·         Sticky Footers

The Goal: Broad Reach
We want our web applications to have broad reach, to work on as many devices as possible. Below you can see some examples of Outside the Box Pizza on various devices. It renders acceptably on smartphones (iPhone, Android, Windows Phone 7), tablets (iPad, Windows 8), and desktop browsers (Chrome, Firefox, Safari, Internet Explorer). It also handles orientation changes on smartphones and tablets.
Outside-the-Box Pizza on various Phones and Tablets

Responsive Web Design & CSS3 Media Queries
The key technique to support a wide variety of modern devices well is Responsive Web Design, the brainchild of Ethan Marcotte. His book on the subject is highly recommended. Responsive Web Design is about adapting to the device we find ourself running on and not making assumption in advance. The most articulated aspect of RWD is adaptive layout for different size and orientation devices, but the principal can be extended to other areas. Let’s first look at device dimensions.
We can use the CSS3 media queries feature to have conditional CSS styling that only applies to devices meeting certain criteria. For example, this is how we express conditional styling in our CSS that should apply to smartphones, which generally have a width of 320 to 480 pixels:
/********************
*                   *
*   Mobile Styles   *
*                   *
********************/

@media only screen and (max-width: 480px) {
    header .float-left, header .float-right {
        float: none;
    }

    header .site-title {
        margin: 10px;
        text-align: center;
    }
    
    .selectGroup li 
    {
        display: inline-block; width: 120px 
    }
    
    h1 { font-size: 1.4em; }
    h2 { display: none; }
    
    .logo { font-size: 1.75em }
    .logo i { display: none }
    .text i { display: none }
    .text b { display: none }

    span b { display: none }
    span i { display: none }
    button b { display: none }
    article { font-size: 0.8em }
    article i { display: none }
    article B { display: none }

    ...


In our case the default CSS is for the desktop browser and our conditional CSS adapts to smaller mobile device dimensions, but I should point out that an alternative way to go is to have your default styles target phones and use conditional styling for larger sizes. Today we have pretty widespread support for HTML5 and CSS3 media queries in our mobile browsers, but when that was less common doing what I just described was important so that the default rendering of the site on “dumb phones” would still be acceptable. Supporting dumb phones wasn’t a priority in this application.
None of the styles in these conditional sections are new; that is, all of the style rules have been previously defined in the base CSS. Rather, what we’re doing here is overriding many of our styles for the small device. There are several reasons to override styles for smaller size screens:
1.       Spacing. We may need to change margins and padding to reduce white space.
2.       Sizes. We may need to change widths, or heights.
3.       Type. We may need to change font characteristics such as size or font family.
4.       Hiding. We may need to hide elements for which we don’t have room to show.
5.       Layout. We may choose to arrange elements differently.
You can reduce the amount of work you need to do here by thinking in percentages as RW advocates. The more we use percentages in our CSS styling rather than fixed units, the less need there is for conditional styles.

Layout Flow
We can solve a lot of problems simply by choosing our layout flow wisely. For example, Outside-the-Box Pizza has an orders page in which many choices are listed for pizza shape, sauce, meat toppings, and veggie toppings. These are all expressed as list items, which are styled such that the browser will intelligently wrap as many across as possible. Notice how this renders on various screen sizes:
Orders Page on Various Mobile Devices
Also notice the toppings icons are designed to be easily touched as well as clicked. When a topping is active, we mark the item as checked and use styling to show its bounding circle in green rather than gray (the “circles” are actually rectangular borders with a generous border radius).
Order Topping Items Designed to be Touch-friendly
Here’s the style for the order list items and how the list items are defined:

.topping { font-family: Arial, Helvetica; font-size: 1.0em; color: Black }
.topping input { display: none; vertical-align: middle; margin-right: 4px; }
.topping span { font-family: Arial, Helvetica; font-size: 1.2em; color: Black }
.topping label { display: inline; font-family: Arial, Helvetica; font-size: 1.2em; color: Black; vertical-align: middle; }

.topping:checked { background-color: #C00000 }
.topping:checked+label { background-color: #C00000 }

.topping img { display: inline; vertical-align: middle; margin-right: 4px; height: 48px;
               border: 4px solid Silver;
               -webkit-border-radius: 32px;
               -moz-border-radius: 32px;
               border-radius: 32px;   
}


<div class="selectGroup">
<div class="selectGroupTitle">Meat Toppings</div>
<ul>
<li class="topping"><label for="topPepperoni"><img src="~/Images/topping_pepperoni.png"  alt="image"/><input id="topPepperoni" name="jqdemo" value="value1" type="checkbox"/>pepperoni</label></li>
<li class="topping"><label for="topSausage"><img src="~/Images/topping_sausage.png"  alt="image"/><input id="topSausage" name="jqdemo" value="value1" type="checkbox"/>sausage</label></li>
<li class="topping"><label for="topHam"><img src="~/Images/topping_ham.png"  alt="image"/><input id="topHam" name="jqdemo" value="value1" type="checkbox"/>ham</label></li>
<li class="topping"><label for="topBacon"><img src="~/Images/topping_bacon.png"  alt="image"/><input id="topBacon" name="jqdemo" value="value1" type="checkbox"/>bacon</label></li>
<li class="topping"><label for="topBeef"><img src="~/Images/topping_beef.png"  alt="image"/><input id="topBeef" name="jqdemo" value="value1" type="checkbox"/>beef</label></li>
<li class="topping"><label for="topChicken"><img src="~/Images/topping_chicken.png"  alt="image"/><input id="topChicken" name="jqdemo" value="value1" type="checkbox"/>chicken</label></li>
<li class="topping"><label for="topTurkey"><img src="~/Images/topping_turkey.png"  alt="image"/><input id="topTurkey" name="jqdemo" value="value1" type="checkbox"/>turkey</label></li>
<li class="topping"><label for="topElk"><img src="~/Images/topping_elk.png"  alt="image"/><input id="topElk" name="jqdemo" value="value1" type="checkbox"/>elk</label></li>
<li class="topping"><label for="topSardines"><img src="~/Images/topping_sardine.png"  alt="image"/><input id="topSardines" name="jqdemo" value="value1" type="checkbox"/>sardines</label></li>
<li class="topping"><label for="topEgg"><img src="~/Images/topping_egg.png"  alt="image"/><input id="topEgg" name="jqdemo" value="value1" type="checkbox"/>egg</label></li>
</ul>
</div>

<div class="selectGroup">
<div class="selectGroupTitle">Veggie Toppings</div>
<ul>
<li class="topping selectedTopping"><label for="topCheese"><img src="~/Images/topping_cheese.png"  alt="image"/><input id="topCheese" name="jqdemo" value="value1" type="checkbox" checked="checked"/>cheese</label></li>
<li class="topping"><label for="topTomato"><img src="~/Images/topping_tomato.png"  alt="image"/><input id="topTomato" name="jqdemo" value="value1" type="checkbox"/>tomatoes</label></li>
<li class="topping"><label for="topPineapple"><img src="~/Images/topping_pineapple.png"  alt="image"/><input id="topPineapple" name="jqdemo" value="value1" type="checkbox"/>pineapple</label></li>
<li class="topping"><label for="topOnions"><img src="~/Images/topping_onion.png"  alt="image"/><input id="topOnions" name="jqdemo" value="value1" type="checkbox"/>onions</label></li>
<li class="topping"><label for="topPeppers"><img src="~/Images/topping_pepper.png"  alt="image"/><input id="topPeppers" name="jqdemo" value="value1" type="checkbox"/>peppers</label></li>
<li class="topping"><label for="topSprouts"><img src="~/Images/topping_sprouts.png"  alt="image"/><input id="topSprouts" name="jqdemo" value="value1" type="checkbox"/>sprouts</label></li>
<li class="topping"><label for="topArtichoke"><img src="~/Images/topping_artichoke.png"  alt="image"/><input id="topArtichoke" name="jqdemo" value="value1" type="checkbox"/>artichoke</label></li>
<li class="topping"><label for="topGorgonzola"><img src="~/Images/topping_gorgonzola.png" alt="image" /><input id="topGorgonzola" name="jqdemo" value="value1" type="checkbox"/>gorgonzo</label></li>
<li class="topping"><label for="topBroccoli"><img src="~/Images/topping_broccoli.png" alt="image" /><input id="topBroccoli" name="jqdemo" value="value1" type="checkbox"/>broccoli</label></li>
<li class="topping"><label for="topPotato"><img src="~/Images/topping_potato.png" alt="image" /><input id="topPotato" name="jqdemo" value="value1" type="checkbox"/>m. potato</label></li>
</ul>
</div>

 
Font Size and Responsive Text
The main thing RWD has to say about text is to set font sizes based on “em” units rather than fixed unit sizes like pixels. In typography, an “em” is the width of the capital letter M in a typeface. Since we want to preserve a user’s right to control the default font size in their browser (for accessibility reasons, for example), doing all of our type setting relative to the “em” is the best approach. Thus our type settings for headings, text, button captions, etc. are all based on the em. Again, we may choose to use different proportions in our conditional styles for some devices.
h1 {
    font-size: 1.8em;
}

h2 {
    font-size: 1.5em;
}

h3 {
    font-size: 1.2em;
}

Another thing we can do to apply the philosophy of RWD to text is to responsive text, which has to do with right-sizing the amount of text content we display. For example, the navigation buttons and top-of-page text on a desktop browser look like this:
Desktop Button Captions and Page Text
…whereas on a phone they look like this, with shorter button captions and shorter page text. The button caption shortening prevents the buttons from wrapping to the next line on many phones. The text shortening prevents the text from taking up too much of the screen so that the content below can be seen without scrolling.

Phone Button Captions and Page Text
Implementing responsive text is a simple matter of choosing some text emphasis style (like <b> or <i>) and defining it to be visible for large screens and hidden on small screens. This is how the button captions and page text are defined in the HTML:
        <nav>
            <div class="content-wrapper">
                <button id="buttonSpecialOffers" class="buttonUnselected" #nclick="javasc#ipt:window.location.href = '/';"><b>Special&nbsp;</b>Offers</button>
                <button id="buttonOrderPizza" class="buttonUnselected" #nclick="javasc#ipt:window.location.href = '/Order';">Order<b>&nbsp;Pizza</b></button>
                <button id="buttonTweet" class="buttonUnselected" #nclick="javasc#ipt:window.location.href = '/Share';">Tweet<b>za&nbsp;Pizza</b></button>
                <button id="buttonPizzaSightings" class="buttonUnselected" #nclick="javasc#ipt:window.location.href = '/Cool';">Cool<b>&nbsp;Pizzas</b></button>
            </div>
        </nav>


All it takes to apply this simply technique with finesse is to think through where you use longer and shorter text and ensure both versions communicate your intent well. Here’s another example of responsive page text:

<span class="text">Ordered an unusual pizza for an unusual occasion? Tweet it and you may win a discount<i> on your next order. We regularly award prizes for most unusual pizza, most unusual occasion, and even most unusual customer<i>!<span>


Full Screen on the iPad
When you access a web application on the iPad, this is how the app will look in the Safari Mobile browser by default. While there’s nothing wrong with the web display, we’d really like the app to have more the look of a native app, preferably running full screen without the browser’s tabs or address bar being visible.
Outside the Box Pizza on iPad - Default Appearance
We can achieve this on the iPad by 1) adding the HTML meta element below to our markup and 2) getting the user to add the web site to their home screen.

<meta name="apple-mobile-web-app-capable" content="yes" />
With the above accomplished, the “app” now has an app icon on the home screen and when invoked runs full-screen. The entire experience is now much closer to that of a native app.
Outside the Box Pizza on iPad - Full Screen
Responsive Images
One other area you should be looking at is sizing your images appropriate for your target device. You don’t want to send a large, hi-resolution image to a phone that is incapable of showing it that way: it’s a waste of bandwidth and slows down you app’s loading and rendering time.
We haven’t put responsive images into place yet in Outside the Box Pizza, but we will be doing so (and will update this post when that happens).

Sticky Footers
A sticky footer will smartly snap to the bottom of the display, which looks a lot more app-like than simply appearing at the end of the content. Sticky footers are best done in CSS (rather than JavaScript): they flow more smoothly and are less-taxing. A well-done sticky footer will also detect and handle long scrolling pages and in that case simply put the footer at the end of the content. Here’s a technique for a sticky footer in CSS you can use (source: http://ryanfait.com/sticky-footer/).
Here’s the relevant CSS for Outside-the-Box Pizza: 
html {
    margin: 0;
    padding: 0;
}

html, body 
{
    height: 100%;
}

#wrap 
{
    min-height: 100%;
    height: auto !important;
    height: 100%;
    margin: 0 auto -3.0em; /* Set footer height. */
}
footer, .push {
    height: 3.0em; /* Set footer height. */
}


Here’s what the rules do:
·         The first rule performs a reset of margin and padding for everything in the document.
·         The second rule sets the height to 100% for the html and body elements.
·         The #wrap rule defines a wrapper style, which will enclose everything in the body except the footer.
·         The .footer / .push rule makes space for the footer.
Notice the height in the #wrap rule is -1 x the height specified in the footer / .push rule. You can use any height you wish, but they must match.
In the HTML markup, we enclose the entire contents of your body in a wrapper div, except the footer – wrap that in a div with the footer class. The wrapper div should include a div with class push at the bottom which is empty. That’s all there is to it.
<div id="wrap">
        <header>
            <div id="banner" class="content-wrapper">
                <div id="logo" class="logo">
                    <div>outside&nbsp;the&nbsp;box&nbsp;<b>pizza</b>&nbsp;<img src="http://outsidetheboxpizza.blob.core.windows.net/images/icon.png" alt="icon" /></div>
<div><i>pizza&nbsp;as&nbsp;individual&nbsp;as&nbsp;you&nbsp;are.</i></div>
                </div>
            </div>
        </header>

        <nav>
            <div class="content-wrapper">
                <button id="buttonSpecialOffers" class="buttonUnselected" onclick="javascript:window.location.href = '/';"><b>Special&nbsp;</b>Offers</button>
                <button id="buttonOrderPizza" class="buttonUnselected" onclick="javascript:window.location.href = '/Order';">Order<b>&nbsp;Pizza</b></button>
                <button id="buttonTweet" class="buttonUnselected" onclick="javascript:window.location.href = '/Share';">Tweet<b>za&nbsp;Pizza</b></button>
                <button id="buttonPizzaSightings" class="buttonUnselected" onclick="javascript:window.location.href = '/Cool';">Cool<b>&nbsp;Pizzas</b></button>
            </div>
        </nav>

        <div id="body">
            <section class="content-wrapper main-content clear-fix">
                @RenderBody()
            </section>
        </div>

        <div class="push"><!--Sticky Footer Push--></div>
    </div>

    <footer>
        <div class="content-wrapper" style="line-height: 3.0em">
            <div class="logo">
            <span style="float: left; color: White; font-size: 0.65em; margin-right: 8px;"><a style="color: White; font-size: 0.75em" href="/Activity" >Sales</a></span>
            <span style="float: left; color: White; font-size: 0.65em; margin-right: 8px;"><a id="StoreLink" style="color: White; font-size: 0.75em" href="/Store/Orders/@ViewBag.StoreId">Store</a></span>
            <span style="float: left; color: White; font-size: 0.65em; margin-right: 8px;"><a id="DeliveryLink" style="color: White; font-size: 0.75em" href="/Store/Driver/@ViewBag.StoreId">Driver</a></span>
            <span style="float: right; color: White; font-size: 0.65em; margin-right: 10px"><a style="color: White; text-decoration: none; font-size: 0.75em" href="/Home/About">About</a></span>
        </div>
        </div>
    </footer>


Summary
Outside-the-Box Pizza leverages responsive web design to adapt its layout to many kinds and sizes of device in order to extend its reach. It renders well on desktop browsers, tablets, and smartphones. Beyond layout, the principle of responsive web design is also seen in the use of responsive text and, in the future, responsive images. Using sticky footers makes the web application seem more like a native app, as does going full-screen if the platform/browser provides a means of achieving that.


Next: Part 4: Social Integration with Twitter

No comments: