building for accessibility

Post on 12-Aug-2015

4.556 Views

Category:

Technology

2 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Designing

Is This Designing?

http://www.adobe.com/inspire/2013/11/photoshop-reflow-generator.html

Is This Designing?

https://flic.kr/p/qR7Lo8

Is This Designing?

http://thisoldcity.com/advocacy/photos-what-snow-tells-us-about-creating-better-public-spaces-e-passyunk-avenue

Is This Designing?

https://flic.kr/p/7bRxB2

Yes.

@nathanhammond nathanhammond.com

nathan@nathanhammond.com

nathanhammond

http://www.unicode.org/reports/tr51/tr51-2.html#Emoji_Modifiers

Good Design

https://flic.kr/p/die16e, https://flic.kr/p/99ad9q

Universal Design

Stokke Tripp Trapp

Universal?

Designing for Accessibility

http://www.istockphoto.com/photo/group-of-business-men-and-women-in-a-meeting-37790018

https://flic.kr/p/asasy3https://flic.kr/p/asasy3

https://commons.wikimedia.org/wiki/File:US_House_Committee.jpg

https://flic.kr/p/9ZA2Jx

Demo

Learnings• Screen readers start reading from the top of the

page on each page load.

• Dynamically updating the DOM results in no information being passed to the user by default.

• Markup is still important. Ember's real links and the code you write in your templates is important.

• ARIA roles still do what we expect.

Building for Accessibility

Page LoadingWe want to emulate the page load behavior

demonstrated with static pages.

End-User Goals• Have the screen reader automatically start reading

the content of the page.

• Have it start reading the content from the best point in the application hierarchy.

• Make it clear to the user that the content on the page has changed.

• Follow other recommended patterns that screen reader users are familiar with.

Ember Goals• Have the solution work with current idiomatic

Ember code or minimize migration cost.

• Make the change in a backwards compatible way to meet Ember's stability without stagnation goal.

• The default approach should be accessible, API design is a feature.

• Provide for extensibility with hooks enabling enhancement of this higher-level feature.

Exploration

InventoryRouter, Transitions, Routes,

Templates, Outlets, HTMLBars

Outlet FocusingWhat if we set the focus on the route's outlet immediately

after the transition completes?

Research

Screen Readers• It's like the Internet Explorer and Netscape HTML/

CSS/JS compatibility workarounds all over again.

• Screen readers tend to read whatever you focus.

• Nobody likes being dumped into forms mode.

• Screen readers are aware of content on the page changing, but don't present that to the user until it is asked for.

Ember• The top level outlet is generated and wrapped in a

containing DIV.

• All other outlets are populated by their templates without first being wrapped in a containing DIV.

• Routes have no reference to their rendered output.

• You can have multiple outlets in a single route.

• No-op transitions trigger no route hooks.

Solution Design

API DesignIt'd be nice if every route had a `focus` hook that was

called with the outlet contents.

Ember.Route.reopen({! focus(morph) {! var elem = morph.firstNode;!! try {! if (!elem.getAttribute('tabindex')) {! if (isInteractive(elem)) {! elem.setAttribute('tabindex', 0);! } else {! elem.setAttribute('tabindex', -1);! }! }!! elem.focus();! } catch (e) {}! }!});

DevelopmentHow do we call the `focus` hook?

Ember.Route.reopen({! enter(transition) {! var route = this;!! // Focus "up one level" for index routes.! if (! transition.pivotHandler &&! this.routeName === transition.pH.routeName + '.index'! ) {! transition.pivot = transition.pivotHandler.routeName;! route = transition.pivotHandler;! this._focus(route, transition);! }!! // Handle fresh entries.! if (!transition.pivot) {! transition.pivot = this.routeName;! this._focus(route, transition)! }!! return this._super(...arguments);! }!});

Ember.Route.reopen({! _focus(route, transition, parent) {! var focus = new Ember.RSVP.Promise(function(resolve, reject) {! this.focusPromiseResolve = resolve;! }.bind(route));!! // Set up our context for after the transition completes.! var transitionResolve = (function(transition, route) {! return function(result) {! delete transition.pivot;!! Ember.run.scheduleOnce('afterRender', route, function() {! route.focus(result.focus)! });! };! })(transition, route);!! // Clean up after ourselves in case there is a transition.retry() call.! var transitionReject = (function(transition, route) {! return function() {! delete transition.pivot;! };! })(transition, route);!! Ember.RSVP.hash({ focus, transition })! .then(transitionResolve)! .catch(transitionReject);! }!});

IntegrationWhat happens when Ember needs to be modified.

diff --git a/packages/ember-htmlbars/lib/keywords/outlet.js b/packages/ember-htmlbars/lib/keywords/outlet.js!index 317e899..6b01753 100644!--- a/packages/ember-htmlbars/lib/keywords/outlet.js!+++ b/packages/ember-htmlbars/lib/keywords/outlet.js!@@ -17,6 +17,10 @@ export default function(morph, env, scope, params, hash, template, inverse, visi!+ if (env.outletState.main.render.focusPromiseResolve) {!+ env.outletState.main.render.focusPromiseResolve(morph);!+ delete env.outletState.main.render.focusPromiseResolve;!+ }! keyword('@real_outlet', morph, env, scope, params, hash, template, inverse, visitor);!!diff --git a/packages/ember-routing/lib/system/router.js b/packages/ember-routing/lib/system/router.js!index bb157c1..5600f99 100644!--- a/packages/ember-routing/lib/system/router.js!+++ b/packages/ember-routing/lib/system/router.js!@@ -202,10 +202,18 @@ var EmberRouter = EmberObject.extend(Evented, {! for (var j = 0; j < connections.length; j++) {!+ delete connections[j].focusPromiseResolve;! var appended = appendLiveRoute(liveRoutes, defaultParentState, connections[j]);! liveRoutes = appended.liveRoutes;! if (appended.ownState.render.name === route.routeName || appended.ownState.render.outlet === 'main') {! ownState = appended.ownState;!+ // Pass forward the focus promise resolve hook from the route to the appropriate connection.!+ if (route.focusPromiseResolve) {!+ ownState.render.focusPromiseResolve = route.focusPromiseResolve;!+ delete route.focusPromiseResolve;!+ }

Contributing Back

Write an RFC!You've created and tested a feature!

It's time to get it shipped!

http://bit.ly/rfc66

Usage Notes• You can use this today! http://bit.ly/outletfocusing

• All template content must be wrapped in a `<div>`. Child outlets must also appear inside that `<div>`.

• Automatically generated templates are just `{{outlet}}` and will thus have problems.

• Provide feedback on RFC #66!

Demo

Learnings

• It's a different experience from the static site.

• We have programmatic control over the focusing behavior.

• Setting the focus has additional consequences in terms of scrolling behavior.

Challenge

top related