Marcy Sutton, Seattle, USA
Accessibility Engineer, Angular Core Team Member
Legend: waffle denotes a major topic in client-rendered app accessibility
<md-checkbox tabindex="0" ...
<md-checkbox tabindex="0"
ng-keypress="doStuff()" ...
Button
// should be:
<md-checkbox tabindex="0" role="checkbox" aria-checked="true">
Checkbox
</md-checkbox>
Menu
<md-radio-group ng-model="data.group3">
<md-radio-button ng-repeat="it in avatarData"
ng-value="it.value">
<md-icon md-svg-icon="{{it.id}}"></md-icon>
</md-radio-button>
</md-radio-group>
aria-label
is missing.
<md-radio-group ng-model="data.group3">
<md-radio-button ng-repeat="it in avatarData"
ng-value="it.value"
aria-label="{{it.title}}">
<md-icon md-svg-icon="{{it.id}}"></md-icon>
</md-radio-button>
</md-radio-group>
Boom! Labeled radio buttons.
<md-sidenav class="md-sidenav-right" md-component-id="right">
<md-content ng-controller="RightCtrl">
<form>
<md-input-container>
<label for="testInput">Test input</label>
<input type="text" id="testInput"
ng-model="data" md-sidenav-focus>
</md-input-container>
</form>
<md-button ng-click="close()" class="md-primary">
Close
</md-button>
</md-content>
</md-sidenav>
<aria-status role="status" aria-live="assertive">
<p ng-repeat="message in messages">{{message}}</p>
</aria-status>
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.module('app', ['ngAria'], function($ariaProvider) {
$ariaProvider.config({
ariaHidden: false
...
<body>
<!-- page content here -->
<script src="angular.min.js"></script>
<script src="angular-aria.js"></script>
</body>
<md-checkbox ng-disabled="true" aria-disabled="true"...
.directive('ngDisabled', ['$aria', function($aria) {
return $aria.$$watchExpr('ngDisabled', 'aria-disabled');
}])
<div ng-click="ohNoYouDidnt()"></div>
.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) {
scope.$apply(callback);
...
exports.config = {
plugins: [{
tenonIO: {
options: {
// options.src will be added by the test
},
printAll: false,
},
chromeA11YDevTools: true,
path: 'node_modules/protractor/plugins/accessiblity'
}]
}
Library | Pricing | API Key | External Request | No. of Tests |
---|---|---|---|---|
Chrome Accessibility Developer Tools | Free | No | No | 14 |
Tenon.io | Free limited accounts, paid subscriptions | Yes | Yes | 63 |
<button ng-focus="anticipationEvent()"
ng-click="deliverWaffles()" id="{{item.id}}">
Give me waffles </button>
<button (focus)="anticipationEvent()"
(click)="deliverWaffles()" [waffle-id]="item.id">
Give me waffles </button>
All links open in new windows