From a47e79fd372f977e45b449fd652f03618ed4d744 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Fri, 4 Aug 2023 13:53:16 -0400 Subject: [PATCH] docs(api): add accessibility section for new htmlAttributes property (#3064) --- docs/api/action-sheet.md | 147 ++++++++++++++++++++++++++++++++++++- docs/api/alert.md | 152 ++++++++++++++++++++++++++++++++++++++- docs/api/toast.md | 129 +++++++++++++++++++++++++++------ 3 files changed, 403 insertions(+), 25 deletions(-) diff --git a/docs/api/action-sheet.md b/docs/api/action-sheet.md index a11e669c194..8d6844e3d3e 100644 --- a/docs/api/action-sheet.md +++ b/docs/api/action-sheet.md @@ -1,6 +1,9 @@ --- title: "ion-action-sheet" --- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + import Props from '@ionic-internal/component-api/v7/action-sheet/props.md'; import Events from '@ionic-internal/component-api/v7/action-sheet/events.md'; import Methods from '@ionic-internal/component-api/v7/action-sheet/methods.md'; @@ -94,10 +97,150 @@ import CssCustomProperties from '@site/static/usage/v7/action-sheet/theming/css- ## Accessibility +### Screen Readers + +Action Sheets set aria properties in order to be [accessible](../reference/glossary#a11y) to screen readers, but these properties can be overridden if they aren't descriptive enough or don't align with how the action sheet is being used in an app. + +#### Role + Action Sheets are given a `role` of [`dialog`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/dialog_role). In order to align with the ARIA spec, either the `aria-label` or `aria-labelledby` attribute must be set. +#### Action Sheet Description + It is strongly recommended that every Action Sheet have the `header` property defined, as Ionic will automatically set `aria-labelledby` to point to the header element. However, if you choose not to include a `header`, an alternative is to use the `htmlAttributes` property to provide a descriptive `aria-label` or set a custom `aria-labelledby` value. + + + + +```javascript +const actionSheet = await this.actionSheetController.create({ + htmlAttributes: { + 'aria-label': 'action sheet dialog', + }, +}); +``` + + + + + +```javascript +const actionSheet = await this.actionSheetController.create({ + htmlAttributes: { + 'aria-label': 'action sheet dialog', + }, +}); +``` + + + + + +```javascript +useIonActionSheet({ + htmlAttributes: { + 'aria-label': 'action sheet dialog', + }, +}); +``` + + + + + +```javascript +const actionSheet = await actionSheetController.create({ + htmlAttributes: { + 'aria-label': 'action sheet dialog', + }, +}); +``` + + + + + +#### Action Sheet Buttons Description + +Buttons containing text will be read by a screen reader. If a button contains only an icon, or a description other than the existing text is desired, a label should be assigned to the button by passing `aria-label` to the `htmlAttributes` property on the button. + + + + + +```javascript +const actionSheet = await this.actionSheetController.create({ + header: 'Header', + buttons: [ + { + icon: 'close', + htmlAttributes: { + 'aria-label': 'close', + }, + }, + ], +}); +``` + + + + + +```javascript +const actionSheet = await this.actionSheetController.create({ + header: 'Header', + buttons: [ + { + icon: 'close', + htmlAttributes: { + 'aria-label': 'close', + }, + }, + ], +}); +``` + + + + + +```javascript +useIonActionSheet({ + header: 'Header', + buttons: [ + { + icon: 'close', + htmlAttributes: { + 'aria-label': 'close', + }, + }, + ], +}); +``` + + + + + +```javascript +const actionSheet = await actionSheetController.create({ + header: 'Header', + buttons: [ + { + icon: 'close', + htmlAttributes: { + 'aria-label': 'close', + }, + }, + ], +}); +``` + + + + + ## Interfaces ### ActionSheetButton @@ -108,6 +251,8 @@ interface ActionSheetButton { role?: 'cancel' | 'destructive' | 'selected' | string; icon?: string; cssClass?: string | string[]; + id?: string; + htmlAttributes?: { [key: string]: any }; handler?: () => boolean | void | Promise; data?: T; } @@ -150,4 +295,4 @@ interface ActionSheetOptions { ## Slots - \ No newline at end of file + diff --git a/docs/api/alert.md b/docs/api/alert.md index f6e5d5ee581..e645cf2f397 100644 --- a/docs/api/alert.md +++ b/docs/api/alert.md @@ -111,25 +111,173 @@ import Customization from '@site/static/usage/v7/alert/customization/index.md'; ## Accessibility +### Screen Readers + +Alerts set aria properties in order to be [accessible](../reference/glossary#a11y) to screen readers, but these properties can be overridden if they aren't descriptive enough or don't align with how the alert is being used in an app. + +#### Role + Ionic automatically sets the Alert's `role` to either [`alertdialog`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/alertdialog_role) if there are any inputs or buttons included, or [`alert`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/alert_role) if there are none. +#### Alert Description + If the `header` property is defined for the Alert, the `aria-labelledby` attribute will be automatically set to the header's ID. The `subHeader` element will be used as a fallback if `header` is not defined. Similarly, the `aria-describedby` attribute will be automatically set to the ID of the `message` element if that property is defined. It is strongly recommended that your Alert have a `message`, as well as either a `header` or `subHeader`, in order to align with the ARIA spec. If you choose not to include a `header` or `subHeader`, an alternative is to provide a descriptive `aria-label` using the `htmlAttributes` property. + + + + +```javascript +const alert = await this.alertController.create({ + message: 'This is an alert with custom aria attributes.', + htmlAttributes: { + 'aria-label': 'alert dialog', + }, +}); +``` + + + + + +```javascript +const alert = await this.alertController.create({ + message: 'This is an alert with custom aria attributes.', + htmlAttributes: { + 'aria-label': 'alert dialog', + }, +}); +``` + + + + + +```javascript +useIonAlert({ + message: 'This is an alert with custom aria attributes.', + htmlAttributes: { + 'aria-label': 'alert dialog', + }, +}); +``` + + + + + +```javascript +const alert = await alertController.create({ + message: 'This is an alert with custom aria attributes.', + htmlAttributes: { + 'aria-label': 'alert dialog', + }, +}); +``` + + + + + + All ARIA attributes can be manually overwritten by defining custom values in the `htmlAttributes` property of the Alert. +#### Alert Buttons Description + +Buttons containing text will be read by a screen reader. If a description other than the existing text is desired, a label can be set on the button by passing `aria-label` to the `htmlAttributes` property on the button. + + + + + +```javascript +const alert = await this.alertController.create({ + header: 'Header', + buttons: [ + { + text: 'Exit', + htmlAttributes: { + 'aria-label': 'close', + }, + }, + ], +}); +``` + + + + + +```javascript +const alert = await this.alertController.create({ + header: 'Header', + buttons: [ + { + text: 'Exit', + htmlAttributes: { + 'aria-label': 'close', + }, + }, + ], +}); +``` + + + + + +```javascript +useIonAlert({ + header: 'Header', + buttons: [ + { + text: 'Exit', + htmlAttributes: { + 'aria-label': 'close', + }, + }, + ], +}); +``` + + + + + +```javascript +const alert = await alertController.create({ + header: 'Header', + buttons: [ + { + text: 'Exit', + htmlAttributes: { + 'aria-label': 'close', + }, + }, + ], +}); +``` + + + + ## Interfaces ### AlertButton ```typescript +type AlertButtonOverlayHandler = boolean | void | { [key: string]: any }; + interface AlertButton { text: string; role?: 'cancel' | 'destructive' | string; cssClass?: string | string[]; - handler?: (value: any) => boolean | void | {[key: string]: any}; + id?: string; + htmlAttributes?: { [key: string]: any }; + handler?: (value: any) => AlertButtonOverlayHandler | Promise; } ``` @@ -199,4 +347,4 @@ interface AlertOptions { ## Slots - \ No newline at end of file + diff --git a/docs/api/toast.md b/docs/api/toast.md index 10a785c2086..ea4cf7a7126 100644 --- a/docs/api/toast.md +++ b/docs/api/toast.md @@ -86,6 +86,112 @@ import ThemingPlayground from '@site/static/usage/v7/toast/theming/index.md'; +## Accessibility + +### Focus Management + +Toasts are intended to be subtle notifications and are not intended to interrupt the user. User interaction should not be required to dismiss the toast. As a result, focus is not automatically moved to a toast when one is presented. + +### Screen Readers + +Toasts set aria properties in order to be [accessible](../reference/glossary#a11y) to screen readers, but these properties can be overridden if they aren't descriptive enough or don't align with how the toast is being used in an app. + +#### Role + +`ion-toast` has `role="status"` and `aria-live="polite"` set on the inner `.toast-content` element. This causes screen readers to only announce the toast message and header. Buttons and icons will not be announced when the toast is presented. + +`aria-live` causes screen readers to announce the content of the toast when it is updated. However, since the attribute is set to `'polite'`, screen readers should not interrupt the current task. + +Since toasts are intended to be subtle notification, `aria-live` should never be set to `"assertive"`. If developers need to interrupt the user with an important message, we recommend using an [alert](./alert). + +#### Toast Buttons Description + +Buttons containing text will be read by a screen reader when they are interacted with. If a button contains only an icon, or a description other than the existing text is desired, a label should be assigned to the button by passing `aria-label` to the `htmlAttributes` property on the button. + + + + + +```javascript +const toast = await this.toastController.create({ + header: 'Header', + buttons: [ + { + icon: 'close', + htmlAttributes: { + 'aria-label': 'close', + }, + }, + ], +}); +``` + + + + + +```javascript +const toast = await this.toastController.create({ + header: 'Header', + buttons: [ + { + icon: 'close', + htmlAttributes: { + 'aria-label': 'close', + }, + }, + ], +}); +``` + + + + + +```javascript +useIonToast({ + header: 'Header', + buttons: [ + { + icon: 'close', + htmlAttributes: { + 'aria-label': 'close', + }, + }, + ], +}); +``` + + + + + +```javascript +const toast = await toastController.create({ + header: 'Header', + buttons: [ + { + icon: 'close', + htmlAttributes: { + 'aria-label': 'close', + }, + }, + ], +}); +``` + + + + + +### Tips + +While this is not a complete list, here are some guidelines to follow when using toasts. + +* Do not require user interaction to dismiss toasts. For example, having a "Dismiss" button in the toast is fine, but the toast should also automatically dismiss on its own after a timeout period. If you need user interaction for a notification, consider using an [alert](./alert) instead. + +* For toasts with long messages, consider adjusting the `duration` property to allow users enough time to read the content of the toast. + ## Interfaces ### ToastButton @@ -97,6 +203,7 @@ interface ToastButton { side?: 'start' | 'end'; role?: 'cancel' | string; cssClass?: string | string[]; + htmlAttributes?: { [key: string]: any }; handler?: () => boolean | void | Promise; } ``` @@ -126,28 +233,6 @@ interface ToastOptions { } ``` -## Accessibility - -### Focus Management - -Toasts are intended to be subtle notifications and are not intended to interrupt the user. User interaction should not be required to dismiss the toast. As a result, focus is not automatically moved to a toast when one is presented. - -### Screen Readers - -`ion-toast` has `role="status"` and `aria-live="polite"` set on the inner `.toast-content` element. This causes screen readers to only announce the toast message and header. Buttons and icons will not be announced. - -`aria-live` causes screen readers to announce the content of the toast when it is updated. However, since the attribute is set to `'polite'`, screen readers should not interrupt the current task. - -Since toasts are intended to be subtle notification, `aria-live` should never be set to `"assertive"`. If developers need to interrupt the user with an important message, we recommend using an [alert](./alert). - -### Tips - -While this is not a complete list, here are some guidelines to follow when using toasts. - -* Do not require user interaction to dismiss toasts. For example, having a "Dismiss" button in the toast is fine, but the toast should also automatically dismiss on its own after a timeout period. If you need user interaction for a notification, consider using an [alert](./alert) instead. - -* For toasts with long messages, consider adjusting the `duration` property to allow users enough time to read the content of the toast. - ## Properties