|
| 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 interact with web-based software applications. |
| 5 | +Also, designing an application to be more accessible generally improves the user experience for all users. |
| 6 | + |
| 7 | +For an in-depth introduction to issues and techniques for designing accessible applications, see Google's web.dev [Learn Accessibility](https://web.dev/learn/accessibility/) course. |
| 8 | + |
| 9 | +This page discusses best practices for designing Angular applications that work well for all users, including those who rely on assistive technologies. |
| 10 | + |
| 11 | +## Accessibility attributes |
| 12 | + |
| 13 | +<!-- TODO: add link once attribute binding guide is implemented --> |
| 14 | +Building accessible web experience often involves setting [Accessible Rich Internet Applications \(ARIA\) attributes](https://web.dev/learn/accessibility/aria-html/) to provide semantic meaning where it might otherwise be missing. |
| 15 | +Use attribute binding template syntax to control the values of accessibility-related attributes. |
| 16 | + |
| 17 | +When binding to ARIA attributes in Angular, you must use the `attr.` prefix. The ARIA specification depends specifically on HTML attributes rather than properties of DOM elements. |
| 18 | + |
| 19 | +<docs-code language="html"> |
| 20 | +<!-- Use attr. when binding to an ARIA attribute --> |
| 21 | +<button [attr.aria-label]="myActionLabel">…</button> |
| 22 | +</docs-code> |
| 23 | + |
| 24 | +Note: This syntax is only necessary for attribute *bindings*. |
| 25 | +Static ARIA attributes require no extra syntax. |
| 26 | + |
| 27 | +<docs-code language="html"> |
| 28 | +<!-- Static ARIA attributes require no extra syntax --> |
| 29 | +<button aria-label="Save document">…</button> |
| 30 | +</docs-code> |
| 31 | + |
| 32 | +HELPFUL: By convention, HTML attributes use lowercase names \(`tabindex`\), while properties use camelCase names \(`tabIndex`\). |
| 33 | + |
| 34 | +<!-- TODO: add link once attribute binding guide implemented --> |
| 35 | +See the [Binding syntax guide](guide/templates) for more background on the difference between attributes and properties. |
| 36 | + |
| 37 | +## Angular UI components |
| 38 | + |
| 39 | +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. |
| 40 | +The [Component Development Kit (CDK)](https://material.angular.io/cdk/categories) includes the `a11y` package that provides tools to support various areas of accessibility. |
| 41 | +For example: |
| 42 | + |
| 43 | +* `LiveAnnouncer` is used to announce messages for screen-reader users using an `aria-live` region. |
| 44 | + 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). |
| 45 | + |
| 46 | +* The `cdkTrapFocus` directive traps Tab-key focus within an element. |
| 47 | + Use it to create accessible experience for components such as modal dialogs, where focus must be constrained. |
| 48 | + |
| 49 | +For full details of these and other tools, see the [Angular CDK accessibility overview](https://material.angular.io/cdk/a11y/overview). |
| 50 | + |
| 51 | +### Augmenting native elements |
| 52 | + |
| 53 | +Native HTML elements capture several standard interaction patterns that are important to accessibility. |
| 54 | +When authoring Angular components, you should re-use these native elements directly when possible, rather than re-implementing well-supported behaviors. |
| 55 | + |
| 56 | +For example, instead of creating a custom element for a new variety of button, create a component that uses an attribute selector with a native `<button>` element. |
| 57 | +This most commonly applies to `<button>` and `<a>`, but can be used with many other types of element. |
| 58 | + |
| 59 | +You can see examples of this pattern in Angular Material: |
| 60 | +[`MatButton`](https://github.com/angular/components/blob/main/src/material/button/button.ts#L33C3-L36C5), [`MatTabNav`](https://github.com/angular/components/blob/main/src/material/tabs/tab-nav-bar/tab-nav-bar.ts#L62), and [`MatTable`](https://github.com/angular/components/blob/main/src/material/table/table.ts#L40). |
| 61 | + |
| 62 | +### Using containers for native elements |
| 63 | + |
| 64 | +Sometimes using the appropriate native element requires a container element. |
| 65 | +For example, the native `<input>` element cannot have children, so any custom text entry components need to wrap an `<input>` with extra elements. |
| 66 | +By just including `<input>` in your custom component's template, it's impossible for your component's users to set arbitrary properties and attributes to the `<input>` element. |
| 67 | +Instead, create a container component that uses content projection to include the native control in the component's API. |
| 68 | + |
| 69 | +You can see [`MatFormField`](https://material.angular.io/components/form-field/overview) as an example of this pattern. |
| 70 | + |
| 71 | +## Case study: Building a custom progress bar |
| 72 | + |
| 73 | +The following example shows how to make a progress bar accessible by using host binding to control accessibility-related attributes. |
| 74 | + |
| 75 | +* The component defines an accessibility-enabled element with both the standard HTML attribute `role`, and ARIA attributes. |
| 76 | + The ARIA attribute `aria-valuenow` is bound to the user's input. |
| 77 | +* In the template, the `aria-label` attribute ensures that the control is accessible to screen readers. |
| 78 | + |
| 79 | +<docs-code-multifile> |
| 80 | + <docs-code |
| 81 | + path="adev/src/content/examples/accessibility/src/app/progress-bar.component.ts" |
| 82 | + language="ts" |
| 83 | + linenums |
| 84 | + highlight="[12, 20]"/> |
| 85 | + <docs-code |
| 86 | + path="adev/src/content/examples/accessibility/src/app/app.component.html" |
| 87 | + language="html" |
| 88 | + linenums |
| 89 | + highlight="[8, 9]"/> |
| 90 | +</docs-code-multifile> |
| 91 | + |
| 92 | +## Routing |
| 93 | + |
| 94 | +### Focus management after navigation |
| 95 | + |
| 96 | +Tracking and controlling [focus](https://web.dev/learn/accessibility/focus/) in a UI is an important consideration in designing for accessibility. |
| 97 | +When using Angular routing, you should decide where page focus goes upon navigation. |
| 98 | + |
| 99 | +To avoid relying solely on visual cues, you need to make sure your routing code updates focus after page navigation. |
| 100 | +Use the `NavigationEnd` event from the `Router` service to know when to update focus. |
| 101 | + |
| 102 | +The following example shows how to find and focus the main content header in the DOM after navigation. |
| 103 | + |
| 104 | +<docs-code language="typescript"> |
| 105 | + |
| 106 | +router.events.pipe(filter(e => e instanceof NavigationEnd)).subscribe(() => { |
| 107 | + const mainHeader = document.querySelector('#main-content-header') |
| 108 | + if (mainHeader) { |
| 109 | + mainHeader.focus(); |
| 110 | + } |
| 111 | +}); |
| 112 | + |
| 113 | +</docs-code> |
| 114 | + |
| 115 | +In a real application, the element that receives focus depends on your specific application structure and layout. |
| 116 | +The focused element should put users in a position to immediately move into the main content that has just been routed into view. |
| 117 | +You should avoid situations where focus returns to the `body` element after a route change. |
| 118 | + |
| 119 | +### Active links identification |
| 120 | + |
| 121 | +CSS classes applied to active `RouterLink` elements, such as `RouterLinkActive`, provide a visual cue to identify the active link. |
| 122 | +Unfortunately, a visual cue doesn't help blind or visually impaired users. |
| 123 | +Applying the `aria-current` attribute to the element can help identify the active link. |
| 124 | +For more information, see [Mozilla Developer Network \(MDN\) aria-current](https://developer.mozilla.org/docs/Web/Accessibility/ARIA/Attributes/aria-current)). |
| 125 | + |
| 126 | +The `RouterLinkActive` directive provides the `ariaCurrentWhenActive` input which sets the `aria-current` to a specified value when the link becomes active. |
| 127 | + |
| 128 | +The following example shows how to apply the `active-page` class to active links as well as setting their `aria-current` attribute to `"page"` when they are active: |
| 129 | + |
| 130 | +```html |
| 131 | + <nav> |
| 132 | + <a routerLink="home" |
| 133 | + routerLinkActive="active-page" |
| 134 | + ariaCurrentWhenActive="page"> |
| 135 | + Home |
| 136 | + </a> |
| 137 | + <a routerLink="about" |
| 138 | + routerLinkActive="active-page" |
| 139 | + ariaCurrentWhenActive="page"> |
| 140 | + About |
| 141 | + </a> |
| 142 | + <a routerLink="shop" |
| 143 | + routerLinkActive="active-page" |
| 144 | + ariaCurrentWhenActive="page"> |
| 145 | + Shop |
| 146 | + </a> |
| 147 | + </nav> |
| 148 | +``` |
| 149 | + |
| 150 | +<!-- vale Angular.Angular_Spelling = NO --> |
| 151 | + |
| 152 | +## More information |
| 153 | + |
| 154 | +* [Accessibility - Google Web Fundamentals](https://developers.google.com/web/fundamentals/accessibility) |
| 155 | +* [ARIA specification and authoring practices](https://www.w3.org/TR/wai-aria) |
| 156 | +* [Material Design - Accessibility](https://material.io/design/usability/accessibility.html) |
| 157 | +* [Smashing Magazine](https://www.smashingmagazine.com/search/?q=accessibility) |
| 158 | +* [Inclusive Components](https://inclusive-components.design) |
| 159 | +* [Accessibility Resources and Code Examples](https://dequeuniversity.com/resources) |
| 160 | +* [W3C - Web Accessibility Initiative](https://www.w3.org/WAI/people-use-web) |
| 161 | +* [Rob Dodson A11ycasts](https://www.youtube.com/watch?v=HtTyRajRuyY) |
| 162 | +* [Angular ESLint](https://github.com/angular-eslint/angular-eslint#functionality) provides linting rules that can help you make sure your code meets accessibility standards. |
| 163 | + |
| 164 | +<!-- vale Angular.Angular_Spelling = YES --> |
| 165 | + |
| 166 | +Books |
| 167 | + |
| 168 | +<!-- vale Angular.Google_Quotes = NO --> |
| 169 | + |
| 170 | +* "A Web for Everyone: Designing Accessible User Experiences," Sarah Horton and Whitney Quesenbery |
| 171 | +* "Inclusive Design Patterns," Heydon Pickering |
| 172 | + |
| 173 | +<!-- vale Angular.Google_Quotes = YES --> |
0 commit comments