KEMBAR78
The Inclusive Web: hands-on with HTML5 and jQuery | KEY
The Inclusive Web
                 Hands on with
                HTML5 and jQuery




            Justin Obara & Colin Clark
                Inclusive Design Research Centre
                                  fluidproject.org
A bit about us, quickly
!"#$

Decapod
What is accessibility?
Rethinking Disability
Rethinking Disability
Rethinking Disability

   A mismatch between the
     user
   and the
     user interface
Disability is a usability issue
Disability is contextual
Designing for Context
Disability is environmental
Accessibility is...


      the ability of the system
to accommodate the needs of the user
the web today
jQuery Hits the Spot
jQuery Hits the Spot

• Browser inconsistencies and bugs
jQuery Hits the Spot

• Browser inconsistencies and bugs
• Complexity of the DOM
jQuery Hits the Spot

• Browser inconsistencies and bugs
• Complexity of the DOM
• Handling events and asynchrony
jQuery Hits the Spot

•   Browser inconsistencies and bugs
•   Complexity of the DOM
•   Handling events and asynchrony
•   Communicating with the server
Toolkits can help!
•   Browser Abstraction
•   Complexity of the DOM
•   Handling events and asynchrony
•   Communicating with the server
Toolkits can help!
•   Browser abstraction
•   A simple, unified API for the DOM
•   Handling events and asynchrony
•   Communicating with the server
Toolkits can help!
•   Browser abstraction
•   A simple, unified API for the DOM
•   Easy, functional events system
•   Communicating with the server
Toolkits can help!
•   Browser abstraction
•   A simple, unified API for the DOM
•   Easy, functional events system
•   Built-in AJAX, XML, and JSON
Without jQuery

function stripeListElements() {

   // get the items from the list

   var myItems = document.getElementsByTagName("li");

   // skip line 0 as it's the header row

   for(var i = 0; i < myItems.length; i++) {

   
   if ((i % 2) === 0) {

   
   
     myItems[i].className = "striped";

   
   }

   }
  }
With jQuery


 jQuery("li");
With jQuery


jQuery("li:even");
With jQuery


jQuery("li:even").addClass("striped");
Accessible
systems are...

  • Flexible
  • Separable
  • Modifiable
Graceful Degradation
Markup

<!-- Only shown if browser doesn't support JavaScript -->
<label for="..." class="fl-progEnhance-basic">Add File:</label>

<!-- Only shown if JavaScript is turned on -->
<div class="fl-progEnhance-enhanced">




                                  It’s just a couple of classes!
Styles

.fl-progEnhance-enhanced {display:none}
.fl-progEnhance-basic {}




              Hide the fancy stuff, show the basics by default.
The Code

// Use JavaScript to hide basic markup.
$("head").append("<style type='text/css'>
   .fl-progEnhance-basic{ display: none; }
   .fl-progEnhance-enhanced { display: block; }
</style>");




                      Use JavaScript to flip the styles around!
how assistive technology
         works
keyboard navigation & aria
Opaque Markup
<!-- This is a Tabs widget.               -->
<!-- How would you know, looking only at the markup? -->

<ol>
  <li id="ch1Tab">
     <a href="#ch1Panel">Chapter 1</a>
  </li>
  <li id="ch2Tab">
     <a href="#ch2Panel">Chapter 2</a>
  </li>
  <li id="quizTab">
     <a href="#quizPanel">Quiz</a>
  </li>
</ol>
<div>
  <div id="ch1Panel">Chapter 1 Stuff</div>
  <div id=”ch2Panel”>Chapter 2 Stuff</div>
  <div id=”quizPanel”>Quiz Stuff</div>
</div>
Opaque Markup: Tabs
ARIA fills the gap
Roles, States, Properties

• Roles describe widgets not present in HTML 4
      slider, menubar, tab, dialog

• Properties describe characteristics:
      draggable, hasPopup, required

• States describe what’s happening:
      busy, disabled, selected, hidden
Using ARIA
<!-- Now *these* are Tabs! -->
<ol role=”tablist”>
 <li id=”ch1Tab” role=”tab”>
  <a href="#ch1Panel">Chapter 1</a>
 </li>
 <li id=”ch2Tab” role=”tab”>
  <a href="#ch2Panel">Chapter 2</a>
 </li>
 <li id=”quizTab” role=”tab”>
  <a href="#quizPanel">Quiz</a>
 </li>
</ol>
<div>
 <div id="ch1Panel" role=”tabpanel”
     aria-labelledby=”ch1Tab”>Chapter 1 Stuff</div>
 <div id=”ch2Panel” role=”tabpanel”
     aria-labelledby=”ch2Tab”>Chapter 2 Stuff</div>
 <div id=”quizPanel” role=”tabpanel”
     aria-labelledby=”quizTab”>Quiz Stuff</div>
</div>
Adding ARIA in code
// Identify the container as a list of tabs.
tabContainer.attr("role", "tablist");

// Give each tab the "tab" role.
tabs.attr("role", "tab");

// Give each panel the appropriate role,          panels.attr("role",
"tabpanel");
panels.each(function (idx, panel) {
   var tabForPanel = that.tabs.eq(idx);
   // Relate the panel to the tab that labels it.
   $(panel).attr("aria-labelledby", tabForPanel[0].id);
});
Keyboard Navigation

• Everything that works with the mouse
  should work with the keyboard
• ... but not always in the same way
• Support familiar conventions
   http://dev.aol.com/dhtml_style_guide
Keyboard Conventions
• Tab key focuses the control or widget
• Arrow keys select an item
• Enter or Spacebar activate an item
  Tab is handled by the browser. For the rest,
  you need to write code. A lot of code.
Keyboard navigation: Tabs
Tabindex examples
<!-- Tab container should be focusable -->
<ol id=”animalTabs” tabindex=”0”>
 <!-- Individual Tabs shouldn’t be focusable -->
 <!-- We’ll focus them with JavaScript instead -->
 <li id=”tab1”>
  <a href=”#cats” tabindex=”-1”>Cats</a>
 </li>
 <li id=”tab2”>
  <a href=”#cats” tabindex=”-1”>Dogs</a>
 </li>
 <li id=”tab3”>
  <a href=”#cats” tabindex=”-1”>Alligators</a>
 </li>
</ol>
Making Things Tabbable
  • Tabindex varies subtly across browsers
  • jquery.attr() normalizes it as of 1.3
  • For all the gory details:
     http://fluidproject.org/blog/2008/01/09/
       getting-setting-and-removing-tabindex-values-with-
       javascript/


// Make the tablist accessible with the Tab key.
tabContainer.attr("tabindex", "0");
// And take the anchors out of the Tab order.
$(“a”, tabs).attr("tabindex", "-1");
Adding the Arrow Keys
// Make each tab accessible with the left and right arrow keys.
tabContainer.fluid("selectable", {
   selectableSelector: that.options.selectors.tabs,
   direction: fluid.a11y.orientation.HORIZONTAL,
   onSelect: function (tab) {
      $(tab).addClass(that.options.styles.highlighted);
   },

      onUnselect: function (tab) {
        $(tab).removeClass(that.options.styles.highlighted);
      }
});
Making Them Activatable

// Make each tab activatable with Spacebar and Enter.
tabs.fluid("activatable", function (evt) {
    // Your handler code here. Maybe the same as .click()?
});
Documentation

• Tutorial:
 http://wiki.fluidproject.org/display/fluid/Keyboard+Accessibility
 +Tutorial


• API Reference:
 http://wiki.fluidproject.org/display/fluid/Keyboard+Accessibility
 +Plugin+API
the web tomorrow
“you have to use flash for that”

                  “the web can’t do that!”

       “you need an app for that!”
music and video
games
augmented reality
mobile
Beyond the buzzword...

• Media, drawing, animation, and interactivity
      <audio>, <video>, <canvas>

• New widgets—you don’t have to roll your own
      <progress>, <menu>

• Richer semantics for forms and documents
      <article>, <nav>, <input type=”date”>
Other cool stuff...

• CSS3
      transition, transform, gradient

• Working with files
      File API, FormData, XHR Level 2

• Coming soon
      Device, Text to Speech!
What about accessibility?
Making use of semantics
What’s coming

• Headings
      Based on nesting within sections

• Continued enhancements from semantics
      e.g. improved AT awareness for navigation <nav>

• Native widgets
Canvas Accessibility
Canvas Accessibility
Canvas Accessibility
1. Shadow DOM
2. Focus indicators    ... not quite yet.




 In the meantime...   1. Build alternatives
                      2. Degrade gracefully
The Bottom Line
• HTML5 is coming—experiment with it now
• Lots of great potential for improving access
• Assistive technologies are slow on the uptake
• Some features are going to be a challenge
  (Canvas)
building cool stuff
an HTML5 uploader
Features

     • Degrades gracefully
     • Uploads multiple files at once
     • Keyboard navigable
     • Uses hot new HTML5 features:

FormData   XMLHttpRequest Level 2   <progress> (almost!)
Dive right in: markup


<input type="file" multiple=""
  id="d-uploader-filesControl"
  class="d-uploader-filesControl fl-progEnhance-basic" />
Getting the files


filesControl.change(function () {
    that.events.onAdd.fire(filesControl[0].files);
});
demo.uploader.sendRequest = function (file, url, events) {
  var formData = new FormData();
  formData.append("file", file);

     // Create a new XHR.
     var xhr = new XMLHttpRequest();
     xhr.open("POST", url, true);

     // Register success and error listeners.
     xhr.onreadystatechange = function () {
        if (status === 200) {
            events.onSuccess.fire(file);
        } else {
            events.onError.fire(file);
        }
     };

     // Listen for progress events as the file is uploading.
     xhr.upload.onprogress = function (progressEvent) {
        events.onProgress.fire(file, progressEvent.loaded, progressEvent.total);
     };

     // Send off the request to the server.
     xhr.send(formData);
};
HTML5 Inputs
<input   type=”tel”> <!-- phone number -->
<input   type=”email”> <!-- e-mail address -->
<input   type=”date”> <!-- date -->
<input   type=”search”> <!-- search field -->

<!-- number field -->
<input type=”number” min=”0” max=”10” step=”1” value=”1”>

<!-- Like an autocomplete widget -->
<input list=”dlist”>
<datalist id=”dlist”><option value=”HTML5”></datalist>
HTML5 Inputs:
       attributes/properies
<label for=”name”>Name</label>
<input type=”text” id=”name” placeholder=”My name is ...”
  required autofocus />
Geolocation
// test if geolocation api is supported
if (!!navigator.geolocation) {
    // success callback is passed a location object
    // coords property holds coordinate information
    // Firefox also has an address property
    navigator.geolocation.getCurrentPosition(success, error);
}
Geolocation:
           Location Object
// test if geolocation api is supported
if (!!navigator.geolocation) {
    // success callback is passed a location object
    navigator.geolocation.getCurrentPosition(success, error);
}
What’s Infusion?

• Application framework built on top of jQuery
• UI components you can reuse and adapt
• Lightweight CSS framework for styling
• Accessibility tools and plugins for jQuery
• Open architecture: everything is configurable
Great UX is hard work

• Your code gets unruly as it grows
• UIs are hard to reuse or repurpose
• Design change requires big code change
• Accessibility is confusing
• Combining different code/libraries doesn’t
  always work
Open Architecture:
      Unlock your markup
      Let developers and users in
      A widget isn’t just one thing
      Question the rules




No Black Boxes
Transparent
   Apps
• M is where it’s at
• Events inside and out
• Assistive technology
  inside the Web, not
  bolted on
UI Options & FSS
UI Options & FSS
CSS Frameworks
“If you’re going to use a framework, it
should be yours; one that you’ve created.
You can look at existing frameworks for
ideas and hack at it. But the professionals
in this room are not well served by picking
up a framework and using it as-is.”
                               - Eric Meyer
Fluid Skinning System

• FSS is built to be hacked on
• Provides a core set of building blocks
• Reset, text, layouts, themes
• Namespaced: no conflicts with your stuff
• Themes for better legibility & readability
       http://wiki.fluidproject.org/x/96M7
https://github.com/jobara/workshops
Questions?
Justin Obara
e: jobara@ocad.ca

Colin Clark
e: cclark@ocad.ca
t: @colinbdclark


fluidproject.org
github.com/fluid-project
Photo Credits

Curb cut, Great PA-NJ, http://www.flickr.com/photos/50393252@N02/4822063888/

Stethoscope, Han-Oh Chung, http://www.flickr.com/photos/chickenlump/2038512161/

Texting while walking, Mobile Monday Amsterdam, http://www.flickr.com/photos/momoams/2926622070/

MOMA WiFi, http://www.flickr.com/photos/89554035@N00/2445178036

Plasticine iPhone, Paula Ortiz López, http://www.flickr.com/photos/paulaortizlopez/5342740603/

Skateboarder, Amin Samsudin, http://www.flickr.com/photos/aminchoc/4108543387/

Plasticine Animal, panshipanshi, http://www.flickr.com/photos/panshipanshi/2123208719/

The Inclusive Web: hands-on with HTML5 and jQuery

  • 1.
    The Inclusive Web Hands on with HTML5 and jQuery Justin Obara & Colin Clark Inclusive Design Research Centre fluidproject.org
  • 2.
    A bit aboutus, quickly
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
    Rethinking Disability A mismatch between the user and the user interface
  • 8.
    Disability is ausability issue
  • 9.
  • 10.
  • 11.
  • 12.
    Accessibility is... the ability of the system to accommodate the needs of the user
  • 13.
  • 15.
  • 16.
    jQuery Hits theSpot • Browser inconsistencies and bugs
  • 17.
    jQuery Hits theSpot • Browser inconsistencies and bugs • Complexity of the DOM
  • 18.
    jQuery Hits theSpot • Browser inconsistencies and bugs • Complexity of the DOM • Handling events and asynchrony
  • 19.
    jQuery Hits theSpot • Browser inconsistencies and bugs • Complexity of the DOM • Handling events and asynchrony • Communicating with the server
  • 20.
    Toolkits can help! • Browser Abstraction • Complexity of the DOM • Handling events and asynchrony • Communicating with the server
  • 21.
    Toolkits can help! • Browser abstraction • A simple, unified API for the DOM • Handling events and asynchrony • Communicating with the server
  • 22.
    Toolkits can help! • Browser abstraction • A simple, unified API for the DOM • Easy, functional events system • Communicating with the server
  • 23.
    Toolkits can help! • Browser abstraction • A simple, unified API for the DOM • Easy, functional events system • Built-in AJAX, XML, and JSON
  • 24.
    Without jQuery function stripeListElements(){ // get the items from the list var myItems = document.getElementsByTagName("li"); // skip line 0 as it's the header row for(var i = 0; i < myItems.length; i++) { if ((i % 2) === 0) { myItems[i].className = "striped"; } } }
  • 25.
  • 26.
  • 27.
  • 28.
    Accessible systems are... • Flexible • Separable • Modifiable
  • 29.
  • 30.
    Markup <!-- Only shownif browser doesn't support JavaScript --> <label for="..." class="fl-progEnhance-basic">Add File:</label> <!-- Only shown if JavaScript is turned on --> <div class="fl-progEnhance-enhanced"> It’s just a couple of classes!
  • 31.
    Styles .fl-progEnhance-enhanced {display:none} .fl-progEnhance-basic {} Hide the fancy stuff, show the basics by default.
  • 32.
    The Code // UseJavaScript to hide basic markup. $("head").append("<style type='text/css'> .fl-progEnhance-basic{ display: none; } .fl-progEnhance-enhanced { display: block; } </style>"); Use JavaScript to flip the styles around!
  • 33.
  • 34.
  • 35.
    Opaque Markup <!-- Thisis a Tabs widget. --> <!-- How would you know, looking only at the markup? --> <ol> <li id="ch1Tab"> <a href="#ch1Panel">Chapter 1</a> </li> <li id="ch2Tab"> <a href="#ch2Panel">Chapter 2</a> </li> <li id="quizTab"> <a href="#quizPanel">Quiz</a> </li> </ol> <div> <div id="ch1Panel">Chapter 1 Stuff</div> <div id=”ch2Panel”>Chapter 2 Stuff</div> <div id=”quizPanel”>Quiz Stuff</div> </div>
  • 36.
  • 37.
  • 38.
    Roles, States, Properties •Roles describe widgets not present in HTML 4 slider, menubar, tab, dialog • Properties describe characteristics: draggable, hasPopup, required • States describe what’s happening: busy, disabled, selected, hidden
  • 39.
    Using ARIA <!-- Now*these* are Tabs! --> <ol role=”tablist”> <li id=”ch1Tab” role=”tab”> <a href="#ch1Panel">Chapter 1</a> </li> <li id=”ch2Tab” role=”tab”> <a href="#ch2Panel">Chapter 2</a> </li> <li id=”quizTab” role=”tab”> <a href="#quizPanel">Quiz</a> </li> </ol> <div> <div id="ch1Panel" role=”tabpanel” aria-labelledby=”ch1Tab”>Chapter 1 Stuff</div> <div id=”ch2Panel” role=”tabpanel” aria-labelledby=”ch2Tab”>Chapter 2 Stuff</div> <div id=”quizPanel” role=”tabpanel” aria-labelledby=”quizTab”>Quiz Stuff</div> </div>
  • 40.
    Adding ARIA incode // Identify the container as a list of tabs. tabContainer.attr("role", "tablist"); // Give each tab the "tab" role. tabs.attr("role", "tab"); // Give each panel the appropriate role, panels.attr("role", "tabpanel"); panels.each(function (idx, panel) { var tabForPanel = that.tabs.eq(idx); // Relate the panel to the tab that labels it. $(panel).attr("aria-labelledby", tabForPanel[0].id); });
  • 41.
    Keyboard Navigation • Everythingthat works with the mouse should work with the keyboard • ... but not always in the same way • Support familiar conventions http://dev.aol.com/dhtml_style_guide
  • 42.
    Keyboard Conventions • Tabkey focuses the control or widget • Arrow keys select an item • Enter or Spacebar activate an item Tab is handled by the browser. For the rest, you need to write code. A lot of code.
  • 43.
  • 44.
    Tabindex examples <!-- Tabcontainer should be focusable --> <ol id=”animalTabs” tabindex=”0”> <!-- Individual Tabs shouldn’t be focusable --> <!-- We’ll focus them with JavaScript instead --> <li id=”tab1”> <a href=”#cats” tabindex=”-1”>Cats</a> </li> <li id=”tab2”> <a href=”#cats” tabindex=”-1”>Dogs</a> </li> <li id=”tab3”> <a href=”#cats” tabindex=”-1”>Alligators</a> </li> </ol>
  • 45.
    Making Things Tabbable • Tabindex varies subtly across browsers • jquery.attr() normalizes it as of 1.3 • For all the gory details: http://fluidproject.org/blog/2008/01/09/ getting-setting-and-removing-tabindex-values-with- javascript/ // Make the tablist accessible with the Tab key. tabContainer.attr("tabindex", "0"); // And take the anchors out of the Tab order. $(“a”, tabs).attr("tabindex", "-1");
  • 46.
    Adding the ArrowKeys // Make each tab accessible with the left and right arrow keys. tabContainer.fluid("selectable", { selectableSelector: that.options.selectors.tabs, direction: fluid.a11y.orientation.HORIZONTAL, onSelect: function (tab) { $(tab).addClass(that.options.styles.highlighted); }, onUnselect: function (tab) { $(tab).removeClass(that.options.styles.highlighted); } });
  • 47.
    Making Them Activatable //Make each tab activatable with Spacebar and Enter. tabs.fluid("activatable", function (evt) { // Your handler code here. Maybe the same as .click()? });
  • 48.
    Documentation • Tutorial: http://wiki.fluidproject.org/display/fluid/Keyboard+Accessibility +Tutorial • API Reference: http://wiki.fluidproject.org/display/fluid/Keyboard+Accessibility +Plugin+API
  • 49.
  • 50.
    “you have touse flash for that” “the web can’t do that!” “you need an app for that!”
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
    Beyond the buzzword... •Media, drawing, animation, and interactivity <audio>, <video>, <canvas> • New widgets—you don’t have to roll your own <progress>, <menu> • Richer semantics for forms and documents <article>, <nav>, <input type=”date”>
  • 57.
    Other cool stuff... •CSS3 transition, transform, gradient • Working with files File API, FormData, XHR Level 2 • Coming soon Device, Text to Speech!
  • 58.
  • 59.
    Making use ofsemantics
  • 60.
    What’s coming • Headings Based on nesting within sections • Continued enhancements from semantics e.g. improved AT awareness for navigation <nav> • Native widgets
  • 61.
  • 62.
  • 63.
    Canvas Accessibility 1. ShadowDOM 2. Focus indicators ... not quite yet. In the meantime... 1. Build alternatives 2. Degrade gracefully
  • 64.
    The Bottom Line •HTML5 is coming—experiment with it now • Lots of great potential for improving access • Assistive technologies are slow on the uptake • Some features are going to be a challenge (Canvas)
  • 65.
  • 66.
  • 67.
    Features • Degrades gracefully • Uploads multiple files at once • Keyboard navigable • Uses hot new HTML5 features: FormData XMLHttpRequest Level 2 <progress> (almost!)
  • 68.
    Dive right in:markup <input type="file" multiple="" id="d-uploader-filesControl" class="d-uploader-filesControl fl-progEnhance-basic" />
  • 69.
    Getting the files filesControl.change(function() { that.events.onAdd.fire(filesControl[0].files); });
  • 70.
    demo.uploader.sendRequest = function(file, url, events) { var formData = new FormData(); formData.append("file", file); // Create a new XHR. var xhr = new XMLHttpRequest(); xhr.open("POST", url, true); // Register success and error listeners. xhr.onreadystatechange = function () { if (status === 200) { events.onSuccess.fire(file); } else { events.onError.fire(file); } }; // Listen for progress events as the file is uploading. xhr.upload.onprogress = function (progressEvent) { events.onProgress.fire(file, progressEvent.loaded, progressEvent.total); }; // Send off the request to the server. xhr.send(formData); };
  • 72.
    HTML5 Inputs <input type=”tel”> <!-- phone number --> <input type=”email”> <!-- e-mail address --> <input type=”date”> <!-- date --> <input type=”search”> <!-- search field --> <!-- number field --> <input type=”number” min=”0” max=”10” step=”1” value=”1”> <!-- Like an autocomplete widget --> <input list=”dlist”> <datalist id=”dlist”><option value=”HTML5”></datalist>
  • 73.
    HTML5 Inputs: attributes/properies <label for=”name”>Name</label> <input type=”text” id=”name” placeholder=”My name is ...” required autofocus />
  • 74.
    Geolocation // test ifgeolocation api is supported if (!!navigator.geolocation) { // success callback is passed a location object // coords property holds coordinate information // Firefox also has an address property navigator.geolocation.getCurrentPosition(success, error); }
  • 75.
    Geolocation: Location Object // test if geolocation api is supported if (!!navigator.geolocation) { // success callback is passed a location object navigator.geolocation.getCurrentPosition(success, error); }
  • 77.
    What’s Infusion? • Applicationframework built on top of jQuery • UI components you can reuse and adapt • Lightweight CSS framework for styling • Accessibility tools and plugins for jQuery • Open architecture: everything is configurable
  • 78.
    Great UX ishard work • Your code gets unruly as it grows • UIs are hard to reuse or repurpose • Design change requires big code change • Accessibility is confusing • Combining different code/libraries doesn’t always work
  • 79.
    Open Architecture: Unlock your markup Let developers and users in A widget isn’t just one thing Question the rules No Black Boxes
  • 80.
    Transparent Apps • M is where it’s at • Events inside and out • Assistive technology inside the Web, not bolted on
  • 81.
  • 82.
  • 83.
    CSS Frameworks “If you’regoing to use a framework, it should be yours; one that you’ve created. You can look at existing frameworks for ideas and hack at it. But the professionals in this room are not well served by picking up a framework and using it as-is.” - Eric Meyer
  • 84.
    Fluid Skinning System •FSS is built to be hacked on • Provides a core set of building blocks • Reset, text, layouts, themes • Namespaced: no conflicts with your stuff • Themes for better legibility & readability http://wiki.fluidproject.org/x/96M7
  • 85.
  • 86.
    Questions? Justin Obara e: jobara@ocad.ca ColinClark e: cclark@ocad.ca t: @colinbdclark fluidproject.org github.com/fluid-project
  • 87.
    Photo Credits Curb cut,Great PA-NJ, http://www.flickr.com/photos/50393252@N02/4822063888/ Stethoscope, Han-Oh Chung, http://www.flickr.com/photos/chickenlump/2038512161/ Texting while walking, Mobile Monday Amsterdam, http://www.flickr.com/photos/momoams/2926622070/ MOMA WiFi, http://www.flickr.com/photos/89554035@N00/2445178036 Plasticine iPhone, Paula Ortiz López, http://www.flickr.com/photos/paulaortizlopez/5342740603/ Skateboarder, Amin Samsudin, http://www.flickr.com/photos/aminchoc/4108543387/ Plasticine Animal, panshipanshi, http://www.flickr.com/photos/panshipanshi/2123208719/

Editor's Notes