Showing posts with label CSS3. Show all posts
Showing posts with label CSS3. Show all posts

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

Tuesday, April 10, 2012

CSS3 Animation and Transitions Tutorial: Soccer Ball Animation

In this post we’ll show you how we created a soccer ball animation using CSS3 animation and transitions. We’ll do this in 6 steps. You can see each step as well as the final result here (you’ll need a screen width of 1650 pixels or more).

The soccer ball shoots up into the air from the left side of the field and goes back down to the right side of the field. This goes back and forth for a total of 3 animations. There is also a transition you can trigger once the animation completes: hovering over the ball hurls it up into the face of a spectator in the foreground at bottom right.


Here are the steps:

1. Setting up a Foundation with Modernizr
2. Animating Size
3. Animating Position
4. Animation Rotation
5. Supporting Multiple Browsers
6. Adding a Transition


Step 1: Setting up a Foundation with Modernizr

In Step 1 we start with a basic page that has a div with an image for the soccer field (‘field’) and another with an image for the ball (‘ball’).

DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"  class="no-js">
<head>
...
<head>
<body>

<div id="notsupported">Sorry, Your Browser DOES NOT Support CSS Animations and Transitionsdiv>
<div id="supported">Yay, Your Browser DOES Support CSS Animations and Transitionsdiv>

<div class="field">
    <img src="background.jpg" alt="field" />
div>

<div class="ball">
    <img src="ball.png" alt="ball" />
div>

<body>
<html>

We want to use the Modernizr library to test for the availability of the features we need, which in this case is css animations and transitions.  We include it in the area of our page.

<script src="http://ajax.aspnetcdn.com/ajax/modernizr/modernizr-2.0.6-development-only.js" type="text/javascript"<script>

If css animations and transitions are not present, we want to display the following red sorry message and hide the field and ball.


If we do have animations and transitions available, we display a green confirming message, show the field and ball:


We accomplish this with the following CSS styles. This works because Modernizr will set classes on the element with names like cssanimations, no-cssanimations, etc. based on the features it detects.

  <style type="text/css">

    /* Display whether or not CSS Animations and Transitions are supported */
     
    .cssanimations #notsupported {
        display: none;
    }

    .cssanimations #supported {
        display: inline;
        padding: 8px;
        background-color: green;
        color: white;
        font-family: Sans-Serif;
    }
       
    .no-cssanimations #notsupported {
        display: inline;
        padding: 8px;
        background-color: red;
        color: white;
        font-family: Sans-Serif;
    }

    .no-cssanimations #supported {
        display: none;
    }

    .no-cssanimations .field {
        display: none;
    }

    .no-cssanimations .ball {
        display: none;
    }


    .csstransitions #notsupported {
        display: none;
    }

    .csstransitions #supported {
        display: inline;
        padding: 8px;
        background-color: Green;
        color: White;
    }
       
    .no-csstransitions #notsupported {
        display: inline;
        padding: 8px;
        background-color: Red;
        color: White;
    }

    .no-csstransitions #supported {
        display: none;
    }

    .no-csstransitions .field {
        display: none;
    }

    .no-csstransitions .ball {
        display: none;
    }


    /* Image of soccer stadium, field, and crowd */

    .field {
        position: absolute;
        top: 150px;
        left: 0px;
        height: 300px;
        width: 1500px;
        text-align: left;
    }


    /* Ball container */

    .ball
    {
        position: absolute;
        top: 300px;
        left: 700px;
        height: 200px;
        width: 200px;
    }


    /* Ball image */

    .ball img
    {
        max-height: 100%;
        max-width: 100%;
    }

  <style>

When we run this in common browsers, we get the red not supported message in IE9 and the green confirming message in IE10, Chrome, Firefox, and Safari.


Step 2: Animating Size

Now we want to put some animation in place, starting with the size of the ball. To do CSS animation today, you need to use vendor prefixes since theimplementations are pre-ratification of the standards. That means instead of using style properties like animate: we’ll be using –webkit-animate for web kit (Chrome, Safari), -moz-animate for Firefox, and –ms-animate for IE. For now, we’ll just put in the –webkit prefix and add the others in a later step.

In the CSS, we achieve the animation by defining a keyframes animation (‘throwing’) and then referring the animation in the style rule for ball. Let’s parse the animation reference in the .ball rule first. The line below means, for webkit browsers, apply the animation ‘throwing’ over a duration of 10 seconds, do it three times, and alternate direction every other time.

-webkit-animation: throwing 10s 3 alternate;

The keyframes animation indicates changes at 0%, 50%, and 100% of the animation. Size goes from 5px height and width to 200px at the midpoint and back down to 5px at end. This gives the effect of a ball getting closer and closer at it soars toward us, then falls back again down to the field.

    /* Animation keyframes */

    @-webkit-keyframes throwing {
        0% { height: 5px; width: 5px; }
        50% { height: 200px; width: 200px; }
        100% { height: 5px; width: 5px; }
    }

Animating size is a good first step, but we’ll need to animate some other characteristics in order to make this effect more convincing.



Step 3: Animating Position

Now that we have the ball size animating, let’s control the start, middle, and end position of the ball to correspond with areas of the background soccer field image. To do this, we simply expand the keyframes animation to also modify the top and left positions.
    /* Animation keyframes */

    @-webkit-keyframes throwing {
        0% { height: 5px; width: 5px; top: 580px; left: 660px }
        50% { height: 200px; width: 200px; top: 50px; left: 800px }
        100% { height: 5px; width: 5pxtop: 400px; left: 875px }
    }

Now our ball grows in size and has a movement path that aligns with the background. It even pops out of its bounding frame, seemingly.


Step 4: Animating Rotation

A final touch on the animation is to have the ball rotate. We can achieve that by also adding a transform that rotates the ball. We’ll rotate to 180 degrees by midpoint and on to 360 by the end.

    /* Animation keyframes */

    @-webkit-keyframes throwing {
        0% { height: 5px; width: 5px; top: 580px; left: 660px }
        50% { -webkit-transform: rotate(180deg); height: 200px; width: 200px; top: 50px; left: 800px }
        100% { -webkit-transform: rotate(360deg); height: 5px; width: 5pxtop: 400px; left: 875px }
    }



Step 5: Supporting Multiple Browsers

Up till now we’ve just been putting the –webkit-xxx animation keywords, which gets us working in Chrome and Safari. Now we want to support Firefox and IE as well. We’ll do that by adding –moz-xxx and –ms-xxx keywords. Here’s our updated style.

  <style type="text/css">

    ...

    /* Ball container */

    .ball
    {
        position: absolute;
        top: 400px;
        left: 875px;
        height: 5px;
        width: 5px;
   
        -webkit-animation: throwing 10s 3 alternate;
        -moz-animation: throwing 10s 3 alternate;
        -ms-animation: throwing 10s 3 alternate;
        animation: throwing 10s 3 alternate;
    }


    /* Ball image */

    .ball img
    {
        max-height: 100%;
        max-width: 100%;
    }


    /* Animation keyframes */

    @-webkit-keyframes throwing {
        0% { height: 5px; width: 5px; top: 580px; left: 660px }
        50% { -webkit-transform: rotate(180deg); height: 200px; width: 200px; top: 50px; left: 800px }
        100% { -webkit-transform: rotate(360deg); height: 5px; width: 5pxtop: 400px; left: 875px }
    }

    @-moz-keyframes throwing {
        0% { height: 5px; width: 5px; top: 580px; left: 660px }
        50% { -moz-transform: rotate(180deg); height: 200px; width: 200px; top: 50px; left: 800px }
        100% { -moz-transform: rotate(360deg); height: 5px; width: 5pxtop: 400px; left: 875px }
    }

    @-ms-keyframes throwing {
        0% { height: 5px; width: 5px; top: 580px; left: 660px }
        50% { -ms-transform: rotate(180deg); height: 200px; width: 200px; top: 50px; left: 800px }
        100% { -ms-transform: rotate(360deg); height: 5px; width: 5pxtop: 400px; left: 875px }
    }

    @keyframes throwing {
        0% { height: 5px; width: 5px; top: 580px; left: 660px }
        50% { transform: rotate(180deg); height: 200px; width: 200px; top: 50px; left: 800px }
        100% { transform: rotate(360deg); height: 5px; width: 5pxtop: 400px; left: 875px }
    }

  <style>

Now we can perform the animation with IE10 and the latest versions of Chrome, Firefox, and Safari. If we wanted to also support Opera or additional browsers that support animation, it’s just a matter of also including their prefixes as well.


Step 6: Adding a Transition

We’ve done animations up till now, which are applied immediately upon definition. Transitions, in contrast, are trigged by some action. We’ll add a transition that hurls the ball into a spectator, and we’ll trigger it when the ball is hovered over.


We define a hover style rule for ball that changes position, size, and rotation:

    /* Hover transition */
   
    .ball:hover
    {
        height: 200px;
        width: 200px
        top: 480px;
        left: 1100px;
   
        -webkit-transform: rotate(360deg);
        /* -moz-transform: rotate(360deg);  <= breaks hover transition in Firefox, presumably a bug */
        -ms-transform: rotate(360deg);
        transform: rotate(360deg);
    }

We then reference the transition in the rule for ball, specifying a 10 second duration.

    /* Ball container */

    .ball
    {
        position: absolute;
        top: 400px;
        left: 875px;
        height: 5px;
        width: 5px;
   
        -webkit-animation: throwing 10s 3 alternate;
        -moz-animation: throwing 10s 3 alternate;
        -ms-animation: throwing 10s 3 alternate;
        animation: throwing 10s 3 alternate;
   
        -webkit-transition: all 10s;
        -moz-transition: all 10s;
        -ms-transition: all 10s;
        transition: all 10s;
    }

Here’s the updated style:

  <style type="text/css">

    ...

    /* Image of soccer stadium, field, and crowd */

    .field {
        position: absolute;
        top: 150px;
        left: 0px;
        height: 300px;
        width: 1500px;
        text-align: left;
    }


    /* Ball container */

    .ball
    {
        position: absolute;
        top: 400px;
        left: 875px;
        height: 5px;
        width: 5px;
   
        -webkit-animation: throwing 10s 3 alternate;
        -moz-animation: throwing 10s 3 alternate;
        -ms-animation: throwing 10s 3 alternate;
        animation: throwing 10s 3 alternate;
   
        -webkit-transition: all 10s;
        -moz-transition: all 10s;
        -ms-transition: all 10s;
        transition: all 10s;
    }


    /* Ball image */

    .ball img
    {
        max-height: 100%;
        max-width: 100%;
    }


    /* Animation keyframes */

    @-webkit-keyframes throwing {
        0% { height: 5px; width: 5px; top: 580px; left: 660px }
        50% { -webkit-transform: rotate(180deg); height: 200px; width: 200px; top: 50px; left: 800px }
        100% { -webkit-transform: rotate(360deg); height: 5px; width: 5pxtop: 400px; left: 875px }
    }

    @-moz-keyframes throwing {
        0% { height: 5px; width: 5px; top: 580px; left: 660px }
        50% { -moz-transform: rotate(180deg); height: 200px; width: 200px; top: 50px; left: 800px }
        100% { -moz-transform: rotate(360deg); height: 5px; width: 5pxtop: 400px; left: 875px }
    }

    @-ms-keyframes throwing {
        0% { height: 5px; width: 5px; top: 580px; left: 660px }
        50% { -ms-transform: rotate(180deg); height: 200px; width: 200px; top: 50px; left: 800px }
        100% { -ms-transform: rotate(360deg); height: 5px; width: 5pxtop: 400px; left: 875px }
    }

    @keyframes throwing {
        0% { height: 5px; width: 5px; top: 580px; left: 660px }
        50% { transform: rotate(180deg); height: 200px; width: 200px; top: 50px; left: 800px }
        100% { transform: rotate(360deg); height: 5px; width: 5pxtop: 400px; left: 875px }
    }


    /* Hover transition */
   
    .ball:hover
    {
        height: 200px;
        width: 200px
        top: 480px;
        left: 1100px;
   
        -webkit-transform: rotate(360deg);
        /* -moz-transform: rotate(360deg);  <= breaks hover transition in Firefox, presumably a bug */
        -ms-transform: rotate(360deg);
        transform: rotate(360deg);
    }

  <style>


Summary

CSS animations and transitions are extremely powerful and fairly easy to learn, but their lack of ubiquity requires discipline in checking for feature availability for which Modernizr is invaluable. A good animation or transition needs to be planned out and storyboarded, much like a movie scene, before it is clear what needs to be done to cleanly implement it.

To see the source, visit the online demo, pull up any of the steps, and view source (each is a single page).