diff --git a/core/src/components/toast/readme.md b/core/src/components/toast/readme.md index 45f36de3c7..ead2432bd7 100644 --- a/core/src/components/toast/readme.md +++ b/core/src/components/toast/readme.md @@ -59,6 +59,30 @@ interface ToastOptions { interface ToastAttributes extends JSXBase.HTMLAttributes {} ``` +## 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 `aria-live="polite"` and `aria-atomic="true"` set by default. + +`aria-live` causes screen readers to announce the content of the toast when it is presented. However, since the attribute is set to `'polite'`, screen readers generally do not interrupt the current task. Developers can customize this behavior by using the `htmlAttributes` property to set `aria-live` to `'assertive'`. This will cause screen readers to immediately notify the user when a toast is presented, potentially interrupting any previous updates. + +`aria-atomic="true"` is set to ensure that the entire toast is announced as a single unit. This is useful when dynamically updating the content of the toast as it prevents screen readers from announcing only the content that has changed. + +### 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 [ion-alert](./alert) instead. + +* Avoid opening multiple toasts in quick succession. If `aria-live` is set to `'assertive'`, screen readers may interrupt the reading of the current task to announce the new toast, causing the context of the previous toast to be lost. + +* For toasts with long messages, consider adjusting the `duration` property to allow users enough time to read the content of the toast. + diff --git a/core/src/components/toast/test/a11y/index.html b/core/src/components/toast/test/a11y/index.html new file mode 100644 index 0000000000..90903f742c --- /dev/null +++ b/core/src/components/toast/test/a11y/index.html @@ -0,0 +1,33 @@ + + + + + Toast - a11y + + + + + + + + + +
+

Toast - a11y

+ + + +
+
+ + + diff --git a/core/src/components/toast/test/a11y/toast.e2e.ts b/core/src/components/toast/test/a11y/toast.e2e.ts new file mode 100644 index 0000000000..ff1cf44d1d --- /dev/null +++ b/core/src/components/toast/test/a11y/toast.e2e.ts @@ -0,0 +1,48 @@ +import { newE2EPage } from '@stencil/core/testing'; +import { AxePuppeteer } from '@axe-core/puppeteer'; + +describe('toast accessibility tests', () => { + test('it should not have any axe violations with polite toasts', async () => { + const page = await newE2EPage({ + url: '/src/components/toast/test/a11y?ionic:_testing=true' + }); + + const ionToastDidPresent = await page.spyOnEvent('ionToastDidPresent'); + + await page.click('#polite'); + + await ionToastDidPresent.next(); + + /** + * IonToast overlays the entire screen, so + * Axe will be unable to verify color contrast + * on elements under the toast. + */ + const results = await new AxePuppeteer(page) + .disableRules('color-contrast') + .analyze(); + expect(results.violations.length).toEqual(0); + }); + + test('it should not have any axe violations with assertive toasts', async () => { + const page = await newE2EPage({ + url: '/src/components/toast/test/a11y?ionic:_testing=true' + }); + + const ionToastDidPresent = await page.spyOnEvent('ionToastDidPresent'); + + await page.click('#assertive'); + + await ionToastDidPresent.next(); + + /** + * IonToast overlays the entire screen, so + * Axe will be unable to verify color contrast + * on elements under the toast. + */ + const results = await new AxePuppeteer(page) + .disableRules('color-contrast') + .analyze(); + expect(results.violations.length).toEqual(0); + }); +}); diff --git a/core/src/components/toast/toast.tsx b/core/src/components/toast/toast.tsx index 6d519a35f4..1131d3770a 100644 --- a/core/src/components/toast/toast.tsx +++ b/core/src/components/toast/toast.tsx @@ -280,6 +280,8 @@ export class Toast implements ComponentInterface, OverlayInterface { return (