From the Blog

Accessibility Roles: Role and Gender Identity

In accessibility, the user interface development best practice is: if it is a button, just use a button, if its a link, just use a link. This sometimes invokes the question, "What is a link and what is a button?". The answer to this is actually quite clear, if it looks like a button and acts like a button (i.e. if it operates on something on the page), it is a button. If it looks like a link and acts like a link (i.e. navigates you off to another page) then its a link. If it looks like a link, but acts like a button, its a button and if it looks like a button but acts like a link, its a link. What matters is what it does, not how it looks.

Role Identity

This got me to thinking that buttons and link roles are a lot like gender identity, it doesn't matter what you look like, what matters is how you feel (and hence, how you would like to act).

Liberal vs. Conservative UI Development

There is the liberal approach to UX (and accessibility), which is "I don't much care what it looks like as long as it is semantically marked up to match the way it behaves."

Then there is the conservative approach to UX (and accessibility) which is "If it acts like a button, just use a fucking button." Maybe with less overt fucking though.

Indiana UI Development

Then of course there is the Indiana approach to UI development, which is that any developer is allowed to refuse to work on a customer's UI, if it requires that they use a technology that they consider inferior (normally, whichever technology they are not familiar with). Yeah, I don't want to hire those developers either.

Posts from the Edge: Injecting Parents into Angular2 Components

If your Angular JS 2 component needs to communicate with its parent, there are a couple of ways to do this (with at least one more on the way):

  1. You could inject the parent into the component using the () dependency annotation, or
  2. You could use events
Both of these have the pros and cons, specifically, if you want to send some complex data to your parent, then injecting the parent into the component is preferable.

What happens if you the parent is one of two (or more) potential types of component? There are apparently some concepts being worked on to allow for this but one approach that currently works is to combine the () annotation with the () annotation and provide more than one optional parent dependency. For example, in an ARIA menu, where a menu item can be included in a menu bar or a menu, you could do:


constructor(el: NgElement,
() () parentMenubar: AriaMenubar,
() () parentMenu: AriaMenu) {
this.parent = (parentMenu !== null) ? parentMenu : parentMenubar;
this.parent.apiCall(this); // Call the parent API(s)

Posts from the Edge: Angular 2 Shadow DOM Emulation

I have been playing around with the alpha version of Angular 2 and will be posting small snippets of learnings as I go. Angular 2 recently switched its default DOM strategy to an emulated strategy from a native shadow DOM strategy. So how do you tell Angular 2 to use the shadow DOM instead of its default emulated shadow DOM?

When you bootstrap your application component, you can bind the NativeShadowDomStrategy to the ShadowDomStrategy and inject it into the component. First in your import section of your main application component, import the appropriate dependencies:


import {bind} from 'angular2/di';
import {ShadowDomStrategy, NativeShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
Now you can do the binding and injecting magic on the bootstrap call:

bootstrap(MyApp, [bind(ShadowDomStrategy).toClass(NativeShadowDomStrategy)]);
Voila, you have native shadow DOM.

Polymer and Web Component Accessibility: Best Practices

Polymer and Web Components promise great flexibility for custom UI widget authors to be able to implement custom elements that are on a par with native DOM elements. However the Polymer components are some of the worst offenders from an accessibility perspective. To see whether there was anything inherent in the framework that is causing this, I implemented an accessible ARIA menu (simplified) in 100% native web components and then ported that implementation to Polymer, resulting in an accessible Polymer ARIA menu. The two implementations share almost all of their code (certainly the JavaScript code is almost 100% the same). The only differences are in the basic bootstrapping and some small changes to take into account the fact that Polymer emulates the shadow DOM in browsers that do not yet support it natively. The bottom line is that it is possible to create accessible components. The code for both implementation can be found on the Axponents GitHub repository. Here are a some of the lessons I learned.

Map your Custom Elements to ARIA Roles 1-to-1

When creating custom elements, you want to keep your shadow DOM content to a minimum [1]. You also want the author of the host document to be able to specify the menu items declaritively, using normal element content for the names of the menu items. A hierarchical menu is composed of a menu bar, menu items and drop-down menus. The drop down menus are almost exactly the same as popup menus in terms of their basic functionality (keyboard commands, behavior, CSS positioning and styling). If we implemented the hierarchical menu as a monolithic component, then we would have to implement a separate popup menu component and almost all of our internal code would be dealing with the distributed nodes instead of our own shadow DOM. Breaking the menu up into its separate menu bar, menu item and menu components allows us to re-use the menu and menu item components in a popup menu with very little additional code, keep the shadow DOM to a minimum and write clean event handlers that mostly manipulate he component's own shadow DOM. Accessible web component best practice: create a 1-to-1 mapping between ARIA roles and custom elements.

Parent Child Communication

The webcomponents.org has a list of [1] best practices for web component development. The list suggests using events to communicate upwards from child elements to their containing elements. If you combine this with custom element API calls, then you can instruct children via the API and receive messages back from them via events. In the ARIA menu implementation, I use this pattern to manage focus and implement the keyboard command interaction: calling child element APIS to instruct those elements to take focus (and let them worry about which internal shadow DOM element should obtain focus) and then listen for when they want to send focus back. This creates a nice clean interface and is the mechanism I used to pass focus back and forth between sub-menus when the sub menu is opened through a click, DOWN arrow or ENTER and when the sub-menu is closed with an ESC or LEFT arrow. Accessible web component best practice: combine custom element APIs with custom events to manage child-parent communication.

Polymer's Poly-Filled Shadow DOM

The Shadow DOM specification is a little bit vague on how to map light- and shadow- DOM elements to the accessibility API. I suspect this is one reason why Polymer does not do anything with the custom elements when poly-filling the shadow DOM. In Safari (on OS X), this causes no problem because it appears as though Safari maps unknown elements to the presentation role automatically. However on Windows this causes ATs to lose track of the number of elements within a menu bar or menu and announce every menu item as 1 of 1. Polymer custom component best practice: set role="presentation" on your custom element inside the attached lifecycle callback.

Differences

The differences in the implementations came down to two things:

  1. The shadow DOM CSS selectors do not work and the instructions on the Polymer web site do not work for hierarchical components. Polymer suggests replacind ::shadow and /deep/ with spaces. This works fine for shallow components, but in our case, the components can be nested. This means that you have to replace these pseudo selectors with the immediate child selector > (or some combination of this).
  2. ID references for elements are automatically exposed as globals on the window namespace. If you use these globals to find elements using querySelector, then the elements you get back will not inherit from the Polymer custom component prototype. To get around this you will need to always execute your calls to querySelector from the document element.

Simplified

The reason this ARIA menu is simplified is that it does not yet implement the complete ARIA authoring guidelines for keyboard interaction. It is missing the ability to jump to a menu item by pressing the first character of that menu item. Implementing this feature would simply require additional custom element APIs and events and does not introduce any fundamentally different problems.

What is Accessibility?

Lately there seem to have been a lot of different posts exclaiming "Accessibility is not X" or "Accessibility is not Y" but not too many helping to define what accessibility is. So what is accessibility? At Deque we talk about "Digital Equality" and obviously that is the goal of accessibility, but how do we get there and what actions should we take and which ones should we avoid?

Accessibility is a Way of Thinking

One of my favorite accessibility projects was when I was able to work with Derek Featherstone, Elle Waters and Humana's chosen design firm on the Humana Web site redesign. The project was fun because I got to spend time with Derek and Elle talking through the pros and cons of certain designs and interactions from an accessibility perspective. That was the first time I heard Derek refer to accessibility as a "usability lens." What he means by that is that, whenever you encounter a user interface or interaction that is difficult to make accessible, what is probably happening is that accessibility has focused-in on a usability problem that extends beyond a single group of users.

If you step back from the "technical accessibility" aspect and focus on answering the question "why do we have this feature?" you are likely to uncover what I like to call a "me too feature" or a user interface that is just plain bad. Maps are a good example of "me too features", they are often placed onto web sites because they are easy to do (just use Google Maps) and everyone has them. So when you step back and ask "why do we have a map here?", there is often no crisp answer.

Bicycles, Strollers and Wheelchairs

Derek also likes to use a map in his presentations to illustrate this issue. In his example, he shows a map of a city which is planning on expanding its borders. The map contains two shaded polygons (one contained within the other) that represent the current and proposed borders of the city.

map showing overlapping polygons of the current and proposed city boundaries
Derek talks through the possible ways that that interface can be made "technically accessible". One of these involves adding a description of the new polygon as in "On the North side, the new boundary crosses Main Street between 7th Avenue and 8th Avenue etc." This solution would classify as accessible and along with other changes to make the controls accessible etc. would certainly be compliant with AODA, WCAG 2 AA etc. The problem is that it is probably not providing the users with functional equivalence.

The solution to the above conundrum is to ask the question "why are we putting this map here?" Derek's answer is that most often the residents are interested in answering the question "Is my house/office/building inside or outside the proposed new boundary?". Once you have this sort of clarity, it becomes clear that the map is not the best solution for anyone. A much better solution is to provide an address search that answers the question directly.

map with overlapping polygons and a search field for the postal code

It just so happens that (as long as the geographic data exists), making that solution accessible and usable is very easy. You just need to make sure the search input field is appropriately labeled, that the instructions on the address format are associated with the input field, that the button is a proper button, if the search results update dynamically, that the screen reader user is made aware of the update etc. etc. So while the accessibility lens has allowed you to create a solution that, like curb cuts, is useful to everyone. You still need to make that solution technically accessible or "compliant".

What Thoughts?

When Elle, Derek and I were discussing the merits of different designs from an accessibility perspective, I found my thoughts jumping through all of those levels. Sometimes I would be asking meta-level questions like "why do we need tabs at all?", other times I would ask questions like "how would a person with Parkinsons (i.e. a keyboard only, sighted user) navigate this page effectively" and sometimes I found myself asking questions like "should we use ARIA markup?". In other words, I found myself thinking in terms of usability, disability and technique interchangeably and sometimes, all at the same time.

Thinking in disabilities requires being able to answer questions like "why would a blind person ever need to buy a car?", "why do I need to have a visible label if there is a title attribute?". It requires empathy. So accessibility is empathy. Thinking in terms of use cases involves understanding user needs. It is usability. So accessibility is usability. Thinking in terms of accessibility techniques involves knowing what techniques work for what types of UI controls on which platforms. It is technical accessibility or "compliance". So accessibility is compliance.

What Actions?

Put yourself in the shoes of the map functionality project manager. What if the application is like most other map applications out there and inaccessible unless using a mouse? In addition to blind screen reader users, users who are sighted but cannot use a mouse will not be able to benefit from the functionality. Clearly a better right solution is one where we create a search interface. What if the data does not exist to create that interface? What do we do? "We are visionary! We are bold! We don't do things in half measures!" you exclaim.

  1. Do we throw up our hands and do nothing?
  2. Do we go away and gather the data, work on the new user interface secure in the knowledge that in 1 year we will be able to offer a superior experience to EVERYONE?
  3. Do we make the application technically accessible so that keyboard users can use it but screen reader users only get a description of the boundaries in the way described above?
  4. Or do we do both #2 and #3?

If we do #2 above, the user may not be able to benefit by the time the application is ready. If we do #3 we will be compliant and the situation will be better for all users while obviously still not really equal for all. If the users need timely access to the application and that access makes a real difference in their lives, then it is clear that the right answer is to do #2 and number #3 so that the next time the same question comes up we are ready with our ideal solution while at the same tim allowing some users to achieve better usability now.

The problem is that in many development organizations, the action that gets taken when we exclaim "Accessibility is not compliance!" is that we give organizations the excuse to only take path #2 and we end up delaying or preventing better access to the functionality for all users who need accessibility now. While often far from ideal, compliance is better than no accessibility at all.

Accessibility is practical

Above all else, as accessibility professionals, we should take our actions thoughtfully, recognizing that the actions we take directly impact what people can and cannot achieve on the Web every day. We should empathize, we should understand and we should not deal in idealistic absolutes. Accessibility is practical, not idealistic.

Acknowledgements

Special thanks to Derek Featherstone for reviewing this blog post and allowing me to use a screen shot from his presentation on .

Angular.js Accessibility – Knocking off my TodoMVCs, accessibly

This is the final part of the blog series on making the TodoMVC Angular.js application accessible. In a prior post, we published a list of accessibility issues in the Angular.js TodoMVC application and then proceeded to fix them all the final accessible TodoMVC application can be found on my github.io page and the entire project in my fork of the TodoMVC repository. To see the description of the final changes go to the end of this post.

Analyzing the Changes

Here are some statistics for the changes required to retro-fit the Angular.js application to make it fully WCAG 2 AA compliant and accessible. The statistics only take the UI code into account as it is highly unlikely that accessibility changes will impact back-end code:

Statistics for changes to make the Angular.js TodoMVC application accessible
Format/Language Percentage of Lines Changed
HTML ~50%
CSS <10%
JavaScript ~30%
These stats are not that surprising given that most of technical accessibility is HTML markup and the JavaScript interacting with that markup to provide device interaction. CSS changes are generally restricted to changes that result from markup changing (different elements, adding classes for things like off-screen) and then CSS for new markup being inserted. You would expect to see the CSS change percentage come down in a larger project and the other two percentages stay approximately the same (depending on how bad the application was to start-with and how consistently bad it is).

Accessibility Remediation Effort

The most difficult (time consuming) changes were those that required significant controller and directive refactoring. In this case that was the keyboard interactivity and the resulting focus management - although if you do not have experience with accessible markup, finding the right markup for all the labeling and associations might take relatively more time than it did for me.

Useful Accessibility Modules?

Overall ngA11y was useful for handling the focus management on the filtering changes (saved about 10 lines of code I would otherwise have had to write myself) and ngAria was also mildly useful for mapping the mouse click to the keyboard ENTER key and making the clickable element keyboard focusable.

I would probably have got more use out of both modules in a more complex application and you will probably want to include (at least parts of) both modules in most of your applications. ngA11y allows you to only pick those components you really need, whereas ngAria is all-or-nothing. WARNING: sweeping generalizations ahead. ngA11y is most useful for

  1. focus management on route changes, and
  2. accessible form validation
and ngAria is most useful for applications that have a lot of clickable elements that need simple keyboard handling that maps directly to the mouse clicks.

Final Changes: Color Contrast and Visual Focus

There is no magic to the color contrast and visual focus CSS changes, just choose some colors that work and change the CSS. For the visual focus indicator, first remove all instances of outline-style : none; from the CSS. I also made some other small adjustments to the button that removes completed todo items to make the hover and focus state pass the color contrast test as well. If you are a UI designer (with actual talent), you could spend more time making the application look a lot better than I did.

Angular.js Accessibility – Name, Role, State and Value

This is the sixth part of the blog series on angular.js accessibility. In a prior post, we published a list of accessibility issues in the Angular.js TodoMVC application. With the exception of the color contrast and visual focus styling issues, the remainder of the accessibility issues have to do with missing labels, missing role information, missing accessible name information (or association) and missing state information. These issues are not particularly unique to Angular.js, but lets fix them anyway.

Label for the New Todo Input

The visible label is being provided by the placeholder. This means there is not really a label for screen reader users and the visible label disappears when the field receives focus - making it difficult for users with cognitive disabilities to use. Admittedly, in this application, with one input field for one purpose this might not apply but we should maintain good habits. The technique I am going to apply is the floating labels technique and has been thoroughly documented, so I will simply show the code and not go into a detailed description of the reason(s) for the code.

First we add the label after the input, associate it correctly and remove the placeholder:


<form id="todo-form" ng-submit="addTodo()" novalidate>
<input id="new-todo" ng-model="newTodo" ng-disabled="saving" required aria-required="true">
<label for="new-todo">What needs to be done?</label>
</form>
Now we add the CSS to style the label and float it when appropriate

#todo-form {
position:relative;
}

#todo-form label {
position:absolute;
top: 24px;
left: 60px;
width: 100%;
font-size:20px;
opacity: 0.8;
}

#todo-form input:focus + label,
#todo-form input:valid + label {
top: 0;
left: 60px;
width: 100%;
transition: 0.2s;
bottom: 100%;
margin-bottom: -16px;
font-size:10px;
}
We also remove the, now redundant, CSS styling for the placeholder

The Wannabe Radio Buttons

The filter controls are actually acting like a radio button grouping but do not have that role and do not convey their state information. There is also no group label for them. First lets fix the HTML markup


<span id="filterheading">Show: </span>
<ul id="filters" role="radiogroup" aria-labelledby="filterheading">
<li role="presentation">
<a data-nga11y-focus ng-class="{selected: status == ''} " role="radio" aria-checked="{{status === '' ? true : false}}" href="#/">All</a>
</li>
<li role="presentation">
<a ng-class="{selected: status == 'active'}" role="radio" aria-checked="{{status === 'active' ? true : false}}" href="#/active">Active</a>
</li>
<li role="presentation">
<a ng-class="{selected: status == 'completed'}" role="radio" aria-checked="{{status === 'completed' ? true : false}}" href="#/completed">Completed</a>
</li>
</ul>
First we add a group label with an id, then we add the role of radiogroup to the unordered list that contains the controls and associate the group label with it, then we add the role of radio to each control within the group and add logic to set the aria-checked attribute to true or false appropriately. Finally, we add a role of presentation to each of the list item elements so that screen readers will count the radio buttons within the group correctly and also announce their selected state. In this markup, the list has become totally superfluous and we could just use some divs or spans to achieve the same thing.

Last change to make it look good is to fix the CSS to style everything nicely by making the list item display:inline.

As coded here, the group label will not work properly on all platforms when tabbing into the group. If you are looking for markup that will work everywhere right now, you could use aria-labelledby to associate the group label AND the text of the control with each control.

Properly Marked-up Todo

The problem with lists of complex controls like the list of todo items containing a checkbox, the tod item itself and the delete button, is that once the list gets long, the screen reader user may forget the order of the checkbox, the delete button and the item itself and for example, think that the checkbox belongs to the previous item. This is especially true when the user is new to the application or if the user is an infrequent user of the application. To prevent this, we should properly label and associate all the elements with one another. Its pretty easy to do, here is the changed markup:


<div class="view">
<input class="toggle" type="checkbox" ng-model="todo.completed" ng-change="toggleCompleted(todo)" aria-labelledby="{{'todo-' + $index}}" title="Check to mark as completed">
<a role="button" id="{{'todo-' + $index}}" class="todoitem" aria-describedby="clicktoedit" ng-click="editTodo(todo)" todo-focus="mustFocus(todo)">{{todo.title}} <span class="visuallyhidden">{{todo.completed ? 'Completed' : ''}}</span></a>
<button class="destroy" ng-click="removeTodo(todo)" aria-label="Delete" aria-describedby="{{'todo-' + $index}}"></button>
</div>
The first change is to add the state of the todo to the todo itself this allows us to convey to screen readers, the information that the strikethrough font is conveying to sighted users. We do this by adding a span tag with the class of "visuallyhidden" containing the text "Completed" if the todo item has been completed and nothing if it has not. Then we add the CSS to hide that text from sighted users because the strikethrough styling conveys this information already. Here is the visuallyhidden CSS:

.visuallyhidden {
position: absolute !important;
height: 1px; width: 1px;
overflow: hidden;
clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
clip: rect(1px, 1px, 1px, 1px);
clip-path: polygon(0px 0px, 0px 0px,0px 0px, 0px 0px);
}

Now we use the todo item itself as the label for the checkbox that is used to toggle the complete/incomplete state. This will convey the name of the todo item, the complete/incomplete state information as well as the checkbox's state and role information correctly. We also add a hint to the checkbox using the title attribute to supply more information about what it is used for.

Finally, we add an aria-describedby relationship between the delete button and the todo item so that that relationship is explicit. You may have noticed in a previous update that the todo item itself also received an aria-describedby relationship to associate it with the instructions on how to edit the todo item. Even though this is technically not an accessibility issue, it does make the application easier to use because a screen reader user does not have to traverse all the way to the bottom of the page to find the instructions.

Main Event

Finally, we add the role of "main" to the section containing the todos to convey its role and also allow screen reader users to navigate directly to it using landmark navigation.

References

A running version of the application with these changes can be found on my github.io page. The changes for this bog post can be found in the commit log and the entire project in my fork of the TodoMVC repository.

Angular.js Accessibility – Deleting a Todo

This is the fifth part of the blog series on angular.js accessibility. In a prior post, we published a list of accessibility issues in the Angular.js TodoMVC application. One of the issues is that the delete button is not keyboard accessible and does not have an accessible name. The code changes for this blog post are in my fork of the TodoMVC repository.

How to Delete?

In order to make the delete functionality work and be usable for all users on all devices (including touch devices which do not have a hover state), we should make the delete button visible at all times, we should add a label to the delete button for screen readers and we should add support for the DELETE key.

Doing Keyboard Handlers Right

The immediate candidate for the DELETE handler code is to extend the existing todoEscape directive to handle the delete key and delete the current todo item. The current todoEscape handler takes some code as a value that gets executed when the ESC key is pressed. If the handler is to be able to deal with more than one command, it needs to be more tightly coupled with the controller. Its scope will need to be un-isolated by adding the scope: true property to the directive and then we will need to change the ESC key handling to call scope.revertEdits(scope.todo), call scope.$apply() and then focus the todo anchor. This then allows us to add code for the DELETE key that calls scope.removeTodo(scope.todo) and then calls scope.$apply().

Moving up the Chain

At this point it should be obvious that the keyboard handler is attached to the wrong element and it is now only possible to delete the todo item when it is being edited. The keyboard functionality should really be part of the entire todo item. It also makes sense to rename the directive to "todoKbd". So lets do that and attach it to the containing <li> element.

Deja Vu All Over Again

The result of this is that you can delete the todo item when any part of it has focus and the list will get updated. The focus gets lost, however because the current item no longer exists and the code does not contain any logic to deal with this scenario.

Where Does Focus Management Belong

There are some scenarios where a simple "focus capturing" directive, using existing scope variables and existing functions might suffice. There are also scenarios (like route changes) where the ngA11y focus directive can be useful to determine which element should grab focus when a route change results focus loss. Then there are the situations the TodoMVC application illustrates - custom dynamic controls and complex single page applications. A good solution for the more complex scenario is to have the controller manage the focus in combination with directives.

First, the removeTodo function should do a bit of magic to determine which todo item should receive the focus after a delete (the next item if one exists, otherwise the previous one). We do this by setting the focusTodo scope variable.


$scope.removeTodo = function (todo) {
// Make sure that the next (or previous) todo will inherit the focus
var index = todos.indexOf(todo);
if (index === todos.length - 1) {
$scope.focusTodo = todos[index - 1];
} else {
$scope.focusTodo = todos[index + 1];
}
// delete the todo
store.delete(todo);
};
(If you have been following the blog posting series, you will note in this code that we have renamed the scope variable lastEditedTodo to focusTodo to be more representative of its expanded role.)

Now lets add a function to the controller that will help us determine which (if any) of the todo items needs to receive focus at any given point in time:


$scope.mustFocus = function (todo) {
var val = todo === $scope.focusTodo && (
!document.activeElement ||
document.activeElement.nodeName === 'BODY' ||
angular.element(document.activeElement).hasClass('edit'));
return val;
};
What this code says is: if the todo item passed into the function is the one that has been assigned to the focusTodo scope variable and the focus has been lost (i.e. there is no element on the page that has focus, or the element that has focus is the BODY element) or the element has just been edited (the focus is still on the input), then the item passed in should be focused (return true) otherwise the item should not be focussed (return false).

Now, combining this with the existing todoFocus directive: todo-focus="mustFocus(todo)" will result in the most appropriate todo item receiving focus after a save, a reverted edit, or a delete.

Looking at the remaining code, there is some ugly code that tries to eliminate duplicate saves when an item that is being edited gets saved through a submit action, which then also results in a blur event firing. This code can be cleaned up by creating a blurTodo() method that decides what to do when the input item is blurred. This can then do a simple check to see whether the item has already been saved and only call save if this is not the case.


$scope.blurTodo = function (todo) {
if (todo !== $scope.editedTodo) {
return;
}
$scope.saveEdits(todo);
};

Cleaning Up

Finally we need to make sure that our event handlers are cleaned up when items get destroyed, so we add a $destroy listener and remove the keydown handler when that gets called. Here is the full code of the todoKbd directive.


angular.module('todomvc')
.directive('todoKbd', function () {
'use strict';

var ESCAPE_KEY = 27;
var TAB_KEY = 9;
var DELETE_KEY = 46;

return {
restrict: 'A',
scope: true,
link: function (scope, elem, attrs) {
elem.on('keydown', function (event) {
var focus = false;
if (!scope.editedTodo && event.keyCode !== DELETE_KEY) {
return;
}
if (event.keyCode === ESCAPE_KEY) {
scope.revertEdits(scope.todo);
focus = true;
} else if (event.keyCode === DELETE_KEY) {
scope.removeTodo(scope.todo);
}
scope.$apply();
if (focus) {
elem[0].querySelector('a').focus();
}
}).on('$destroy', function () {
elem.off('keydown');
});
}
};
});

Takeaway

The big takeaway from the last couple of blog posts is that when developing a complex Angular.js application, you must consider keyboard handling and focus management early in the development process. Retro-fitting this functionality onto an existing application will be more disruptive and expensive than in an imperative framework like jQuery because of the highly structured nature of the controllers, directives and their scopes. The TodoMVC application is relatively simple but the changes required to make the editing and deletion keyboard accessible have resulted in changes to a significant percentage of the application's source code. You can imagine how much more disruptive it might have been had the todo list been part of a larger application with additional interactive components.

Doing keyboard accessibility early in the development process also means that your wireframes and interaction designs must include mouse, touch, keyboard and voice interaction (if applicable) that takes focus management into account.

Angular.js Accessibility – Editing a Todo with the Keyboard

This is the fourth part of the blog series on angular.js accessibility. In the previous post, we listed the issues still remaining with the TodoMVC application after fixing some basic focus management and dynamic update announcement issues. One of the issues is that the todos have to be double-clicked in order to be edited. This double click action is not possible with the keyboard AND the item itself is not keyboard focusable (which makes the inability to double-click it somewhat of a moot point). The code changes for this blog post are in my fork of the TodoMVC repository.

ngAria to the rescue

First thought was to use ngAria because that attempts to add keyboard accessibility wherever mouse interactivity can be deduced. So I added a dependency on ngAria and installed the module. The effect of this was to make the elements keyboard focusable in Firefox but it did not add any keyboard interaction for the ng-dblclick attribute. It also exposed a bug in Chrome, where Chrome would refuse to focus the <label> element being used for the todo item's text despite the fact that it had tabindex="0". So I changed ng-dblclick to ng-click (and made appropriate changes to the rest of the page) and changed the <label> to a <a>. These two changes allowed me to leverage the fact that ngAria makes the todo keyboard focusable, but including a whole module just for tabindex="0" seems like overkill. I'm leaving it in for now to see whether it brings any value further down the line.

Focus on Focus

These changes made the item able to be edited with the keyboard but exposed another deficiency - toggling between the editable and uneditable state caused the focus to get lost.

I was not joking when I listed focus management as number one of the four core problems that are somewhat unique to Angular.js in answer to the stackoverflow question: What are the accessibility implications of using a framework like angularjs?. The reason is aptly illustrated by this example. The author of the application has attempted to implement the application in an "angular.js idiomatic" fashion i.e. using directives that are small and independent and can possibly be re-used many times. So for example the todo-focus directive decides when to put focus into an element when the value of the directive attribute resolves to true.


angular.module('todomvc')
.directive('todoFocus', function todoFocus($timeout) {
'use strict';

return function (scope, elem, attrs) {
scope.$watch(attrs.todoFocus, function (newVal) {
if (newVal) {
$timeout(function () {
elem[0].focus();
}, 0, false);
}
});
};
});

As used in this application, it is intended to do this when the user "clicks" the item and it works very well in that it does in fact capture the focus when the item is clicked (or double-clicked in the original). Here is the code that calls "editTodo" on double click:


<label ng-dblclick="editTodo(todo)">{{todo.title}}</label>

Here is the editTodo code, which sets the "editedTodo" scope variable:


$scope.editTodo = function (todo) {
$scope.editedTodo = todo;
// Clone the original todo to restore it on demand.
$scope.originalTodo = angular.extend({}, todo);
};

Here is the code, which sets the "editing" class on the <li> container, causing the <input> element to be shown


<li ng-repeat="todo in todos | filter:statusFilter track by $index" ng-class="{completed: todo.completed, editing: todo == editedTodo}">

Finally, here is the code that executes the directive on the <input> element itself, causing it to grab focus:


<input class="edit" ng-trim="false" ng-model="todo.title" todo-escape="revertEdits(todo)" ng-blur="saveEdits(todo, 'blur')" todo-focus="todo == editedTodo">
You could easily imagine using this directive in many places to determine when something should grab focus (in fact I use it later on to try to fix the focus loss problem). That is the strength and the promise of Angular.js directives.

The second directive is the todo-escape directive which adds a keyboard event listener that listens for the ESC key to be pressed to revert (cancel) the editing.


angular.module('todomvc')
.directive('todoEscape', function () {
'use strict';

var ESCAPE_KEY = 27;

return function (scope, elem, attrs) {
elem.bind('keydown', function (event) {
if (event.keyCode === ESCAPE_KEY) {
scope.$apply(attrs.todoEscape);
}
});
};
});
As applied on the <input> element below:

<input class="edit" ng-trim="false" ng-model="todo.title" todo-escape="revertEdits(todo)" ng-blur="saveEdits(todo, 'blur')" todo-focus="todo == editedTodo">
this causes the revertEdits function to be called:

$scope.revertEdits = function (todo) {
todos[todos.indexOf(todo)] = $scope.originalTodo;
$scope.editedTodo = null;
$scope.originalTodo = null;
$scope.reverted = true;
};
which sets editedTodo to null, causing the CSS to hide the <input> element (causing focus to be lost). Once again, this escape directive might be used in many contexts to cancel something - a dialog box might be another legitimate use of this directive. However what happens now is that the isolated nature of this directive means that it does not know anything about the fact that it is being used to hide the element that currently has focus and it inadvertently leads to a focus management issue.

The third piece is the standard Angular.js ng-submit directive which causes the ENTER key acts as a form submission. As used in the original application, this saves the new todo text, clears the scope variables, which leads to the class change on the containing list item, which hides the input element and loses the focus. Once again, a re-usable directive that leads to an unintended consequence. The fourth item is the todoCtrl controller itself - which contains the callbacks passed as parameters to some of these other directives and contains the scope variables.

Whither my clean design?

In order to maintain focus in this scenario we have to make some changes. To get the focus to go back to the anchor tag after the edit is complete, we can add another variable to the controller's scope. Lets call this lastEditedTodo and then use the todo-focus directive to set focus back to the anchor tag once edit changes have been saved. This is an elegant solution - using one of our re-usable directives to solve our problem. Here is the HTML markup:


<a role="button" class="todoitem" aria-describedby="clicktoedit" ng-click="editTodo(todo)" todo-focus="todo == lastEditedTodo">{{todo.title}}</a>
and there are two places that the lastEditedTodo scope variable is set, once in the saveEdits function and once in the revertEdits function.

This works well but leaves the scenario where the ESC key still loses focus. Fixing this issue is not as nice. The code that causes the input edit to disappear on ESC is scope.$apply(attrs.todoEscape); which executes the attribute values in the DOM for all inputs that are configured as follows todo-escape="revertEdits(todo)". Calling $apply like that calls the revertEdits function which sets editedTodo scope variable to null:


$scope.revertEdits = function (todo) {
todos[todos.indexOf(todo)] = $scope.originalTodo;
$scope.editedTodo = null;
$scope.originalTodo = null;
$scope.reverted = true;
};

Which then causes the "editing" class to be removed from the parent <li> element (as shown above) which in turn causes the <input> element to be hidden by CSS, causing focus to be lost. Very declarative and very "Angulary" and a total nightmare for something like focus management. For a more detailed description as to why simply placing $scope.lastEditedTodo = todo; inside revertEdits will not work, see the side notes.

To fix this focus management issue, we can either totally refactor the code or we can add an ugly dependency between the todo-escape directive and the actual HTML markup as follows:


var par = elem.eq(0).parent().parent()[0];
par.querySelector('a').focus();
i.e. look for the enclosing <li> element, find the anchor within it and focus that anchor. YUCK!!!! But it works and is minimally invasive and might be good enough (I have a suspicion that when we get around to making the delete button work, we may revisit this decision)...

That is it! I did remove the following lines of code


if (todo.title === $scope.originalTodo.title) {
return;
}
from the saveEdits function because they caused inconsistent escape functionality and constitute a relatively unnecessary optimization that could be performed elsewhere.

Acknowledgements

Thanks to Marcy Sutton for helping me make the explanation more understandable. It sure helps to have another set of eyes on something...

Side Notes

Why does the addition of $scope.lastEditedTodo = todo; inside revertEdits not work? This has to do with the digest loop and how the todo-escape directive has been written. The directive uses $apply to execute the code that is the value of the directive attribute. In this example that is todo-escape="revertEdits(todo)", which calls the revertEdits function. This in turn updates the scope and these scope updates do not get applied because they happen inside the call to $apply. See this article on $apply within $apply issuesfor an explanation of why. The bottom line is that we need to change the todo-escape directive in some way that makes it dependent on either the DOM or the todoCtrl controller, which makes it less standalone and less elegant.

You may have heard that one of the main reasons for using Angular.js is because of the clean nature of declarative markup. That looking at the page allows you to immediately see what is going on. The above example of all the things that are going on during the edit toggle as described above shows how this does not have to hold true. You have to know how three directives, one controller, the HTML and the CSS all interact to really understand what is going on. I am sure there are better ways to write the TodoMVC application using Angular but use of Angular itself does not guarantee an easy to understand application per se.

Angular.js Accessibility – The Woeful TodoMVC Application

This is part three in a series of posts on Angular.js accessibility. In the first post, I fixed a focus management issue, and in the I showed how to do accessible dynamic update announcements.

One of the biggest problems with Angular.js accessibility is the fact that pretty much ALL of the examples out there are deficient in the most basic accessibility. the TodoMVC application is particularly bad. Even after the two problems I have already fixed, the application shows 18 issues when analyzed with FireEyes and actually suffers from many more that automated testing cannot detect. It is difficult to write an application that is as inaccessible as this one is and to make one that is less accessible, you would actually have to try really hard.

Highlight of the accessibility issues

This is a list of the most egregious of the accessibility issues:

Issue Type Description
Color Contrast All of the text on the page does not pass the color contrast ratio. The contrast ratio ranges from 1.19:1 to 2.1:1 for most of the UI, with the "better" parts of the page having a ratio of 3.78:1. The recent updates to the TodoMVC styles (see the references) have not made things any better. It is a good thing that the names of the contributors have a contrast ratio of only 1.95:1 because that makes it less likely we will be able to identify them and subject them to public ridicule.
Missing Labels None of the input fields have proper label associations.
Visible focus Many of the interactive controls have no visual focus.
Keyboard operability The "delete" buttons can only receive keyboard focus when the item with which they are associated is moused over, which makes the button unusable for a keyboard only user.
Accessible name The delete button does not have an accessible name, so if a screen reader user could get to it, they would not know what it does.
Information association The association of the checkbox, the todo text and the delete button is not explicit
Device independence You have to double-click a todo item to be able to edit it. Of course, this along with the fact that the element you have to click is not keyboard focusable, makes it impossible for keyboard-only users to perform this action.
Instructions The instructions on how to interact with the elements are not programmatically associated with the controls and are also not located within close proximity.
State When an item is completed, that state is not communicated in a non-visual manner.
Role and State The filters are acting like a radio button group but are not marked up as a radio button group, the group has no heading and the currently selected item is only being conveyed visually.
Semantic Markup There are sections with headers and footers but there is no main (this is a best practice for this page because of its simplicity)
In the next set of blog postings Marcy Sutton and I will be showing you how to fix them.

References

Pascal Hartig's Tweet on TodoMVC style updates