|
| 1 | +# Accessibility in Angular |
| 2 | + |
| 3 | +The web is used by a wide variety of people, including those who have visual or motor impairments. |
| 4 | +A variety of assistive technologies are available that make it much easier for these groups to |
| 5 | +interact with web-based software applications. |
| 6 | +In addition, designing an application to be more accessible generally improves the user experience for all users. |
| 7 | + |
| 8 | +For an in-depth introduction to issues and techniques for designing accessible applications, see the [Accessibility](https://developers.google.com/web/fundamentals/accessibility/#what_is_accessibility) section of the Google's [Web Fundamentals](https://developers.google.com/web/fundamentals/). |
| 9 | + |
| 10 | +This page discusses best practices for designing Angular applications that |
| 11 | +work well for all users, including those who rely on assistive technologies. |
| 12 | + |
| 13 | +## Accessibility attributes |
| 14 | + |
| 15 | +Building accessible web experience often involves setting [ARIA attributes](https://developers.google.com/web/fundamentals/accessibility/semantics-aria) |
| 16 | +to provide semantic meaning where it might otherwise be missing. |
| 17 | +Use [attribute binding](guide/template-syntax#attribute-binding) template syntax to control the values of accessibility-related attributes. |
| 18 | + |
| 19 | +When binding to ARIA attributes in Angular, you must use the `attr.` prefix, as the ARIA |
| 20 | +specification depends specifically on HTML attributes rather than properties on DOM elements. |
| 21 | + |
| 22 | +```html |
| 23 | +<!-- Use attr. when binding to an ARIA attribute --> |
| 24 | +<button [attr.aria-label]="myActionLabel">...</button> |
| 25 | +``` |
| 26 | + |
| 27 | +Note that this syntax is only necessary for attribute _bindings_. |
| 28 | +Static ARIA attributes require no extra syntax. |
| 29 | + |
| 30 | +```html |
| 31 | +<!-- Static ARIA attributes require no extra syntax --> |
| 32 | +<button aria-label="Save document">...</button> |
| 33 | +``` |
| 34 | + |
| 35 | +NOTE: |
| 36 | + |
| 37 | +<div class="alert is-helpful"> |
| 38 | + |
| 39 | + By convention, HTML attributes use lowercase names (`tabindex`), while properties use camelCase names (`tabIndex`). |
| 40 | + |
| 41 | + See the [Template Syntax](https://angular.io/guide/template-syntax#html-attribute-vs-dom-property) guide for more background on the difference between attributes and properties. |
| 42 | + |
| 43 | +</div> |
| 44 | + |
| 45 | + |
| 46 | +## Angular UI components |
| 47 | + |
| 48 | +The [Angular Material](https://material.angular.io/) library, which is maintained by the Angular team, is a suite of reusable UI components that aims to be fully accessible. |
| 49 | +The [Component Development Kit (CDK)](https://material.angular.io/cdk/categories) includes the `a11y` package that provides tools to support various areas of accessibility. |
| 50 | +For example: |
| 51 | + |
| 52 | +* `LiveAnnouncer` is used to announce messages for screen-reader users using an `aria-live` region. See the W3C documentation for more information on [aria-live regions](https://www.w3.org/WAI/PF/aria-1.1/states_and_properties#aria-live). |
| 53 | + |
| 54 | +* The `cdkTrapFocus` directive traps Tab-key focus within an element. Use it to create accessible experience for components like modal dialogs, where focus must be constrained. |
| 55 | + |
| 56 | +For full details of these and other tools, see the [Angular CDK accessibility overview](https://material.angular.io/cdk/a11y/overview). |
| 57 | + |
| 58 | + |
| 59 | +### Augmenting native elements |
| 60 | + |
| 61 | +Native HTML elements capture a number of standard interaction patterns that are important to accessibility. |
| 62 | +When authoring Angular components, you should re-use these native elements directly when possible, rather than re-implementing well-supported behaviors. |
| 63 | + |
| 64 | +For example, instead of creating a custom element for a new variety of button, you can create a component that uses an attribute selector with a native `<button>` element. |
| 65 | +This most commonly applies to `<button>` and `<a>`, but can be used with many other types of element. |
| 66 | + |
| 67 | +You can see examples of this pattern in Angular Material: [`MatButton`](https://github.com/angular/components/blob/master/src/material/button/button.ts#L66-L68), [`MatTabNav`](https://github.com/angular/components/blob/master/src/material/tabs/tab-nav-bar/tab-nav-bar.ts#L67), [`MatTable`](https://github.com/angular/components/blob/master/src/material/table/table.ts#L17). |
| 68 | + |
| 69 | +### Using containers for native elements |
| 70 | + |
| 71 | +Sometimes using the appropriate native element requires a container element. |
| 72 | +For example, the native `<input>` element cannot have children, so any custom text entry components need |
| 73 | +to wrap an `<input>` with additional elements. |
| 74 | +While you might just include the `<input>` in your custom component's template, |
| 75 | +this makes it impossible for users of the component to set arbitrary properties and attributes to the input element. |
| 76 | +Instead, you can create a container component that uses content projection to include the native control in the |
| 77 | +component's API. |
| 78 | + |
| 79 | +You can see [`MatFormField`](https://material.angular.io/components/form-field/overview) as an example of this pattern. |
| 80 | + |
| 81 | +## Case study: Building a custom progress bar |
| 82 | + |
| 83 | +The following example shows how to make a simple progress bar accessible by using host binding to control accessibility-related attributes. |
| 84 | + |
| 85 | +* The component defines an accessibility-enabled element with both the standard HTML attribute `role`, and ARIA attributes. The ARIA attribute `aria-valuenow` is bound to the user's input. |
| 86 | + |
| 87 | + ```ts |
| 88 | + import { Component, Input } from '@angular/core'; |
| 89 | + /** |
| 90 | + * Example progressbar component. |
| 91 | + */ |
| 92 | + @Component({ |
| 93 | + selector: 'example-progressbar', |
| 94 | + template: `<div class="bar" [style.width.%]="value"></div>`, |
| 95 | + styleUrls: ['./progress-bar.css'], |
| 96 | + host: { |
| 97 | + // Sets the role for this component to "progressbar" |
| 98 | + role: 'progressbar', |
| 99 | + |
| 100 | + // Sets the minimum and maximum values for the progressbar role. |
| 101 | + 'aria-valuemin': '0', |
| 102 | + 'aria-valuemax': '0', |
| 103 | + |
| 104 | + // Binding that updates the current value of the progressbar. |
| 105 | + '[attr.aria-valuenow]': 'value', |
| 106 | + } |
| 107 | + }) |
| 108 | + export class ExampleProgressbar { |
| 109 | + /** Current value of the progressbar. */ |
| 110 | + @Input() value: number = 0; |
| 111 | + } |
| 112 | + ``` |
| 113 | + |
| 114 | +* In the template, the `aria-label` attribute ensures that the control is accessible to screen readers. |
| 115 | + |
| 116 | + ```html |
| 117 | + <label> |
| 118 | + Enter an example progress value |
| 119 | + <input type="number" min="0" max="100" |
| 120 | + [value]="progress" (input)="progress = $event.target.value"> |
| 121 | + </label> |
| 122 | + |
| 123 | + <!-- The user of the progressbar sets an aria-label to communicate what the progress means. --> |
| 124 | + <example-progressbar [value]="progress" aria-label="Example of a progress bar"> |
| 125 | + </example-progressbar> |
| 126 | + ``` |
| 127 | + |
| 128 | +[See the full example in StackBlitz](https://stackblitz.com/edit/angular-kn5jdi?file=src%2Fapp%2Fapp.component.html). |
| 129 | + |
| 130 | +## Routing and focus management |
| 131 | + |
| 132 | +Tracking and controlling [focus](https://developers.google.com/web/fundamentals/accessibility/focus/) in a UI is an important consideration in designing for accessibility. |
| 133 | +When using Angular routing, you should decide where page focus goes upon navigation. |
| 134 | + |
| 135 | +To avoid relying solely on visual cues, you need to make sure your routing code updates focus after page navigation. |
| 136 | +Use the `NavigationEnd` event from the `Router` service to know when to update |
| 137 | +focus. |
| 138 | + |
| 139 | +The following example shows how to find and focus the main content header in the DOM after navigation. |
| 140 | + |
| 141 | +```ts |
| 142 | + |
| 143 | +router.events.pipe(filter(e => e instanceof NavigationEnd)).subscribe(() => { |
| 144 | + const mainHeader = document.querySelector('#main-content-header') |
| 145 | + if (mainHeader) { |
| 146 | + mainHeader.focus(); |
| 147 | + } |
| 148 | +}); |
| 149 | + |
| 150 | +``` |
| 151 | +In a real application, the element that receives focus will depend on your specific |
| 152 | +application structure and layout. |
| 153 | +The focused element should put users in a position to immediately move into the main content that has just been routed into view. |
| 154 | +You should avoid situations where focus returns to the `body` element after a route change. |
| 155 | + |
| 156 | + |
| 157 | +## Additional resources |
| 158 | + |
| 159 | +* [Accessibility - Google Web Fundamentals](https://developers.google.com/web/fundamentals/accessibility) |
| 160 | + |
| 161 | +* [ARIA specification and authoring practices](https://www.w3.org/TR/wai-aria/) |
| 162 | + |
| 163 | +* [Material Design - Accessibility](https://material.io/design/usability/accessibility.html) |
| 164 | + |
| 165 | +* [Smashing Magazine](https://www.smashingmagazine.com/search/?q=accessibility) |
| 166 | + |
| 167 | +* [Inclusive Components](https://inclusive-components.design/) |
| 168 | + |
| 169 | +* [Accessibility Resources and Code Examples](https://dequeuniversity.com/resources/) |
| 170 | + |
| 171 | +* [W3C - Web Accessibility Initiative](https://www.w3.org/WAI/people-use-web/) |
| 172 | + |
| 173 | +* [Rob Dodson A11ycasts](https://www.youtube.com/watch?v=HtTyRajRuyY) |
| 174 | + |
| 175 | +* [Codelyzer](http://codelyzer.com/rules/) provides linting rules that can help you make sure your code meets accessibility standards. |
| 176 | + |
| 177 | +Books |
| 178 | + |
| 179 | +* "A Web for Everyone: Designing Accessible User Experiences", Sarah Horton and Whitney Quesenbery |
| 180 | + |
| 181 | +* "Inclusive Design Patterns", Heydon Pickering |
0 commit comments