rich internet applications - how to make them accessible
Post on 05-Sep-2014
678 Views
Preview:
DESCRIPTION
TRANSCRIPT
Rich
Internet
Apps –
How to
Make
Them
Accessible
Dylan BarrellTwitter: @dylanbarrell, @unobfuscatorGitHub: dylanbhttp://unobfuscated.blogspot.com/
8 trillion
$8 trillion
15%
1 billion
POUR
PerceivableOperableUnderstandableRobust
Call a Spade a Spade
RoleNameState(s)Value
Keyboard (and gestures) – add graphic of a Braille keyboard
• Accessibility and the DOM– DOM tree
• Semantic structure• Styled with CSS• Mouse and Keyboard behavior implemented by
browser• Default mapping to the accessibility tree
– Accessibility tree• Representation of the accessibility information• Each element has a name, a role, a value and a
state• Interpreted by Assistive technology• Used in combination with the Accessibility API
Insert periodic table of ARIA roles
• ARIA roles:– Provide the ability to control the
transition between application (forms) mode and document mode
– Provide more native announcments for widgets like menus, tabs, sliders etc.
– Provide for the ability to control announcements when updates occur away from the focus
– Provide much more control over the structure of the document and how someone navigates around it
<div id=”myId" class=”…”><button class=”…” id=”…”role="slider”aria-labelledby=”sliderLabelID”aria-valuemin="0" aria-valuemax="100”aria-valuenow="0" aria-valuetext="0%”></button>
</div>
• Adding role:– Changes the mapping to the
accessibility API– Does not change the behavior• Focussability• Keyboard interaction• Mouse interaction
– Does not change the appearance
<div id=”myId" class=”…”><button class=”…” id=”…”role="slider”aria-labelledby=”sliderLabelID”aria-valuemin="0" aria-valuemax="100”aria-valuenow="0" aria-valuetext="0%”></button>
</div>
Insert periodic table of ARIA attributes
• ARIA attributes:– Solve the problem of multiple labels and
descriptions through the addition of finer-grained labeling attributes
– Enhance the ARIA roles through the addition of standard state, value and role-specifying attributes
– Add some attributes for better control of what is spoken by the screen reader versus what is simply there for presentational purposes
<div id=”myId" class=”…”><button class=”…” id=”…”role="slider”aria-labelledby=”sliderLabelID”aria-valuemin="0" aria-valuemax="100”aria-valuenow="0" aria-valuetext="0%”></button>
</div>
<div id=”myId" class=”…”><button class=”…” id=”…”role="slider”aria-labelledby=”sliderLabelID”aria-valuemin="0" aria-valuemax="100”aria-valuenow="0" aria-valuetext="0%”></button>
</div>
<div id=”myId" class=”…”><button class=”…” id=”…”role="slider”aria-labelledby=”sliderLabelID”aria-valuemin="0" aria-valuemax="100”aria-valuenow="0" aria-valuetext="0%”></button>
</div>
<div id=”myId" class=”…”><button class=”…” id=”…”role="slider”aria-labelledby=”sliderLabelID”aria-valuemin="0" aria-valuemax="100”aria-valuenow="0" aria-valuetext="0%”></button>
</div>
<div id=”myId" class=”…”><button class=”…” id=”…”role="slider”aria-labelledby=”sliderLabelID”aria-valuemin="0" aria-valuemax="100”aria-valuenow="0" aria-valuetext="0%”></button>
</div>
<div id=”myId" class=”…”><button class=”…” id=”…”role="slider”aria-labelledby=”sliderLabelID”aria-valuemin="0" aria-valuemax="100”aria-valuenow="0" aria-valuetext="0%”></button>
</div>
handled = false, $this = jQuery(this);
if (e.ctrlKey || e.shiftKey || e.altKey || e.metaKey) { // not interested return; } /* * Open a sub-menu and place focus on the first menuitem within it */ function openMenu() { if($this.hasClass("a11yfy-has-submenu")) { $this.addClass("open").attr("aria-expanded",
"true").find(">ul>li:visible").first().attr("tabindex", "0").focus(); $this.attr("tabindex", "-1"); } } /* * Move the focus to the menuitem preceding the current menuitem */ function prevInMenu() { var $context = $this; $this.attr("tabindex", "-1"); while (true) { if ($context.prev().is(':visible')) { $context.prev().attr("tabindex", "0").focus(); return } $context = $context.prev(); if (!$context.prev().length) { $context = $this.parent().find(">li").last(); if ($context.is(':visible')) { $context.attr("tabindex", "0").focus(); return } } if ($context[0] === $this[0]) { $this.attr("tabindex", "0") break; } } } /* * Move the focus to the next menuitem after the currently focussed
menuitem */ function nextInMenu() { var $context = $this; $this.attr("tabindex", "-1"); while (true) { if ($context.next().is(':visible')) { $context.next().attr("tabindex", "0").focus(
/* * This implements the WAI-ARIA-PRACTICES keyboard functionality
where * pressing the key, corresponding to the first letter of a VISIBLE element * will move the focus to the first such element after the currently
focussed * element */ var keyCode = e.charCode || e.which || e.keyCode, keyString = String.fromCharCode(keyCode).toLowerCase(), ourIndex = -1, currentItem = this, $this = jQuery(this), $nextItem, $prevItem, $menuitems = $menu.find("li[role=\"menuitem\"]:visible");
if (keyCode === 9) { return true; }
$menuitems.each(function(index, value) { if (value === currentItem) { ourIndex = index; } if (index > ourIndex && !$nextItem) { if (jQuery(value).text().trim().toLowerCase().indexOf(keyString)
=== 0) { if (ourIndex !== -1) { $nextItem = jQuery(value); } else if (!$prevItem) { $prevItem = jQuery(value); } } } }); if (!$nextItem && $prevItem) { $nextItem = $prevItem; } if ($nextItem) { $nextItem.attr("tabindex", "0").focus(); $this.attr("tabindex", "-1"); if ($nextItem.parent().get(0) !== $this.parent().get(0)) { $this.parent().parent("li").removeClass("open").attr("aria-
expanded", "false"); } } e.stopPropagation(); }).on("keydown", function(e) { /* * This implements the WAI-ARIA-PRACTICES keyboard navigation
functionality */ var keyCode = e.which || e.keyCode,
} else { /* If in sub-menu, open sub-sub-menu */ openMenu(); } break; case 40: //down handled = true; if ($this.parent().hasClass("a11yfy-top-level-
menu")) { /* If in menubar, open sub-menu */ openMenu(); } else { /* If in sub-menu, move to the next menuitem
*/ nextInMenu(); } break; } if (handled) { e.preventDefault(); e.stopPropagation(); } return true;
); return } $context = $context.next(); if (!$context.next().length) { $context = $this.parent().find(">li").first(); if ($context.is(':visible')) { $context.attr("tabindex", "0").focus(); return } } if ($context[0] === $this[0]) { $this.attr("tabindex", "0") break; } } } switch(keyCode) { case 32: // space case 13: // enter handled = true; if ($this.find(">a").length) { if ($this.find(">a")[0].click) { /* If this is a leaf node, activate it*/ $this.find(">a")[0].click(); } else { // This is a hack for PhantomJS $this.find(">a").first().trigger("click"); } } else { /* If it has a sub-menu, open the sub-menu */ openMenu(); } break; case 37: //left case 27: //esc handled = true; if (keyCode === 37 && $this.parent().hasClass("a11yfy-top-level-menu")) { /* If in the menubar, then simply move to the previous menuitem */ prevInMenu(); } else { if ($this.parent().attr("role") === "menu") { // this is part of a submenu, set focus on containing li $this.parent().parent().attr("tabindex", "0").focus() .removeClass("open").attr("aria-expanded", "false"); $this.attr("tabindex", "-1"); } } break; case 38: //up handled = true; if ($this.parent().hasClass("a11yfy-top-level-menu")) { /* If in the menubar, then open the sub-menu */ openMenu(); } else { /* If in sub-menu, move to previous element */ prevInMenu(); } break; case 39: //right handled = true; if ($this.parent().hasClass("a11yfy-top-level-menu")) { /* If in menubar, move to next menuitem */ nextInMenu();
First ARIA Best Practice – If there is a native HTML element that does
the job, use that
Examples1. Use <button> and <input
type=“submit”> NOT <a role=“button”>
2. Use <ul>, <ol> and <li> NOT <span role=“list”> etc.
Compelling ARIA roles• Landmark Roles
– main, search, navigation, contentinfo, complementary, banner
– region in combination with aria-label
• Live Region Roles– log, status, alert
• Some Widget Roles– tabpanel and tab– slider– menu, menubar, menuitem and associated attributes– dialog – in combination with the document role to get it
to work in NVDA– tree and treeitem – a bit tricky to get to work reliably
• Some form roles– button, textbox, checkbox, radio, radiogroup
• presentation role
Second ARIA Best Practice – test it on all YOUR platforms with the assistive technology YOU must
support
All platforms have problems, most have workarounds, iOS is the
most problematic and Android is not quite ready for prime time
yet
ARIA holes• Tables, tables, tables– Use the a11yfy library
• Arrow keys on iOS– Insert dynamic modal content in line– Use gestures
• Gestures– Think hard about your mapping to the
portable gestures– add on screen controls where possible
Accessible Gesture Calendar Examplehttps://github.com/dylanb/gestura11yhttp://dylanb.github.io/datepicker/datepicker.html
• Shows use of tabindex to control focus• Shows use of role=“application” to force
application mode• Shows use of aria-live regions to announce the
current date as the user moves around• Shows use of aria-hidden to hide presentation
markup from the screen reader• Shows use of keyboard handler and mapping to
gestures• Shows how to ensure that gestures are
consistent regardless of zoom level
Accessible Gesture Calendar Examplehttps://github.com/dylanb/gestura11y
Third ARIA Best Practice – Always attach your event
handlers to the same element that has the role and the
focus
If you stick to this rule, you will avoid events not being delivered consistently
Fourth ARIA Best Practice – In complex widgets like
menubars, tabpanels etc. always make all interim
structures presentational
Fifth ARIA Best Practice – in a complex widget where you
are managing focus, disable all naturally focusable
elements with tabindex=“-1”
Example is the a11yfy menu examples where the anchors
are given tabindex=“-1”
FinallyThere is a wealth or resources including
The ARIA specification (recommendation, normative)http://www.w3.org/TR/wai-aria/
The Authoring Practices (draft) http://www.w3.org/TR/wai-aria-practices/
Using ARIA in HTML (draft, informative)http://www.w3.org/TR/aria-in-html/
The WAI Web Site http://www.w3.org/WAI/intro/aria
Mozilla Developer Network https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA
top related