accessibility: a journey to accessible rich components
TRANSCRIPT
A Journey to Accessible Rich UI Components
with Jason Jang
“Journey” sounds inappropriately epic and kind of cornered me into a theme, for this, my first talk.
What to Expect▹My story▹Important Considerations for our
organization▹Code Examples of making a Rich UI
Component accessible
Sometimes the hardest part of learning a language, is learning the language around
the language.
Disclaimer
Whoa, deep.
Some Terminology▹a11y > accessibility▹component > a part of an interface▹Rich UI > interactive components
Whoa, deep.
My Story
Whoa, deep.
In the beginning...▹“Making websites” was a hobby▹… that became a side gig▹… that became a part-time gig▹… that became a full-time gig at an agency▹… that became a stint as a freelancer▹… that became a full-time gig at Achievers
There. There’s your journey.
First Encounter with a11y▹Everyone uses the Internet▹People use screen readers? Never got use one.▹A11y considered a checkmark▹All text and images
First Encounter with a11ySemantic HTML and good markup
▹ Headings, Lists, Links▹ <b> <i> vs <strong> <em>
Landmark Roles: ▹ <div role=”banner”>▹ <div role=”form”>▹ <div role=”main”>▹ <div role=”navigation”>▹ <div role=”search”>
a11y Tools▹WAVE Accessibility Tool▹aChecker▹Quail (JS Accessibility Tool)▹Google Accessibility Tool
Fast-forward 5 or so yearsI lost count
Platform Demo
Jason, can we make this accessible?
ARIA SAVES THE DAY!
WHY ARIA?...and not Arya?
WAI-ARIA!Web Accessibility Initiative – Accessible Rich Internet Applications
Henceforth referred to as accessibility API or ARIA
listitemlogmainmarqueemathmenumenubarmenuitemmenuitemcheckboxmenuitemradionavigationnoteoptionpresentation
dialogdirectorydocumentformgridtablegridcellgroupheadingimginput (abstract role)landmark linklistlistbox
alertalertdialogapplicationarticlebannerbuttoncheckboxcolumnheadercomboboxcommandcomplementarycompositecontentinfodefinition
progressbarradioradiogrouprange (abstract role)regionroletype (abstract role)rowrowgrouprowheaderscrollbarsearchsection (abstract role)
LOOK AT ALL THESE ROLES!
alertalertdialogapplicationarticlebannerbuttoncheckboxcolumnheadercomboboxcommandcomplementarycompositecontentinfodefinitiondialogdirectorydocumentformgridtablegridcellgroupheadingimginput (abstract role)landmark linklistlistbox
listitemlogmainmarqueemathmenumenubarmenuitemmenuitemcheckboxmenuitemradionavigationnoteoptionpresentation
listitemlogmainmarqueemathmenumenubarmenuitemmenuitemcheckboxmenuitemradionavigationnoteoptionpresentation
dialogdirectorydocumentformgridtablegridcellgroupheadingimginput (abstract role)landmark linklistlistbox
progressbarradioradiogrouprange (abstract role)regionroletype (abstract role)rowrowgrouprowheaderscrollbarsearchsection (abstract role)
LOOK AT ALL THESE ROLES!
a11y: You’ve Changed
This is not the Aria you were looking for, and that’s a reference to something else entirely.
a11y: You’ve Changed▹ Support for Rich Interfaces
▹new roles <div role=”button”>
▹presentation changes <div role=”button” aria-pressed=”true”>
▹asynchronous content <div role=”button” aria-pressed=”true”>
▹ a11y evaluation tools still useful elsewhere
We’re all equipped and ready to go!Right?
WaitBefore we could dig into it...
Important ConsiderationsFor your Organization
There were several
Important Considerations
1. Define Usersa. Assistive Technologies
2. Take Stock of Codebase and UI
3. Determine rollout plan
4. How will you influence a Paradigm Shift?
1. Define Users
1. Define Users▹Vision-impaired▹Dexterity-impaired▹Hearing impaired
1a. Assistive TechnologiesLearn about the Tools▹JAWS, NVDA, VoiceOver▹Windows Eyes, ZoomText▹Specialized Input Devices
2. Take Stock of YourCodebase and UI
2. Take Stock of Codebase and UI▹How is your markup?▹Identify and address Anti-Patterns▹Breakdown features into components so
you can tackle them individually (Agile)
3. Take Stock of Codebase and UI
3. Determine Rollout Plan
▹ Prioritize core features▹ Break down into components▹ Tackle global elements
▹ Don’t omit features or content
4. Influence a Paradigm Shift
▹a11y is NOT a checkmark▹Affects everyone in the development lifecycle
4. Influence a Paradigm Shift
Education▹ Devs and Testers need software training▹ Designers need to understand a11y needs for both
users and developers▹ Project Managers need to know how to break the
work down
4. Influence a Paradigm Shift
4. Influence a Paradigm ShiftProcess Improvement▹ Design mocks need to be vetted for a11y at design
stage before development begins▹ Pattern/Component Library even more valuable
4. Influence a Paradigm ShiftTesting▹ a11y testing can be expensive▹ Some automation possible▹ Manual testing required▹ Testing with real users is the only real test
Okay, with all things considered...
Accessible Rich UI Components
The Code Demo
a11y Rich UI Components...1. ...use WAI-ARIA for communicating:
▹ Roles▹ Presentation changes like state and visibility▹ Asynchronous content changes
2. …are fully keyboard accessible
Time for a quick
SCREEN READER DEMO
and now for some
CODE EXAMPLES
Code Examples1. Form Basics2. Live Regions3. Slider Example4. Tabs and Tab Panels
Code Example 1: Form Basics (1 of 3)
Code Example 1: Form Basics (2 of 3)
<label>Your email</label>
<input type="email" />
<label>Reason for contacting</label>
<select>
<option value="Option 1">Questions</option>
<option value="Option 2">Admiration</option>
<option value="Option 3">Can I get your number?</option>
</select>
<label>Message</label>
<textarea></textarea>
Code Example 1: Form Basics (3 of 3)
<label for="email">Your email</label>
<input id="email" type="email" />
<label for="reason">Reason for contacting</label>
<select id="reason">
<option value="Option 1">Questions</option>
<option value="Option 2">Admiration</option>
<option value="Option 3">Can I get your number?</option>
</select>
<label for="message">Message</label>
<textarea id="message"></textarea>
Code Example 2: Live Region (1 of 3)
<!-- FORM--><label for="message">Message</label><textarea id="message"></textarea><input class="button-primary" type="submit" value="Submit">
<!-- THROBBER TO INDICATE AJAX CALL --><img src="img/ajax-loader.gif" js-throbber class="hidden">
<!-- FEEDBACK FOR AJAX CALL --><div class="alert error hidden" js-ajax-alert="error" ><a href="#message">There was an error with your message</a></div>
<!-- LIVE REGION --><div role="alert" aria-live="assertive" aria-relevant="additions" class="a11yHiddenText" js-globalLiveRegion></div>
Code Example 2: Live Region (2 of 3)
<script> function formSubmit() { $("form").submit(function() { showThrobber(); $.ajax( "formSubmit.php" ) .done(function() { hideThrobber(); alert( "success" ); }) .fail(function() { hideThrobber(); alert( "error" ); }); }); }</script>
Code Example 2: Live Region (2 of 3)
Code Example 2: Live Region (3 of 3)
<script> function formSubmit() { $("form").submit(function() { showThrobber(); $.ajax( "formSubmit.php" ) .done(function() { hideThrobber(); updateLiveRegion("Form submitted successfully"); }) .fail(function() { hideThrobber(); updateLiveRegion("Form submit unsuccessful"); }); }); } function updateLiveRegion(text) { $("[js-globalLiveRegion]").html(text); }</script>
Code Example 2: Live Region (3 of 3)<script> function formSubmit() { $("form").submit(function() { submitPending(true); $.ajax( "formSubmit.php" ) .done(function() { submitPending(false); updateLiveRegion("Form submitted successfully"); }) .fail(function() { submitPending(false); updateLiveRegion("Form submit unsuccessful"); }); }); } function updateLiveRegion(text) { $("[js-globalLiveRegion]").html(text); } function submitPending(true) { showThrobber(true); disableSubmit(true); } function disableSubmit(state) { $("#submit") .attr("aria-disabled", state) .attr("disabled", "disabled") .addClass('.disabled'); }</script>
<div js-slider> <span class="ui-slider-handle" tabindex="0"></span></div>
Code Example 3: Slider
<div js-slider> <span class="ui-slider-handle" tabindex="0"
role="slider" aria-valuemin="0" aria-valuemax="500" aria-valuenow="0" aria-valuetext="0"></span></div>
$slider.slider({ value: 0, min: 0, max: 500, step: 50, slide: function(event, ui) { $display.html(ui.value); var $handle = $slider.find(".ui-slider-handle");
$handle .attr("aria-valuemin", 0) .attr("aria-valuemax", 500) .attr("aria-valuenow", 0) .attr("aria-valuetext", 0); }});
Code Example 4: Tabs
Code Example 4: Tabs<div id="tabs-container" js-tabContainer class="clearfix"> <ul class="tabs-menu"> <li class="current"><a href="#tab-1" js-tab>Tab 1</a></li> <li><a href="#tab-2" js-tab>Tab 2</a></li> <li><a href="#tab-3" js-tab>Tab 3</a></li> <li><a href="#tab-4" js-tab>Tab 4</a></li> </ul> <div class="tab"> <div id="tab-1" class="tab-content"> <p>...</p> </div> <div id="tab-2" class="tab-content"> <p>...</p> </div> <div id="tab-3" class="tab-content"> <p>...</p> </div> <div id="tab-4" class="tab-content"> <p>...</p> </div> </div></div>
Code Example 4: Tabs<div id="tabs-container" js-tabContainer class="clearfix"> <ul class="tabs-menu" role="tablist"> <li role="tab" aria-selected="true" class="current"><a href="#tab-1" js-tab>Tab 1</a></li> <li role="tab" aria-selected="false"><a href="#tab-2" js-tab>Tab 2</a></li> <li role="tab" aria-selected="false"><a href="#tab-3" js-tab>Tab 3</a></li> <li role="tab" aria-selected="false"><a href="#tab-4" js-tab>Tab 4</a></li> </ul> <div class="tab"> <div id="tab-1" class="tab-content" role="tabpanel" aria-hidden="false"> <p>...</p> </div> <div id="tab-2" class="tab-content" role="tabpanel" aria-hidden="true"> <p>...</p> </div> <div id="tab-3" class="tab-content" role="tabpanel" aria-hidden="true"> <p>...</p> </div> <div id="tab-4" class="tab-content" role="tabpanel" aria-hidden="true"> <p>...</p> </div> </div></div>
Code Example 4: Tabsfunction initTabsKeyboardA11y($tabsContainer) { $("[js-tabContainer]").keydown(function(e) { e.preventDefault();
var $currentTab = $(this).find("[role=tab].current [js-tab]"); var keyPressed = getKeyName(e.keyCode);
if (keyPressed === "right") { var $next = $currentTab.parent().next().find("[js-tab]") if ($next) { $next.trigger("click").focus(); } } else if (keyPressed === "left") { var $prev = $currentTab.parent().prev().find("[js-tab]") if ($prev) { $prev.trigger("click").focus(); } } });}
Knowledge Nugget!If you can’t find an ARIA ROLE that addresses your design pattern, there’s a good chance you have an ANTI-PATTERN (or they just call it by a different name :)
Time for another quick
SCREEN READER DEMOThis time on an accessible page
Keyboard Accessibility
Keyboard Accessibility▹ Screen Readers Provide shortcuts to traverse page▹ TAB is also commonly used▹ Certain ROLEs expect the use of arrow keys▹ You can often refer to OS or other online examples
for keyboard flow
Keyboard Accessibility● Get to know tabindex● Both ENTER and SPACEBAR should trigger
a click● In Forms, SPACEBAR activates, ENTER
submits the form
Biggest Hurdles▹Learning to use screen readers (ongoing!)▹Deciding where to start▹Testing
Added Benefits from A11yImproved UI▹Forces you to scrutinize UI decisions▹Opportunity to fix design decisions
Food for Thought▹ Decouple your a11y JS from regular JS▹ Create helpers for JS and SCSS▹ Here’s a clever way to enforce a11y
Before we finish...
Let’s Remember
Influence a Paradigm Shift▹A11y affects everyone▹Consider users, testers, designers, developers….
Let’s Also Remember
Rich Components...▹ Leverage WAI-ARIA to comm. with screen readers▹Provide Keyboard a11y
Special Thanks
Chris Gurney (PM) & Avinder Walia (Dev)