diff --git a/docs/api/title.md b/docs/api/title.md index 1f807c23599..3bfedc891e8 100644 --- a/docs/api/title.md +++ b/docs/api/title.md @@ -57,7 +57,7 @@ When creating headings, we typically recommend using [semantic heading elements To achieve this, developers should use the [`heading` role](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/heading_role) on Title. This will indicate to assistive technologies that Title is a type of heading. From there, developers should use the [`aria-level` attribute](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-level) to set the heading level. -For example, if we wanted to make a Title behave like an `h1` element, we would set `role="heading"` and `aria-level="1"` on the Title. +For example, if we wanted to make a Title behave like an `h1` element, we would set `role="heading"` and `aria-level="1"` on the Title. This is necessary when using the [Focus Manager](../developing/managing-focus#assistive-technology-focus-management). Since multiple Title elements can be used on a view in conjunction with semantic heading elements, Ionic does not automatically set the Title's `role` or `aria-level`. It is the responsibility of the developer to handle this. diff --git a/docs/developing/config.md b/docs/developing/config.md index 389033b4cca..02ac03567e2 100644 --- a/docs/developing/config.md +++ b/docs/developing/config.md @@ -162,43 +162,44 @@ class AppComponent { Below are the config options that Ionic uses. -| Config | Type | Description | -| --------------------------- | --------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `actionSheetEnter` | `AnimationBuilder` | Provides a custom enter animation for all `ion-action-sheet`, overriding the default "animation". | -| `actionSheetLeave` | `AnimationBuilder` | Provides a custom leave animation for all `ion-action-sheet`, overriding the default "animation". | -| `alertEnter` | `AnimationBuilder` | Provides a custom enter animation for all `ion-alert`, overriding the default "animation". | -| `alertLeave` | `AnimationBuilder` | Provides a custom leave animation for all `ion-alert`, overriding the default "animation". | -| `animated` | `boolean` | If `true`, Ionic will enable all animations and transitions across the app. | -| `backButtonDefaultHref` | `string` | Overrides the default value for the `defaultHref` property in all `` components. | -| `backButtonIcon` | `string` | Overrides the default icon in all `` components. | -| `backButtonText` | `string` | Overrides the default text in all `` components. | -| `innerHTMLTemplatesEnabled` | `boolean` | Relevant Components: `ion-alert`, `ion-infinite-scroll-content`, `ion-loading`, `ion-refresher-content`, `ion-toast`. If `true`, content passed to the relevant components will be parsed as HTML instead of plaintext. Defaults to `false`. | -| `hardwareBackButton` | `boolean` | If `true`, Ionic will respond to the hardware back button in an Android device. | -| `infiniteLoadingSpinner` | `SpinnerTypes` | Overrides the default spinner type in all `` components. | -| `loadingEnter` | `AnimationBuilder` | Provides a custom enter animation for all `ion-loading`, overriding the default "animation". | -| `loadingLeave` | `AnimationBuilder` | Provides a custom leave animation for all `ion-loading`, overriding the default "animation". | -| `loadingSpinner` | `SpinnerTypes` | Overrides the default spinner for all `ion-loading` overlays. | -| `menuIcon` | `string` | Overrides the default icon in all `` components. | -| `menuType` | `string` | Overrides the default menu type for all `` components. | -| `modalEnter` | `AnimationBuilder` | Provides a custom enter animation for all `ion-modal`, overriding the default "animation". | -| `modalLeave` | `AnimationBuilder` | Provides a custom leave animation for all `ion-modal`, overriding the default "animation". | -| `mode` | `Mode` | The mode determines which platform styles to use for the whole application. | -| `navAnimation` | `AnimationBuilder` | Overrides the default "animation" of all `ion-nav` and `ion-router-outlet` across the whole application. | -| `pickerEnter` | `AnimationBuilder` | Provides a custom enter animation for all `ion-picker`, overriding the default "animation". | -| `pickerLeave` | `AnimationBuilder` | Provides a custom leave animation for all `ion-picker`, overriding the default "animation". | -| `platform` | [`PlatformConfig`](/docs/angular/platform#customizing-platform-detection-methods) | Overrides the default platform detection methods. | -| `popoverEnter` | `AnimationBuilder` | Provides a custom enter animation for all `ion-popover`, overriding the default "animation". | -| `popoverLeave` | `AnimationBuilder` | Provides a custom leave animation for all `ion-popover`, overriding the default "animation". | -| `refreshingIcon` | `string` | Overrides the default icon in all `` components. | -| `refreshingSpinner` | `SpinnerTypes` | Overrides the default spinner type in all `` components. | -| `rippleEffect` | `boolean` | If `true`, Material Design ripple effects will be enabled across the app. | -| `sanitizerEnabled` | `boolean` | If `true`, Ionic will enable a basic DOM sanitizer on component properties that accept custom HTML. | -| `spinner` | `SpinnerTypes` | Overrides the default spinner in all `` components. | -| `statusTap` | `boolean` | If `true`, clicking or tapping the status bar will cause the content to scroll to the top. | -| `swipeBackEnabled` | `boolean` | If `true`, Ionic will enable the "swipe-to-go-back" gesture across the application. | -| `tabButtonLayout` | `TabButtonLayout` | Overrides the default "layout" of all `ion-bar-button` across the whole application. | -| `toastDuration` | `number` | Overrides the default `duration` for all `ion-toast` components. | -| `toastEnter` | `AnimationBuilder` | Provides a custom enter animation for all `ion-toast`, overriding the default "animation". | -| `toastLeave` | `AnimationBuilder` | Provides a custom leave animation for all `ion-toast`, overriding the default "animation". | -| `toggleOnOffLabels` | `boolean` | Overrides the default `enableOnOffLabels` in all `ion-toggle` components. | -| `experimentalCloseWatcher` | `boolean` | If `true`, the [CloseWatcher API](https://github.com/WICG/close-watcher) will be used to handle all Escape key and hardware back button presses to dismiss menus and overlays and to navigate. Note that the `hardwareBackButton` config option must also be `true`. (experimental) | +| Config | Type | Description | +| --------------------------- | --------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `actionSheetEnter` | `AnimationBuilder` | Provides a custom enter animation for all `ion-action-sheet`, overriding the default "animation". | +| `actionSheetLeave` | `AnimationBuilder` | Provides a custom leave animation for all `ion-action-sheet`, overriding the default "animation". | +| `alertEnter` | `AnimationBuilder` | Provides a custom enter animation for all `ion-alert`, overriding the default "animation". | +| `alertLeave` | `AnimationBuilder` | Provides a custom leave animation for all `ion-alert`, overriding the default "animation". | +| `animated` | `boolean` | If `true`, Ionic will enable all animations and transitions across the app. | +| `backButtonDefaultHref` | `string` | Overrides the default value for the `defaultHref` property in all `` components. | +| `backButtonIcon` | `string` | Overrides the default icon in all `` components. | +| `backButtonText` | `string` | Overrides the default text in all `` components. | +| `innerHTMLTemplatesEnabled` | `boolean` | Relevant Components: `ion-alert`, `ion-infinite-scroll-content`, `ion-loading`, `ion-refresher-content`, `ion-toast`. If `true`, content passed to the relevant components will be parsed as HTML instead of plaintext. Defaults to `false`. | +| `hardwareBackButton` | `boolean` | If `true`, Ionic will respond to the hardware back button in an Android device. | +| `infiniteLoadingSpinner` | `SpinnerTypes` | Overrides the default spinner type in all `` components. | +| `loadingEnter` | `AnimationBuilder` | Provides a custom enter animation for all `ion-loading`, overriding the default "animation". | +| `loadingLeave` | `AnimationBuilder` | Provides a custom leave animation for all `ion-loading`, overriding the default "animation". | +| `loadingSpinner` | `SpinnerTypes` | Overrides the default spinner for all `ion-loading` overlays. | +| `menuIcon` | `string` | Overrides the default icon in all `` components. | +| `menuType` | `string` | Overrides the default menu type for all `` components. | +| `modalEnter` | `AnimationBuilder` | Provides a custom enter animation for all `ion-modal`, overriding the default "animation". | +| `modalLeave` | `AnimationBuilder` | Provides a custom leave animation for all `ion-modal`, overriding the default "animation". | +| `mode` | `Mode` | The mode determines which platform styles to use for the whole application. | +| `navAnimation` | `AnimationBuilder` | Overrides the default "animation" of all `ion-nav` and `ion-router-outlet` across the whole application. | +| `pickerEnter` | `AnimationBuilder` | Provides a custom enter animation for all `ion-picker`, overriding the default "animation". | +| `pickerLeave` | `AnimationBuilder` | Provides a custom leave animation for all `ion-picker`, overriding the default "animation". | +| `platform` | [`PlatformConfig`](/docs/angular/platform#customizing-platform-detection-methods) | Overrides the default platform detection methods. | +| `popoverEnter` | `AnimationBuilder` | Provides a custom enter animation for all `ion-popover`, overriding the default "animation". | +| `popoverLeave` | `AnimationBuilder` | Provides a custom leave animation for all `ion-popover`, overriding the default "animation". | +| `refreshingIcon` | `string` | Overrides the default icon in all `` components. | +| `refreshingSpinner` | `SpinnerTypes` | Overrides the default spinner type in all `` components. | +| `rippleEffect` | `boolean` | If `true`, Material Design ripple effects will be enabled across the app. | +| `sanitizerEnabled` | `boolean` | If `true`, Ionic will enable a basic DOM sanitizer on component properties that accept custom HTML. | +| `spinner` | `SpinnerTypes` | Overrides the default spinner in all `` components. | +| `statusTap` | `boolean` | If `true`, clicking or tapping the status bar will cause the content to scroll to the top. | +| `swipeBackEnabled` | `boolean` | If `true`, Ionic will enable the "swipe-to-go-back" gesture across the application. | +| `tabButtonLayout` | `TabButtonLayout` | Overrides the default "layout" of all `ion-bar-button` across the whole application. | +| `toastDuration` | `number` | Overrides the default `duration` for all `ion-toast` components. | +| `toastEnter` | `AnimationBuilder` | Provides a custom enter animation for all `ion-toast`, overriding the default "animation". | +| `toastLeave` | `AnimationBuilder` | Provides a custom leave animation for all `ion-toast`, overriding the default "animation". | +| `toggleOnOffLabels` | `boolean` | Overrides the default `enableOnOffLabels` in all `ion-toggle` components. | +| `experimentalCloseWatcher` | `boolean` | **Experimental:** If `true`, the [CloseWatcher API](https://github.com/WICG/close-watcher) will be used to handle all Escape key and hardware back button presses to dismiss menus and overlays and to navigate. Note that the `hardwareBackButton` config option must also be `true`. | +| `focusManagerPriority` | [`FocusManagerPriority[]`](./managing-focus#types) | **Experimental:** When defined, Ionic will move focus to the appropriate element after each page transition. This ensures that users relying on assistive technology are informed when a page transition happens. Disabled by default. | diff --git a/docs/developing/managing-focus.md b/docs/developing/managing-focus.md index 3adcb726bbd..9859ed2fb78 100644 --- a/docs/developing/managing-focus.md +++ b/docs/developing/managing-focus.md @@ -13,6 +13,8 @@ import TabItem from '@theme/TabItem'; /> +## Manual Focus Management + Ionic provides a `setFocus` API on components such as [Input](../api/input), [Searchbar](../api/searchbar), and [Textarea](../api/textarea) that allows developers to manually set focus to an element. This API should be used in place of the `autofocus` attribute and called within: - The `ionViewDidEnter` lifecycle event for routing applications when a page is entered. @@ -20,18 +22,18 @@ Ionic provides a `setFocus` API on components such as [Input](../api/input), [Se - The `appload` event for vanilla JavaScript applications when the application loads. - The result of a user gesture or interaction. -## Why not autofocus? +### Why not autofocus? The `autofocus` attribute is a standard HTML attribute that allows developers to set focus to an element when a page loads. This attribute is commonly used to set focus to the first input element on a page. However, the `autofocus` attribute can cause issues in routing applications when navigating between pages. This is because the `autofocus` attribute will set focus to the element when the page loads, but will not set focus to the element when the page is revisited. Learn more about the `autofocus` attribute in the [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus). -## Platform Restrictions +### Platform Restrictions There are platform restrictions you should be aware of when using the `setFocus` API, including: 1. Android requires user interaction before setting focus to an element. This can be as simple as a user tapping on the screen. 2. Interactive elements can only focused a result of a user gesture on Mobile Safari (iOS), such as calling `setFocus` as the result of a button click. -## Basic Usage +### Basic Usage The example below demonstrates how to use the `setFocus` API to request focus on an input when the user clicks a button. @@ -39,7 +41,7 @@ import Basic from '@site/static/usage/v8/input/set-focus/index.md'; -## Routing +### Routing Developers can use the `ionViewDidEnter` lifecycle event to set focus to an element when a page is entered. @@ -126,7 +128,7 @@ export default Home; ```` -## Overlays +### Overlays Developers can use the `didPresent` lifecycle event to set focus to an element when an overlay is presented. @@ -236,3 +238,44 @@ export default Home; ```` + +## Assistive Technology Focus Management + +By default, Single Page Applications have no built-in way of informing screen readers that the active view has changed in a browser or webview. This means that users who rely on assistive technology do not always know if a navigation event occurred. + +Developers who enable the [focusManagerPriority config](./config#ionicconfig) can delegate focus management to Ionic during page transitions. When enabled, Ionic will move focus to the correct element as specified in the config option. This will inform screen readers that a navigation event occurred. + +### Types + +```typescript +type FocusManagerPriority = 'content' | 'heading' | 'banner'; +``` + +### Content Types + +The following table explains what each content type represents: + +| Type | Description | Ionic Component | Semantic HTML Equivalent | Landmark Equivalent | +| --------- | ----------------------------- | ------------------------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `content` | The main portion of the view. | [Content](../api/content) | [`main`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/main) | [`role="main"`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/Main_role) | +| `heading` | The title of the view. | [Title](../api/title) | [`h1`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h1) | [`role="heading"`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/heading_role) with [`aria-level="1"`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-level) | +| `banner` | The header of the view. | [Header](../api/header) | [`header`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/header) | [`role="banner"`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/Banner_Role) | + +:::important +Developers should assign `role="heading"` and `aria-level="1"` to the primary [Title](../api/title) on each view. Since multiple [Title](../api/title) components can be used in a single view, Ionic does not automatically assign these attributes. +::: + +### Specifying Priority + +The config should be specified in order of decreasing priority. In the following example, Ionic will always focus headings to start. Ionic only proceeds to the next focus priority, banner, if a view does not have a heading: + +```js +focusManagerPriority: ['heading', 'banner']; +``` + +### Implementation Notes + +- When specifying a focus priority, browsers may still move focus within that focus priority. For example, when specifying a `'content'` focus priority, Ionic will move focus to the content. However, the browser may then move focus within that content to the first focusable element such as a button. +- If none of the focus priorities are found in a view, Ionic will instead focus the view itself to ensure focus generally moves to the correct place. Browsers may then adjust focus within the view. +- When navigating from the current view to the previous view, Ionic will move focus back to the element that presented the current view. +- The focus manager can be overridden on a per-view basis as shown in [Manual Focus Management with Routing](#routing).