diff --git a/.gitignore b/.gitignore index e34ae50b1dc..b5a133e84f9 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ yarn-debug.log* yarn-error.log* static/**/node_modules/ +.idea diff --git a/cspell-wordlist.txt b/cspell-wordlist.txt index 76d855bf4cf..873b109548d 100644 --- a/cspell-wordlist.txt +++ b/cspell-wordlist.txt @@ -13,6 +13,7 @@ Udemy Vetur Wistia WCAG +CDK actionsheet fabs @@ -85,6 +86,3 @@ webnative browserslistrc ionicframework -tappable -Overscroll -expressjs diff --git a/docs/angular/overlays.md b/docs/angular/overlays.md new file mode 100644 index 00000000000..2e4335effcd --- /dev/null +++ b/docs/angular/overlays.md @@ -0,0 +1,213 @@ +--- +title: Overlay Components +sidebar_label: Overlays +--- + + + Angular Overlay Components: Modals, Popovers with Custom Injectors + + + +Ionic provides overlay components such as modals and popovers that display content on top of your application. In Angular, these overlays can be created using controllers like `ModalController` and `PopoverController`. + +## Creating Overlays + +Overlays can be created programmatically using their respective controllers: + +```typescript +import { Component } from '@angular/core'; +import { ModalController } from '@ionic/angular/standalone'; +import { MyModalComponent } from './my-modal.component'; + +@Component({ + selector: 'app-home', + templateUrl: './home.component.html', +}) +export class HomeComponent { + constructor(private modalController: ModalController) {} + + async openModal() { + const modal = await this.modalController.create({ + component: MyModalComponent, + componentProps: { + title: 'My Modal', + }, + }); + await modal.present(); + } +} +``` + +## Custom Injectors + +By default, overlay components use the root injector for dependency injection. This means that services or tokens provided at the route level or within a specific component tree are not accessible inside the overlay. + +The `injector` option allows you to pass a custom Angular `Injector` when creating a modal or popover. This enables overlay components to access services and tokens that are not available in the root injector. + +### Use Cases + +Custom injectors are useful when you need to: + +- Access route-scoped services from within an overlay +- Use Angular CDK's `Dir` directive for bidirectional text support +- Access any providers that are not registered at the root level + +### Usage + +To use a custom injector, pass it to the `create()` method: + +```typescript +import { Component, Injector } from '@angular/core'; +import { ModalController } from '@ionic/angular/standalone'; +import { MyModalComponent } from './my-modal.component'; +import { MyRouteService } from './my-route.service'; + +@Component({ + selector: 'app-feature', + templateUrl: './feature.component.html', + providers: [MyRouteService], // Service provided at route level +}) +export class FeatureComponent { + constructor(private modalController: ModalController, private injector: Injector) {} + + async openModal() { + const modal = await this.modalController.create({ + component: MyModalComponent, + injector: this.injector, // Pass the component's injector + }); + await modal.present(); + } +} +``` + +The modal component can now inject `MyRouteService`: + +```typescript +import { Component, inject } from '@angular/core'; +import { MyRouteService } from '../my-route.service'; + +@Component({ + selector: 'app-my-modal', + templateUrl: './my-modal.component.html', +}) +export class MyModalComponent { + private myRouteService = inject(MyRouteService); +} +``` + +### Creating a Custom Injector + +You can also create a custom injector with specific providers: + +```typescript +import { Component, Injector } from '@angular/core'; +import { ModalController } from '@ionic/angular/standalone'; +import { MyModalComponent } from './my-modal.component'; +import { MyService } from './my.service'; + +@Component({ + selector: 'app-feature', + templateUrl: './feature.component.html', +}) +export class FeatureComponent { + constructor(private modalController: ModalController, private injector: Injector) {} + + async openModal() { + const myService = new MyService(); + myService.configure({ someOption: true }); + + const customInjector = Injector.create({ + providers: [{ provide: MyService, useValue: myService }], + parent: this.injector, + }); + + const modal = await this.modalController.create({ + component: MyModalComponent, + injector: customInjector, + }); + await modal.present(); + } +} +``` + +### Using with Angular CDK Directionality + +A common use case is providing the Angular CDK `Dir` directive to overlays for bidirectional text support: + +```typescript +import { Component, Injector } from '@angular/core'; +import { Dir } from '@angular/cdk/bidi'; +import { ModalController } from '@ionic/angular/standalone'; +import { MyModalComponent } from './my-modal.component'; + +@Component({ + selector: 'app-feature', + templateUrl: './feature.component.html', +}) +export class FeatureComponent { + constructor(private modalController: ModalController, private injector: Injector) {} + + async openModal() { + const modal = await this.modalController.create({ + component: MyModalComponent, + injector: this.injector, // Includes Dir from component tree + }); + await modal.present(); + } +} +``` + +### Popover Controller + +The `PopoverController` supports the same `injector` option: + +```typescript +import { Component, Injector } from '@angular/core'; +import { PopoverController } from '@ionic/angular/standalone'; +import { MyPopoverComponent } from './my-popover.component'; + +@Component({ + selector: 'app-feature', + templateUrl: './feature.component.html', +}) +export class FeatureComponent { + constructor(private popoverController: PopoverController, private injector: Injector) {} + + async openPopover(event: Event) { + const popover = await this.popoverController.create({ + component: MyPopoverComponent, + event: event, + injector: this.injector, + }); + await popover.present(); + } +} +``` + +## Angular Options Types + +Ionic Angular exports its own `ModalOptions` and `PopoverOptions` types that extend the core options with Angular-specific properties like `injector`: + +- `ModalOptions` - Extends core `ModalOptions` with the `injector` property +- `PopoverOptions` - Extends core `PopoverOptions` with the `injector` property + +These types are exported from `@ionic/angular` and `@ionic/angular/standalone`: + +```typescript +import type { ModalOptions, PopoverOptions } from '@ionic/angular/standalone'; +``` + +## Docs for Overlays in Ionic + +For full docs and to see usage examples, visit the docs page for each of the overlays in Ionic: + +- [Action Sheet](https://ionicframework.com/docs/api/action-sheet) +- [Alert](https://ionicframework.com/docs/api/alert) +- [Loading](https://ionicframework.com/docs/api/loading) +- [Modal](https://ionicframework.com/docs/api/modal) +- [Picker](https://ionicframework.com/docs/api/picker) +- [Popover](https://ionicframework.com/docs/api/popover) +- [Toast](https://ionicframework.com/docs/api/toast) diff --git a/docs/api/datetime.md b/docs/api/datetime.md index af3f8671ebe..38b55921cab 100644 --- a/docs/api/datetime.md +++ b/docs/api/datetime.md @@ -41,7 +41,9 @@ import ShowAdjacentDays from '@site/static/usage/v8/datetime/show-adjacent-days/ import MultipleDateSelection from '@site/static/usage/v8/datetime/multiple/index.md'; import GlobalTheming from '@site/static/usage/v8/datetime/styling/global-theming/index.md'; +import CalendarHeaderStyling from '@site/static/usage/v8/datetime/styling/calendar-header/index.md'; import CalendarDaysStyling from '@site/static/usage/v8/datetime/styling/calendar-days/index.md'; +import DatetimeHeaderStyling from '@site/static/usage/v8/datetime/styling/datetime-header/index.md'; import WheelStyling from '@site/static/usage/v8/datetime/styling/wheel-styling/index.md'; @@ -352,6 +354,24 @@ Ionicの強力なテーマシステムを使用すると、特定のテーマに +### Datetime Header + +The datetime header manages the content for the `title` slot and the selected date. + +:::note +The selected date will not render if `preferWheel` is set to `true`. +::: + + + +### Calender Header + +The calendar header manages the date navigation controls (month/year picker and prev/next buttons) and the days of the week when using a grid style layout. + +The header can be styled using CSS shadow parts. + + + ### Calendar Days The calendar days in a grid-style `ion-datetime` can be styled using CSS shadow parts. diff --git a/docs/api/modal.md b/docs/api/modal.md index c8afa0a00b4..5e1618454a3 100644 --- a/docs/api/modal.md +++ b/docs/api/modal.md @@ -212,6 +212,26 @@ import CustomDialogs from '@site/static/usage/v8/modal/custom-dialogs/index.md'; * `ion-content` は、フルページモダル、カード、シートで使用することを意図しています。カスタムダイアログのサイズが動的であったり、不明であったりする場合は、 `ion-content` を使用するべきではありません。 * カスタムダイアログを作成することは、デフォルトのモーダルエクスペリエンスから逃れる方法を提供します。そのため、カスタムダイアログは、カードやシートのモーダルでは使用しないでください。 +## Event Handling + +### Using `ionDragStart` and `ionDragEnd` + +The `ionDragStart` event is emitted as soon as the user begins a dragging gesture on the modal. This event fires at the moment the user initiates contact with the handle or modal surface, before any actual displacement occurs. It is particularly useful for preparing the interface for a transition, such as hiding certain interactive elements (like headers or buttons) to ensure a smooth dragging experience. + +The `ionDragEnd` event is emitted when the user completes the dragging gesture by releasing the modal. Like the move event, it includes the final [`ModalDragEventDetail`](#modaldrageventdetail) object. This event is commonly used to finalize state changes once the modal has come to a rest. + +import DragStartEndEvents from '@site/static/usage/v8/modal/drag-start-end-events/index.md'; + + + +### Using `ionDragMove` + +The `ionDragMove` event is emitted continuously while the user is actively dragging the modal. This event provides a [`ModalDragEventDetail`](#modaldrageventdetail) object containing real-time data, essential for creating highly responsive UI updates that react instantly to the user's touch. For example, the `progress` value can be used to dynamically darken a header's opacity as the modal is dragged upward. + +import DragMoveEvent from '@site/static/usage/v8/modal/drag-move-event/index.md'; + + + ## Interfaces ### ModalOptions @@ -253,7 +273,60 @@ interface ModalCustomEvent extends CustomEvent { } ``` -## アクセシビリティ +### ModalDragEventDetail + +When using the `ionDragMove` and `ionDragEnd` events, the event detail contains the following properties: + +```typescript +interface ModalDragEventDetail { + /** + * The current Y position of the modal. + * + * This can be used to determine how far the modal has been dragged. + */ + currentY: number; + /** + * The change in Y position since the gesture started. + * + * This can be used to determine the direction of the drag. + */ + deltaY: number; + /** + * The velocity of the drag in the Y direction. + * + * This can be used to determine how fast the modal is being dragged. + */ + velocityY: number; + /** + * A number between 0 and 1. + * + * In a sheet modal, progress represents the relative position between + * the lowest and highest defined breakpoints. + * + * In a card modal, it measures the relative position between the + * bottom of the screen and the top of the modal when it is fully + * open. + * + * This can be used to style content based on how far the modal has + * been dragged. + */ + progress: number; + /** + * If the modal is a sheet modal, this will be the breakpoint that + * the modal will snap to if the user lets go of the modal at the + * current moment. + * + * If it's a card modal, this property will not be included in the + * event payload. + * + * This can be used to style content based on where the modal will + * snap to upon release. + */ + snapBreakpoint?: number; +} +``` + +## Accessibility ### Keyboard Interactions diff --git a/docs/api/picker.md b/docs/api/picker.md index 5f2cfa6eb4b..83debbd21c2 100644 --- a/docs/api/picker.md +++ b/docs/api/picker.md @@ -297,6 +297,37 @@ Each [Picker Column](./picker-column) can be navigated using the keyboard when f | Home | Scroll to the first option. | | End | Scroll to the last option. | +## Accessibility + +### Screen Readers + +Pickerは、各[Picker Column](./picker-column)に[`slider` role](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/slider_role)を実装することで、スクリーンリーダーを使用したナビゲーションをサポートします。以下のジェスチャーを使用してPickerをナビゲートできます。 + +| Gesture | Function | +| - | - | +| Swipe Left | Move focus to the previous Picker Column. | +| Swipe Right | Move focus to the next Picker Column. | +| Swipe Up | Select the next option in the Picker Column. | +| Swipe Down | Select the previous option in the Picker Column. | +| Double Tap and Slide Up/Down | Adjust the selected option in the Picker Column. Can be used as an alternative to swiping up and down. | + +:::caution +The Swipe Up and Swipe Down gestures rely on the correct key events being synthesized as noted on the [`slider` documentation](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/slider_role). [Chromium-based browsers do not synthesize keyboard events correctly](https://issues.chromium.org/issues/40816094), but the "Double Tap and Slide Up/Down" gesture can be used as an alternative until this has been implemented in Chromium-based browsers. +::: + +### Keyboard Interactions + +Each [Picker Column](./picker-column) can be navigated using the keyboard when focused. + +| Key | Description | +| -------------------- | ------------------------------------ | +| ArrowUp | Scroll to the previous option. | +| ArrowDown | Scroll to the next option. | +| PageUp | Scroll up by more than one option. | +| PageDown | Scroll down by more than one option. | +| Home | Scroll to the first option. | +| End | Scroll to the last option. | + ## プロパティ diff --git a/docs/api/range.md b/docs/api/range.md index 2cac2659530..063c9a07212 100644 --- a/docs/api/range.md +++ b/docs/api/range.md @@ -124,6 +124,8 @@ import CSSProps from '@site/static/usage/v8/range/theming/css-properties/index.m Rangeには [CSS Shadow Parts](#css-shadow-parts) があり、Rangeコンポーネント内の特定の要素ノードを完全にカスタマイズすることができます。CSS Shadow Partsは最も多くのカスタマイズ機能を提供し、Rangeコンポーネントで高度なスタイリングが必要な場合に推奨されるアプローチです。 +When `dualKnobs` is enabled, additional Shadow Parts are exposed to allow each knob to be styled independently. These are available in two forms: **static identity parts** (`A` and `B`) and **dynamic position parts** (`lower` and `upper`). The A and B parts always refer to the same physical knobs, even if the knobs cross. In contrast, the lower and upper parts reflect the current value position and automatically swap if the knobs cross. This allows styling by consistent identity or by relative value within the range. + import CSSParts from '@site/static/usage/v8/range/theming/css-shadow-parts/index.md'; diff --git a/docs/api/refresher.md b/docs/api/refresher.md index a34f4612474..8d7e12ea44d 100644 --- a/docs/api/refresher.md +++ b/docs/api/refresher.md @@ -73,10 +73,10 @@ Developers should apply the following CSS to the scrollable container. This CSS .ion-content-scroll-host::before, .ion-content-scroll-host::after { position: absolute; - + width: 1px; height: 1px; - + content: ""; } @@ -102,6 +102,17 @@ import Advanced from '@site/static/usage/v8/refresher/advanced/index.md'; +## Event Handling + +### Using `ionPullStart` and `ionPullEnd` + +The `ionPullStart` event is emitted when the user begins a pull gesture. This event fires when the user starts to pull the refresher down. + +The `ionPullEnd` event is emitted when the refresher returns to an inactive state, with a reason property of `'complete'` or `'cancel'` indicating whether the refresh operation completed successfully or was canceled. + +import PullStartEndEvents from '@site/static/usage/v8/refresher/pull-start-end-events/index.md'; + + ## Interfaces @@ -113,6 +124,14 @@ interface RefresherEventDetail { } ``` +### RefresherPullEndEventDetail + +```typescript +interface RefresherPullEndEventDetail { + reason: 'complete' | 'cancel'; +} +``` + ### RefresherCustomEvent 必須ではありませんが、このコンポーネントから発行される Ionic イベントでより強く型付けを行うために、`CustomEvent` インターフェースの代わりにこのインターフェースを使用することが可能です。 @@ -124,6 +143,17 @@ interface RefresherCustomEvent extends CustomEvent { } ``` +### RefresherPullEndCustomEvent + +While not required, this interface can be used in place of the `CustomEvent` interface for stronger typing with the `ionPullEnd` event. + +```typescript +interface RefresherPullEndCustomEvent extends CustomEvent { + detail: RefresherPullEndEventDetail; + target: HTMLIonRefresherElement; +} +``` + ## プロパティ diff --git a/docs/layout/dynamic-font-scaling.md b/docs/layout/dynamic-font-scaling.md index 1848cb11a47..778327dca17 100644 --- a/docs/layout/dynamic-font-scaling.md +++ b/docs/layout/dynamic-font-scaling.md @@ -18,47 +18,19 @@ import DynamicFontScaling from '@site/static/usage/v8/layout/dynamic-font-scalin ## Ionic でダイナミックフォントスケーリングを有効にする -:::info -この機能は現在、iOS ではオプトインです。ただし、Ionic 8 からデフォルトで有効になり、以下の CSS は不要になります。 -::: - -ダイナミックフォントスケーリングは、Android ではデフォルトで既に有効になっています。開発者は、iOS で以下の手順を使用して有効にできます: - -1. [typography.css](/docs/layout/global-stylesheets#typographycss)ファイルがインポートされていることを確認します。 -2. グローバルスタイルシートに以下の CSS を追加します: - -```css -html { - --ion-dynamic-font: var(--ion-default-dynamic-font); -} -``` - -:::note -内部では、Ionic は iOS デバイスでダイナミックフォントスケーリングを有効にするために以下の CSS を設定します: - -```css -html { - font: var(--ion-dynamic-font); -} -``` - -::: - -## ダイナミックフォントスケーリングの使用 - ### アプリケーションで有効にする -[typography.css](/docs/layout/global-stylesheets#typographycss)ファイルがインポートされている限り、ダイナミックフォントスケーリングはデフォルトで有効になっています。このファイルをインポートすると、`--ion-dynamic-font`変数が定義され、ダイナミックフォントスケーリングがアクティブになります。推奨されませんが、開発者はアプリケーションコードでこの変数を`initial`に設定することで、ダイナミックフォントスケーリングをオプトアウトできます。 +ダイナミックフォントスケーリング は、[typography.css](/docs/layout/global-stylesheets#typographycss) ファイルがインポートされている限り、デフォルトで有効になっています。このファイルをインポートすると `--ion-dynamic-font` 変数が定義され、ダイナミックフォントスケーリング が有効になります。推奨はされませんが、開発者はアプリケーションコード内でこの変数を `initial` に設定することで ダイナミックフォントスケーリング を無効にすることもできます。 -### カスタムコンポーネントの統合 +### カスタムコンポーネントへの統合 -開発者は、カスタムコンポーネントを設定して、`px`単位を使用する`font-size`宣言を[rem 単位](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units#lengths)を使用するように変換することで、ダイナミックフォントスケーリングを活用できます。`px`から`rem`に変換する簡単な方法は、ピクセルフォントサイズをデフォルトのブラウザフォントサイズ(通常は`16px`)で割ることです。たとえば、コンポーネントのフォントサイズが`14px`の場合、`14px / 16px = 0.875rem`として`rem`に変換できます。また、フォントサイズがオーバーライドされた Ionic コンポーネントも、`rem`単位を使用するように更新する必要があることに注意してください。 +開発者は、`px` 単位を使用している `font-size` 宣言を [rem 単位](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units#lengths) に変更することで、カスタムコンポーネントを ダイナミックフォントスケーリングに対応させることができます。`px` から `rem` へ変換する簡単な方法は、ピクセルのフォントサイズをブラウザのデフォルトフォントサイズ(通常は `16px`)で割ることです。例えば、コンポーネントのフォントサイズが `14px` の場合、`14px / 16px = 0.875rem` として `rem` に変換できます。また、フォントサイズを上書きしている Ionic コンポーネントがある場合も、`rem` 単位を使用するよう更新する必要があります。 -考慮すべき点の 1 つは、コンポーネントの寸法が大きなフォントサイズに対応するために変更する必要がある場合があることです。たとえば、`width`と`height`プロパティをそれぞれ`min-width`と`min-height`に変更する必要がある場合があります。開発者は、[length 値](https://developer.mozilla.org/en-US/docs/Web/CSS/length)を使用する CSS プロパティについてアプリケーションを監査し、`px`から`rem`への適切な変換を行う必要があります。また、大きなテキストを読みやすく保つために、長いテキストを切り詰めるのではなく、次の行に折り返すことをお勧めします。 +もう一つ注意すべき点として、フォントサイズが大きくなった場合に対応できるよう、コンポーネントの寸法を変更する必要がある場合があります。例えば、`width` や `height` プロパティは、それぞれ `min-width` や `min-height` に変更する必要があるかもしれません。開発者は、[length 値](https://developer.mozilla.org/en-US/docs/Web/CSS/length) を使用している CSS プロパティがないかアプリケーション全体を確認し、必要に応じて `px` から `rem` への変換を行うべきです。また、大きなテキストでも読みやすくするため、長いテキストは途中で省略するのではなく次の行に折り返すことを推奨します。 ### カスタムフォントファミリー -Ionic のデフォルトフォントは、あらゆるサイズで見栄えが良く、他のモバイルアプリとの一貫性を確保するように設計されているため、使用することをお勧めします。ただし、開発者は CSS を使用してダイナミックフォントスケーリングでカスタムフォントファミリーを使用できます: +Ionic のデフォルトフォントは、どのサイズでも見やすく設計されており、他のモバイルアプリとの一貫性も保たれるため、これらを使用することを推奨します。ただし、開発者は CSS を使用して Dynamic Font Scaling とともにカスタムフォントファミリーを利用することもできます: ```css html { @@ -220,196 +192,3 @@ Action Sheet などの特定のネイティブ iOS コンポーネントは、Io :::info iOS は、デフォルトフォントサイズが`16px`の"Callout"テキストスタイルを提供します。ただし、このフォントスタイルは現在、Web コンテンツに公開されていません。詳細については、[WebKit でサポートされているテキストスタイル](https://webkit.org/blog/3709/using-the-system-font-in-web-content/)を参照してください。 ::: - -# Dynamic Font Scaling - -Dynamic Font Scaling is a feature that allows users to choose the size of the text displayed on the screen. This helps users who need larger text for better readability, and it also accommodates users who can read smaller text. - -## Try It Out - -:::tip -Be sure to try this on an Android, iOS, or iPadOS device. - -If you are testing on Chrome for Android, make sure ["Accessibility Page Zoom"](#chrome-for-android) is enabled. -::: - -Follow the [Changing the Font Size on a Device](#changing-the-font-size-on-a-device) guide to set your preferred font size, and watch the text in the demo below grow or shrink according to your preferences. - - - -## Using Dynamic Font Scaling - -### Enabling in an Application - -Dynamic Font Scaling is enabled by default as long as the [typography.css](/docs/layout/global-stylesheets#typographycss) file is imported. Importing this file will define the `--ion-dynamic-font` variable which will activate Dynamic Font Scaling. While not recommended, developers can opt-out of Dynamic Font Scaling by setting this variable to `initial` in their application code. - -### Integrating Custom Components - -Developers can configure their custom components to take advantage of Dynamic Font Scaling by converting any `font-size` declarations that use `px` units to use [rem units](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units#lengths) instead. An easy way to convert from `px` to `rem` is to divide the pixel font size by the default browser font size, which is typically `16px`. For example, if a component has a font size of `14px`, then this could be converted to `rem` by doing `14px / 16px = 0.875rem`. Also note that any Ionic components that have had their font sizes overridden should also be updated to use `rem` units. - -One thing to keep in mind is that the dimensions of your components may need to change to accommodate the larger font sizes. For example, `width` and `height` properties may need to change to `min-width` and `min-height`, respectively. Developers should audit their applications for any CSS properties that use [length values](https://developer.mozilla.org/en-US/docs/Web/CSS/length) and make any applicable conversions from `px` to `rem`. We also recommend having long text wrap to the next line instead of truncating to keep large text readable. - -### Custom Font Family - -We recommend using the default fonts in Ionic as they are designed to look good at any size and ensure consistency with other mobile apps. However, developers can use a custom font family with Dynamic Font Scaling via CSS: - -```css -html { - --ion-dynamic-font: var(--ion-default-dynamic-font); - --ion-font-family: 'Comic Sans MS'; -} -``` - -### `em` units versus `rem` units - -Developers have two options for relative font sizes: [`em` and `rem`](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units#ems_and_rems). - -`em` units set the font size of an element relative to the font size of its parent. - -In the following example, the computed font size of `.child` is `40px` because it is a child of `.parent` (`2em * 20px = 40px`). - -```css -.parent { - font-size: 20px; -} - -.child { - font-size: 2em; -} -``` - -However, the `em` unit has a compounding effect which can cause issues. In the following example, the second `.child` element has a computed font size of `80px` since the font sizes compound. - -```html -
- Parent element with 20px -
- Child element with 40px -
Child element with 80px
-
-
-``` - -
- Parent element with 20px -
- Child element with 40px -
Child element with 80px
-
-
- -Due to this compounding effect, we strongly recommend using `rem` units instead of `em` units when working with Dynamic Font Scaling. `rem` units set the font size of an element relative to the font size of the root element, which is typically ``. The default font size of the root element is typically `16px`. - -In the following example, the computed font size of `.child` is `32px` because the font size is being computed relative to `html`, not `.parent`. - -```css -.parent { - font-size: 20px; -} - -.child { - font-size: 2rem; -} -``` - -## How Dynamic Font Scaling works in Ionic - -Ionic components that define font sizes and participate in Dynamic Font Scaling typically use [rem units](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units#lengths). This sizes the text in each component relative to the font size of the root element, which is usually the `html` element. This means that as the root element's font size changes, the text in all Ionic components scale in a consistent manner. This avoids the need to manually override each component's font size. Some elements inside of these components, such as icons, use `em` units instead so the elements are sized relative to the text, though the text itself is sized using `rem` units. - -### iOS - -Dynamic Font Scaling in Ionic builds on top of an iOS feature called [Dynamic Type](https://developer.apple.com/documentation/uikit/uifont/scaling_fonts_automatically#overview). To do this, Ionic sets the [font](https://developer.mozilla.org/en-US/docs/Web/CSS/font) of the root element to an Apple-defined text style. For consistency, Ionic uses the [body](https://developer.apple.com/documentation/uikit/uifont/textstyle/1616682-body) text style. - -Using the Apple-defined text style enables Dynamic Type, allowing all text in Ionic components to scale according to the system-level preference. Note that these Apple-defined fonts only work on Apple devices. As a result, these fonts will not work on Android devices even if your app is using `ios` mode. - -Ionic follows [Apple's Human Interface Guidelines for Typography](https://developer.apple.com/design/human-interface-guidelines/typography) when an app is in `ios` mode. As a result, important content is prioritized when the text size changes. This means a few things: - -1. Content in an `ion-header` or an `ion-footer` will have maximum font sizes to prioritize content in `ion-content` which is deemed more important than content in the `ion-header` and `ion-footer`. -2. Components such as `ion-badge` and `ion-back-button` will have minimum font sizes so they remain readable. -3. Text in components such as `ion-tab-bar` and `ion-picker` do not participate in Dynamic Font Scaling according to Apple's Human Interface Guidelines. - -### Android Web View - -The Android Web View's font scaling mechanism is always enabled in web content and will automatically scale font sizes defined using the `px` unit. This means that any maximum or minimum font sizes specified using `px` will still be scaled even if the final font size does not align with the maximum or minimum font sizes specified. - -In the following example we are using the [min()](https://developer.mozilla.org/en-US/docs/Web/CSS/min) function to indicate that the font size of `.foo` should be no larger than `14px`. - -```css -.foo { - font-size: min(1rem, 14px); -} -``` - -If the root element's default font size is `16px`, and the system-level font scale is `1.5` (i.e text sizes should be increased by 50%), then `1rem` will evaluate to `24px` because `16 * 1.5 = 24`. - -This is larger than our defined maximum of `14px`, so one might assume that the evaluated font size of `.foo` is `14px`. However, since the Android Web View scales any font sizes defined using the `px` unit, this means the `14px` used in our `min()` function will also be scaled by 1.5. - -As a result, this means that the maximum computed font size is actually `21px` since `14 * 1.5 = 21` and therefore the overall computed font size of `.foo` is `21px`. - -### Chrome for Android - -The Chrome Web Browser on Android behaves differently than the Android Web View. By default, Chrome for Android does not respect the system-level font scale setting. However, the Chromium team is working on a new feature to allow for this. When enabled, this feature will change the `zoom` level of the `html` element which will cause the layout to increase in size in addition to the text. - -Developers can test this behavior by enabling the experimental "Accessibility Page Zoom" feature in `chrome://flags`. - -See https://bugs.chromium.org/p/chromium/issues/detail?id=645717 for more information. - -### Using Modes on Different Platforms - -Each platform has slightly different font scaling behaviors, and the `ios` and `md` modes have been implemented to take advantage of the scaling behaviors on their respective platforms. - -For example, `ios` mode makes use of maximum and minimum font sizes to follow [Apple's Human Interface Guidelines for Typography](https://developer.apple.com/design/human-interface-guidelines/typography). `md` mode does not implement this same behavior because Material Design does not have that same guidance. This means that using `md` mode on an iOS device may allow for very large font sizes in headers and footers. - -As a result, we strongly recommend using `ios` mode on iOS devices and `md` mode on Android devices when using Dynamic Font Scaling. - -## Changing the Font Size on a Device - -Font scaling preferences are configured on a per-device basis by the user. This allows the user to scale the font for all applications that support this behavior. This guide shows how to enable font scaling for each platform. - -### iOS - -Font scaling on iOS can be configured in the Settings app. - -See [Apple Support](https://support.apple.com/en-us/102453) for more information. - -### Android - -Where users access the font scaling configuration varies across devices, but it is typically found in the "Accessibility" page in the Settings app. - -:::info -The Chrome Web Browser on Android has some limitations with respecting system-level font scales. See [Chrome for Android](#chrome-for-android) for more information. -::: - -## Troubleshooting - -### Dynamic Font Scaling is not working - -There are a number of reasons why Dynamic Font Scaling may not have any effect on an app. The following list, while not exhaustive, provides some things to check to debug why Dynamic Font Scaling is not working. - -1. Verify that your version of Ionic supports Dynamic Font Scaling. Dynamic Font Scaling was added starting in Ionic v7.5. -2. Verify that the [typography.css](/docs/layout/global-stylesheets#typographycss) file has been imported. This file is required for Dynamic Font Scaling to work. -3. Verify that your code does not override the root element's default font size. Manually setting a font size on the root element will prevent Dynamic Font Scaling from working as intended. -4. Verify that your code does not override font sizes on Ionic components. Ionic components that set `font-size` rules will use `rem` units. However, if your app overrides that to use `px`, then that custom rule will need to be converted to use `rem`. See [Integrating Custom Components](#integrating-custom-components) for more information. -5. Verify "Accessibility Page Zoom" is enabled if using Chrome for Android. See [Chrome for Android](#chrome-for-android) for more information. - -### Maximum and minimum font sizes are not being respected on Android - -The Android Web View scales any font sizes defined using the `px` unit by the system-level font scale preference. This means that actual font sizes may be larger or smaller than the font sizes defined in [min()](https://developer.mozilla.org/en-US/docs/Web/CSS/min), [max()](https://developer.mozilla.org/en-US/docs/Web/CSS/max), or [clamp()](https://developer.mozilla.org/en-US/docs/Web/CSS/clamp). - -See [how font scaling works on Android](#android) for more information. - -### Font sizes are larger/smaller even with Dynamic Font Scaling disabled - -Ionic components define font sizes using [rem units](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units#lengths) even when Dynamic Font Scaling is disabled. This sizes the text in each component relative to the font size of the root element, which is usually the `html` element. As a result, if the font size of `html` changes, the computed font size of all Ionic components will change too. - -### Scaled Ionic iOS component font sizes do not exactly match native iOS equivalents - -Certain native iOS components such as the Action Sheet make use of private font scales that Ionic does not have access to. While we try to stay as close as possible to the native behavior, text in some components may render slightly larger or smaller than their native counterparts. - -### The text size in my Ionic app on iOS changed when enabling Dynamic Font Scaling - -The root element's default font size is typically `16px`. However, Dynamic Font Scaling on iOS devices make use of the ["Body" text style](https://developer.apple.com/design/human-interface-guidelines/typography#Specifications) which has a default font size of `17px`. Since the text in Ionic components is scaled relative to the root element's font size, some text may get larger or smaller when Dynamic Font Scaling is enabled, even if the system-level text scale did not change. - -:::info -iOS provides a "Callout" text style which has a default font size of `16px`. However, this font style is currently not exposed to web content. See [the supported text styles in WebKit](https://webkit.org/blog/3709/using-the-system-font-in-web-content/) for more information. -::: diff --git a/docs/react/navigation.md b/docs/react/navigation.md index 63a532fd8cf..963f88975c8 100644 --- a/docs/react/navigation.md +++ b/docs/react/navigation.md @@ -146,6 +146,8 @@ const DashboardPage: React.FC = ({ match }) => { The `IonPage` component wraps each view in an Ionic React app and allows page transitions and stack navigation to work properly. Each view that is navigated to using the router must include an `IonPage` component. +`IonPage` is also required for proper styling. It provides a flex container that ensures page content, such as `IonContent`, is properly sized and does not overlap other UI elements like `IonTabBar`. + ```tsx import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react'; import React from 'react'; diff --git a/docs/vue/navigation.md b/docs/vue/navigation.md index d90aaec181b..c2bcf23a752 100644 --- a/docs/vue/navigation.md +++ b/docs/vue/navigation.md @@ -494,6 +494,8 @@ The example below shows how the Spotify app reuses the same album component to s The `IonPage` component wraps each view in an Ionic Vue app and allows page transitions and stack navigation to work properly. Each view that is navigated to using the router must include an `IonPage` component. +`IonPage` is also required for proper styling. It provides a flex container that ensures page content, such as `IonContent`, is properly sized and does not overlap other UI elements like `IonTabBar`. + ```vue