Accessible Angular.js

Accessibility is about people.

Tandem bicycling at CSUN 2015

Marcy Sutton, Seattle, USA
Accessibility Engineer, Angular Core Team Member

#AngularJS web apps are never accessible because even the books on it start with inaccessible code samples! Paul J. Adam (@pauljadam) January 2, 2015
#AngularJS web apps are never accessible because even the books on it start with inaccessible code samples! Paul J Adam January 2, 2015

& Me


  • Material Design & a11y waffle with happy cartoon cat
  • ngAria
  • Protractor
  • Angular 2

Legend: waffle denotes a major topic in client-rendered app accessibility

Material Design

Material Design for multi-screen consistency

Material Design for Angular 1.x

Angular Material docs website

Interactivity Happy waffle: Important Topic


Button Button Button
Dogs Cats Pigs

Reachable Controls

      <md-checkbox tabindex="0" ...

Add tabindex

Operable Controls

      <md-checkbox tabindex="0" 
                   ng-keypress="doStuff()" ...
Enable keyboard events

Semantics Happy waffle: Important Topic


        // should be:

ARIA Happy waffle: Important Topic

      <md-checkbox tabindex="0" role="checkbox" aria-checked="true">
Roles, states and properties

Text Alternatives Happy waffle: Important Topic




   Stop waffling and get on with it

Enforcing text alternatives

Angular Material Radio Buttons opens in a new window

Fixing ARIA warnings

  <md-radio-group ng-model="data.group3">
    <md-radio-button ng-repeat="it in avatarData"
        <md-icon md-svg-icon="{{}}"></md-icon>


aria-label is missing.

  <md-radio-group ng-model="data.group3">
    <md-radio-button ng-repeat="it in avatarData"
        <md-icon md-svg-icon="{{}}"></md-icon>


Boom! Labeled radio buttons.

Focus management Happy waffle: Important Topic

Focus management

  <md-sidenav class="md-sidenav-right" md-component-id="right">
    <md-content ng-controller="RightCtrl">
          <label for="testInput">Test input</label>
          <input type="text" id="testInput"
                 ng-model="data" md-sidenav-focus>
      <md-button ng-click="close()" class="md-primary">
Angular Material Sidenav with custom focus

Notifying the User Happy waffle: Important Topic

Angular Material Autocomplete with 4 Matches

Notifying the User (code)

  <aria-status role="status" aria-live="assertive">
    <p ng-repeat="message in messages">{{message}}</p>

  switch (self.matches.length) {
    case 0:  return 'There are no matches available.';
    case 1:  return 'There is 1 match available.';
    default: return 'There are ' + self.matches.length + ' matches available.';
Angular Material Autocomplete Source

Angular ❤ Open Source

Angular on Github


Accessibility Module in Angular 1.3+

Including ngAria

  angular.module('app', ['ngAria'], function($ariaProvider) {
      ariaHidden: false

      <!-- page content here -->
      <script src="angular.min.js"></script>
      <script src="angular-aria.js"></script>


Adds support to these directives:

  • ngModel
  • ngDisabled
  • ngShow
  • ngHide
  • ngClick
  • ngDblClick
  • ngMessages

ngAria & ngDisabled

  <md-checkbox ng-disabled="true" aria-disabled="true"...

  .directive('ngDisabled', ['$aria', function($aria) {
    return $aria.$$watchExpr('ngDisabled', 'aria-disabled');
ngAria Source for ngDisabled for ngDisabled opens in a new window


      <div ng-click="ohNoYouDidnt()"></div>

ngAria & ngClick

 .directive('ngClick',['$aria', function($aria) {
    return {
      compile: function(scope, elem, attr) {
        var nodeBlackList = ['BUTTON','A','INPUT','TEXTAREA','SELECT'];
        if (!isNodeOneOf(elem, nodeBlackList)) {
          if (!elem.attr('role') && config.buttonRole) {
            elem.attr('role', 'button');
          if (config.bindKeypress) {
            elem.on('keypress', function(event) {
              if (event.keyCode === 32 || event.keyCode === 13) {
              ... Opens in a new window

That's a lot of effort...

Just use buttons!

Happy waffle: Important Topic

Protractor: end-to-end testing for AngularJS

  • Node.js command line application
  • Runs on WebDriver
  • Choose your test framework
  • Great for continuous integration

Protractor A11Y Plugin

Test your site with:

Command Line Accessibility Tests Link opens in a new window

Protractor A11Y Plugin: Setup

  exports.config = {
    plugins: [{
      tenonIO: {
        options: {
          // options.src will be added by the test
        printAll: false,
      chromeA11YDevTools: true,
      path: 'node_modules/protractor/plugins/accessiblity'
Plugin Documentation Link opens in a new window

Protractor Accessibility

Can I use?

Library Pricing API Key External Request No. of Tests
Chrome Accessibility Developer Tools Free No No 14 Free limited accounts, paid subscriptions Yes Yes 63

Automated Testing Strategies

  • Check for Labels
  • Validate Roles
  • Watched ARIA Properties
  • Interactions
  • Color Contrast

Angular 2

Changes in Angular 2

Event bindings

      <button ng-focus="anticipationEvent()"
              ng-click="deliverWaffles()" id="{{}}">
        Give me waffles </button>
Angular 1: attributes

      <button (focus)="anticipationEvent()"
              (click)="deliverWaffles()" [waffle-id]="">
        Give me waffles </button>
Angular 2: properties

Angular 2 includes ARIA support

Angular 2 on Github

Contribute! Link opens in a new window


All links open in new windows

The End. waffle