@@ -12,14 +12,23 @@ assistive technologies used by persons with disabilities.
1212
1313##Including ngAria
1414
15- Using ngAria is as simple as requiring the ngAria module in your application. ngAria hooks into
15+ Using {@link ngAria ngAria} is as simple as requiring the ngAria module in your application. ngAria hooks into
1616standard AngularJS directives and quietly injects accessibility support into your application
1717at runtime.
1818
1919```js
2020angular.module('myApp', ['ngAria'])...
2121```
2222
23+ ###Using ngAria
24+ Most of what ngAria does is only visible "under the hood". To see the module in action, once you've
25+ added it as a dependency, you can test a few things:
26+ * Using your favorite element inspector, look for ngAria attributes in your own code.
27+ * Test using your keyboard to ensure `tabindex` is used correctly.
28+ * Fire up a screen reader such as VoiceOver to listen for ARIA support.
29+ [Helpful screen reader tips.](http://webaim.org/articles/screenreader_testing/)
30+
31+ ##Supported directives
2332Currently, ngAria interfaces with the following directives:
2433
2534 * <a href="#ngmodel">ngModel</a>
@@ -31,7 +40,7 @@ Currently, ngAria interfaces with the following directives:
3140
3241<h2 id="ngmodel">ngModel</h2>
3342
34- Most of ngAria's heavy lifting happens in the [ ngModel](https://docs.angularjs.org/api/ng/directive/ ngModel)
43+ Most of ngAria's heavy lifting happens in the {@link ngModel ngModel}
3544directive. For elements using ngModel, special attention is paid by ngAria if that element also
3645has a a role or type of `checkbox`, `radio`, `range` or `textbox`.
3746
@@ -47,15 +56,80 @@ attributes (if they have not been explicitly specified by the developer):
4756
4857###Example
4958
50- ```html
51- <md-checkbox ng-model="val" required>
52- ```
53-
54- Becomes:
55-
56- ```html
57- <md-checkbox ng-model="val" required aria-required="true" tabIndex="0">
58- ```
59+ <example module="ngAria_ngModelExample" deps="angular-aria.js">
60+ <file name="index.html">
61+ <style>
62+ [role=checkbox] {
63+ cursor: pointer;
64+ display: inline-block;
65+ }
66+ [role=checkbox] .icon:before {
67+ content: '\2610';
68+ display: inline-block;
69+ font-size: 2em;
70+ line-height: 1;
71+ vertical-align: middle;
72+ speak: none;
73+ }
74+ [role=checkbox].active .icon:before {
75+ content: '\2611';
76+ }
77+ pre {
78+ white-space: pre-wrap;
79+ }
80+ </style>
81+ <div>
82+ <form ng-controller="formsController">
83+ <some-checkbox role="checkbox" ng-model="checked" ng-class="{active: checked}"
84+ ng-disabled="isDisabled" ng-click="toggleCheckbox()"
85+ aria-label="Custom Checkbox" show-attrs>
86+ <span class="icon" aria-hidden="true"></span>
87+ Custom Checkbox
88+ </some-checkbox>
89+ </form>
90+ </div>
91+ <script>
92+ var app = angular.module('ngAria_ngModelExample', ['ngAria'])
93+ .controller('formsController', function($scope){
94+ $scope.checked = false;
95+ $scope.toggleCheckbox = function(){
96+ $scope.checked = !$scope.checked;
97+ }
98+ })
99+ .directive('someCheckbox', function(){
100+ return {
101+ restrict: 'E',
102+ link: function($scope, $el, $attrs) {
103+ $el.on('keypress', function(event){
104+ event.preventDefault();
105+ if(event.keyCode === 32 || event.keyCode === 13){
106+ $scope.toggleCheckbox();
107+ $scope.$apply();
108+ }
109+ });
110+ }
111+ }
112+ })
113+ .directive('showAttrs', function() {
114+ return function($scope, $el, $attrs) {
115+ var pre = document.createElement('pre');
116+ $el.after(pre);
117+ $scope.$watch(function() {
118+ var $attrs = {};
119+ Array.prototype.slice.call($el[0].attributes, 0).forEach(function(item) {
120+ if (item.name !== 'show-$attrs') {
121+ $attrs[item.name] = item.value;
122+ }
123+ });
124+ return $attrs;
125+ }, function(newAttrs, oldAttrs) {
126+ pre.textContent = JSON.stringify(newAttrs, null, 2);
127+ }, true);
128+ }
129+ });
130+ </script>
131+ </file>
132+ </example>
59133
60134ngAria will also add `tabIndex`, ensuring custom elements with these roles will be reachable from
61135the keyboard. It is still up to **you** as a developer to **ensure custom controls will be
@@ -106,6 +180,24 @@ screen reader users won't accidentally focus on "mystery elements". Managing tab
106180child control can be complex and affect performance, so it’s best to just stick with the default
107181`display: none` CSS. See the [fourth rule of ARIA use](http://www.w3.org/TR/aria-in-html/#fourth-rule-of-aria-use).
108182
183+ ###Example
184+ ```css
185+ .ng-hide {
186+ display: block;
187+ opacity: 0;
188+ }
189+ ```
190+ ```html
191+ <div ng-show="false" class="ng-hide" aria-hidden="true"></div>
192+ ```
193+
194+ Becomes:
195+
196+ ```html
197+ <div ng-show="true" aria-hidden="false"></div>
198+ ```
199+ *Note: Child links, buttons or other interactive controls must also be removed from the tab order.*
200+
109201<h2 id="nghide">ngHide</h2>
110202
111203>The [ngHide](https://docs.angularjs.org/api/ng/directive/ngHide) directive shows or hides the
@@ -116,33 +208,128 @@ The default CSS for `ngHide`, the inverse method to `ngShow`, makes ngAria redun
116208`aria-hidden` on the directive when it is hidden or shown, but the content is already hidden with
117209`display: none`. See explanation for <a href="#ngshow">ngShow</a> when overriding the default CSS.
118210
119- <h2 id="ngclick">ngClick and ngDblClick</h2>
120- If `ngClick` or `ngDblClick` is encountered, ngAria will add `tabIndex` if it isn't there already.
121- Even with this, you must currently still add `ng-keypress` to non-interactive elements such as
122- `<div>` or `<taco-button>` to enable keyboard access. Conversation is
123- [currently ongoing](https://github.com/angular/angular.js/issues/9254) about whether ngAria
124- should also bind `ng-keypress` to be more useful.
211+ <h2 id="ngclick-and-ngdblclick">ngClick and ngDblclick</h2>
212+ If `ng-click` or `ng-dblclick` is encountered, ngAria will add `tabindex` if it isn't there already.
213+ Even with this, you must currently still add `ng-keypress` to non-interactive elements such as `div`
214+ or `taco-button` to enable keyboard access. Conversation is currently ongoing about whether ngAria
215+ should also bind `ng-keypress`.
216+
217+ <h3>Example</h3>
218+ ```html
219+ <div ng-click="toggleMenu()"></div>
220+ ```
221+
222+ Becomes:
223+ ```html
224+ <div ng-click="toggleMenu()" tabindex="0"></div>
225+ ```
226+ *Note: ngAria still requires `ng-keypress` to be added manually to non-native controls like divs.*
125227
126228<h2 id="ngmessages">ngMessages</h2>
127229
128230The new ngMessages module makes it easy to display form validation or other messages with priority
129231sequencing and animation. To expose these visual messages to screen readers,
130232ngAria injects `aria-live="polite"`, causing them to be read aloud any time a message is shown,
131233regardless of the user's focus location.
234+ ###Example
235+
236+ ```html
237+ <div ng-messages="myForm.myName.$error">
238+ <div ng-message="required">You did not enter a field</div>
239+ <div ng-message="maxlength">Your field is too long</div>
240+ </div>
241+ ```
132242
133- * * *
243+ Becomes:
244+
245+ ```html
246+ <div ng-messages="myForm.myName.$error" aria-live="polite">
247+ <div ng-message="required">You did not enter a field</div>
248+ <div ng-message="maxlength">Your field is too long</div>
249+ </div>
250+ ```
134251
135252##Disabling attributes
136253The attribute magic of ngAria may not work for every scenario. To disable individual attributes,
137- you can use the {@link ngAria.$ariaProvider#config config} method:
138-
139- ```
140- angular.module('myApp', ['ngAria'], function config($ariaProvider) {
141- $ariaProvider.config({
142- tabindex: false
254+ you can use the {@link ngAria.$ariaProvider#config config} method. Just keep in mind this will
255+ tell ngAria to ignore the attribute globally.
256+
257+ <example module="ngAria_ngDisabledExample" deps="angular-aria.js">
258+ <file name="index.html">
259+ <style>
260+ [role=checkbox] {
261+ cursor: pointer;
262+ display: inline-block;
263+ }
264+ [role=checkbox] .icon:before {
265+ content: '\2610';
266+ display: inline-block;
267+ font-size: 2em;
268+ line-height: 1;
269+ vertical-align: middle;
270+ speak: none;
271+ }
272+ [role=checkbox].active .icon:before {
273+ content: '\2611';
274+ }
275+ </style>
276+ <form ng-controller="formsController">
277+ <div ng-model="someModel" show-attrs>
278+ Div with ngModel and aria-invalid disabled
279+ </div>
280+ <div role="checkbox" ng-model="checked" ng-class="{active: checked}"
281+ aria-label="Custom Checkbox" ng-click="toggleCheckbox()" some-checkbox show-attrs>
282+ <span class="icon" aria-hidden="true"></span>
283+ Custom Checkbox for comparison
284+ </div>
285+ </form>
286+ <script>
287+ angular.module('ngAria_ngDisabledExample', ['ngAria'], function config($ariaProvider) {
288+ $ariaProvider.config({
289+ ariaInvalid: false,
290+ tabindex: true
291+ });
292+ })
293+ .controller('formsController', function($scope){
294+ $scope.checked = false;
295+ $scope.toggleCheckbox = function(){
296+ $scope.checked = !$scope.checked;
297+ }
298+ })
299+ .directive('someCheckbox', function(){
300+ return {
301+ restrict: 'A',
302+ link: function($scope, $el, $attrs) {
303+ $el.on('keypress', function(event){
304+ event.preventDefault();
305+ if(event.keyCode === 32 || event.keyCode === 13){
306+ $scope.toggleCheckbox();
307+ $scope.$apply();
308+ }
309+ });
310+ }
311+ }
312+ })
313+ .directive('showAttrs', function() {
314+ return function(scope, el, attrs) {
315+ var pre = document.createElement('pre');
316+ el.after(pre);
317+ scope.$watch(function() {
318+ var attrs = {};
319+ Array.prototype.slice.call(el[0].attributes, 0).forEach(function(item) {
320+ if (item.name !== 'show-attrs') {
321+ attrs[item.name] = item.value;
322+ }
323+ });
324+ return attrs;
325+ }, function(newAttrs, oldAttrs) {
326+ pre.textContent = JSON.stringify(newAttrs, null, 2);
327+ }, true);
328+ }
143329 });
144- });
145- ```
330+ </script>
331+ </file>
332+ </example>
146333
147334##Common Accessibility Patterns
148335
@@ -171,6 +358,7 @@ Accessibility best practices that apply to web apps in general also apply to Ang
171358
172359 * [Using ARIA in HTML](http://www.w3.org/TR/aria-in-html/)
173360 * [AngularJS Accessibility at ngEurope](https://www.youtube.com/watch?v=dmYDggEgU-s&list=UUEGUP3TJJfMsEM_1y8iviSQ)
361+ * [Testing with Screen Readers](http://webaim.org/articles/screenreader_testing/)
174362 * [Chrome Accessibility Developer Tools](https://chrome.google.com/webstore/detail/accessibility-developer-t/fpkknkljclfencbdbgkenhalefipecmb?hl=en)
175363 * [W3C Accessibility Testing](http://www.w3.org/wiki/Accessibility_testing)
176364 * [WebAIM](http://webaim.org)
0 commit comments