diff --git a/docs/api/modal.mdx b/docs/api/modal.mdx
index e68e368290..c42f823e77 100644
--- a/docs/api/modal.mdx
+++ b/docs/api/modal.mdx
@@ -127,7 +127,7 @@ import CardExample from '@site/static/usage/v9/modal/card/basic/index.mdx';
:::info
-[Content](./content) should be used inside of the sheet modal if you want your modal content to be scrollable.
+[Content](./content.mdx) should be used inside of the sheet modal if you want your modal content to be scrollable.
:::
@@ -175,34 +175,58 @@ import SheetScrollingContentExample from '@site/static/usage/v9/modal/sheet/expa
-## Styling
+## Theming
-Modals are presented at the root of your application so they overlay your entire app. This behavior applies to both inline modals and modals presented from a controller. As a result, custom modal styles can not be scoped to a particular component as they will not apply to the modal. Instead, styles must be applied globally. For most developers, placing the custom styles in `global.css` is sufficient.
+[Controller modals](#controller-modals) are appended to the root of your application, outside of the component that presented them. Component-scoped styles will not reach these modals, so their styles must be applied globally, from a stylesheet that is not scoped to a component.
-:::note
+[Inline modals](#inline-modals-recommended) stay where they are declared in your template, so component-scoped styles do apply to them.
-If you are building an Ionic Angular app, the styles need to be added to a global stylesheet file. Read [Style Placement](#style-placement) in the Angular section below for more information.
+### Styling
-:::
+Modals expose [CSS Custom Properties](#css-custom-properties) and [CSS Shadow Parts](#css-shadow-parts) for customizing their appearance.
-:::note
+import ThemeExample from '@site/static/usage/v9/modal/styling/theming/index.mdx';
-`ion-modal` works under the assumption that stacked modals are the same size. As a result, each subsequent modal will have no box shadow and a backdrop opacity of `0`. This is to avoid the effect of shadows and backdrops getting darker with each added modal. This can be changed by setting the `--box-shadow` and `--backdrop-opacity` CSS variables:
+
-:::
+### Stacked Modals
-```
+`ion-modal` works under the assumption that stacked modals are the same size. As a result, each subsequent modal will have no box shadow and a backdrop opacity of `0`. This is to avoid the effect of shadows and backdrops getting darker with each added modal. This can be changed by setting the `--box-shadow` and `--backdrop-opacity` CSS variables:
+
+```css
ion-modal.stack-modal {
--box-shadow: 0 28px 48px rgba(0, 0, 0, 0.4);
--backdrop-opacity: var(--ion-backdrop-opacity, 0.32);
}
```
-import ThemeExample from '@site/static/usage/v9/modal/styling/theming/index.mdx';
+### Sizing to Content
-
+A modal fills the height of its container by default. Setting the `--height` CSS variable to `auto`, `fit-content`, `min-content`, or `max-content` sizes the modal to the height of what it renders instead.
+
+:::warning
+
+The height must be set on the modal through the `--height` CSS variable. Setting `height` on the `content` [CSS Shadow Part](#css-shadow-parts) will still resize the modal, but any [Content](./content.mdx) inside of it will collapse to a height of `0`, leaving only the header visible.
+
+```css
+/* DO NOT DO THIS */
+ion-modal::part(content) {
+ height: fit-content;
+}
+
+/* Do this instead */
+ion-modal {
+ --height: fit-content;
+}
+```
+
+:::
+
+import SizingToContentExample from '@site/static/usage/v9/modal/styling/sizing-to-content/index.mdx';
+
+
-### Animations
+## Animations
The enter and leave animations can be customized by using our animation builder and assigning animations to `enterAnimation` and `leaveAnimation`.
@@ -220,7 +244,7 @@ import CustomDialogs from '@site/static/usage/v9/modal/custom-dialogs/index.mdx'
A few things to keep in mind when creating custom dialogs:
-- `ion-content` is intended to be used in full-page modals, cards, and sheets. If your custom dialog has a dynamic or unknown size, `ion-content` should not be used.
+- `ion-content` can be used in a custom dialog, including one that is sized to its contents. The dialog's height must come from the `--height` CSS variable rather than from the dialog's children. See [Sizing to Content](#sizing-to-content).
- Creating custom dialogs provides a way of ejecting from the default modal experience. As a result, custom dialogs should not be used with card or sheet modals.
## Event Handling
diff --git a/static/usage/v9/modal/styling/sizing-to-content/angular/example_component_css.md b/static/usage/v9/modal/styling/sizing-to-content/angular/example_component_css.md
new file mode 100644
index 0000000000..92b72024d7
--- /dev/null
+++ b/static/usage/v9/modal/styling/sizing-to-content/angular/example_component_css.md
@@ -0,0 +1,8 @@
+```css
+ion-modal {
+ --height: fit-content;
+ --width: 90%;
+ --border-radius: 8px;
+ --box-shadow: 0 28px 48px rgba(0, 0, 0, 0.4);
+}
+```
diff --git a/static/usage/v9/modal/styling/sizing-to-content/angular/example_component_html.md b/static/usage/v9/modal/styling/sizing-to-content/angular/example_component_html.md
new file mode 100644
index 0000000000..3e5f8f3d89
--- /dev/null
+++ b/static/usage/v9/modal/styling/sizing-to-content/angular/example_component_html.md
@@ -0,0 +1,32 @@
+```html
+
+
+ App
+
+
+
+ Open Modal
+
+
+
+
+
+ Modal
+
+ Close
+
+
+
+
+
+ @for (item of ['Item 1', 'Item 2', 'Item 3']; track item) {
+
+ {{ item }}
+
+ }
+
+
+
+
+
+```
diff --git a/static/usage/v9/modal/styling/sizing-to-content/angular/example_component_ts.md b/static/usage/v9/modal/styling/sizing-to-content/angular/example_component_ts.md
new file mode 100644
index 0000000000..45071ee743
--- /dev/null
+++ b/static/usage/v9/modal/styling/sizing-to-content/angular/example_component_ts.md
@@ -0,0 +1,23 @@
+```ts
+import { Component } from '@angular/core';
+import {
+ IonButton,
+ IonButtons,
+ IonContent,
+ IonHeader,
+ IonItem,
+ IonLabel,
+ IonList,
+ IonModal,
+ IonTitle,
+ IonToolbar,
+} from '@ionic/angular';
+
+@Component({
+ selector: 'app-example',
+ templateUrl: 'example.component.html',
+ styleUrls: ['example.component.css'],
+ imports: [IonButton, IonButtons, IonContent, IonHeader, IonItem, IonLabel, IonList, IonModal, IonTitle, IonToolbar],
+})
+export class ExampleComponent {}
+```
diff --git a/static/usage/v9/modal/styling/sizing-to-content/demo.html b/static/usage/v9/modal/styling/sizing-to-content/demo.html
new file mode 100644
index 0000000000..33eab3e0af
--- /dev/null
+++ b/static/usage/v9/modal/styling/sizing-to-content/demo.html
@@ -0,0 +1,61 @@
+
+
+
+
+
+ Modal | Sizing to Content
+
+
+
+
+
+
+
+
+
+
+
+ App
+
+
+
+ Open Modal
+
+
+
+
+ Modal
+
+ Close
+
+
+
+
+
+
+ Item 1
+
+
+ Item 2
+
+
+ Item 3
+
+
+
+
+
+
+
+
+
+
diff --git a/static/usage/v9/modal/styling/sizing-to-content/index.mdx b/static/usage/v9/modal/styling/sizing-to-content/index.mdx
new file mode 100644
index 0000000000..2421774fa2
--- /dev/null
+++ b/static/usage/v9/modal/styling/sizing-to-content/index.mdx
@@ -0,0 +1,35 @@
+import Playground from '@site/src/components/global/Playground';
+
+import javascript from './javascript.md';
+import vue from './vue.md';
+
+import react_main_tsx from './react/main_tsx.md';
+import react_main_css from './react/main_css.md';
+
+import angular_example_component_html from './angular/example_component_html.md';
+import angular_example_component_css from './angular/example_component_css.md';
+import angular_example_component_ts from './angular/example_component_ts.md';
+
+
diff --git a/static/usage/v9/modal/styling/sizing-to-content/javascript.md b/static/usage/v9/modal/styling/sizing-to-content/javascript.md
new file mode 100644
index 0000000000..abbaaaaa6c
--- /dev/null
+++ b/static/usage/v9/modal/styling/sizing-to-content/javascript.md
@@ -0,0 +1,47 @@
+```html
+
+
+
+
+ App
+
+
+
+ Open Modal
+
+
+
+
+ Modal
+
+ Close
+
+
+
+
+
+
+ Item 1
+
+
+ Item 2
+
+
+ Item 3
+
+
+
+
+
+
+
+```
diff --git a/static/usage/v9/modal/styling/sizing-to-content/react/main_css.md b/static/usage/v9/modal/styling/sizing-to-content/react/main_css.md
new file mode 100644
index 0000000000..92b72024d7
--- /dev/null
+++ b/static/usage/v9/modal/styling/sizing-to-content/react/main_css.md
@@ -0,0 +1,8 @@
+```css
+ion-modal {
+ --height: fit-content;
+ --width: 90%;
+ --border-radius: 8px;
+ --box-shadow: 0 28px 48px rgba(0, 0, 0, 0.4);
+}
+```
diff --git a/static/usage/v9/modal/styling/sizing-to-content/react/main_tsx.md b/static/usage/v9/modal/styling/sizing-to-content/react/main_tsx.md
new file mode 100644
index 0000000000..834160ac40
--- /dev/null
+++ b/static/usage/v9/modal/styling/sizing-to-content/react/main_tsx.md
@@ -0,0 +1,63 @@
+```tsx
+import React, { useRef } from 'react';
+import {
+ IonButton,
+ IonButtons,
+ IonContent,
+ IonHeader,
+ IonItem,
+ IonLabel,
+ IonList,
+ IonModal,
+ IonPage,
+ IonTitle,
+ IonToolbar,
+} from '@ionic/react';
+
+import './main.css';
+
+function Example() {
+ const modal = useRef(null);
+
+ function dismiss() {
+ modal.current?.dismiss();
+ }
+
+ return (
+
+
+
+ App
+
+
+
+
+ Open Modal
+
+
+
+
+
+ Modal
+
+ dismiss()}>Close
+
+
+
+
+
+ {['Item 1', 'Item 2', 'Item 3'].map((item) => (
+
+ {item}
+
+ ))}
+
+
+
+
+
+ );
+}
+
+export default Example;
+```
diff --git a/static/usage/v9/modal/styling/sizing-to-content/vue.md b/static/usage/v9/modal/styling/sizing-to-content/vue.md
new file mode 100644
index 0000000000..33dedffb3a
--- /dev/null
+++ b/static/usage/v9/modal/styling/sizing-to-content/vue.md
@@ -0,0 +1,59 @@
+```vue
+
+
+
+
+
+ App
+
+
+
+ Open Modal
+
+
+
+
+ Modal
+
+ Close
+
+
+
+
+
+
+ {{ item }}
+
+
+
+
+
+
+
+
+```
diff --git a/versioned_docs/version-v6/api/modal.mdx b/versioned_docs/version-v6/api/modal.mdx
index e9f562d368..2f37874f04 100644
--- a/versioned_docs/version-v6/api/modal.mdx
+++ b/versioned_docs/version-v6/api/modal.mdx
@@ -137,30 +137,30 @@ import SheetHandleBehaviorExample from '@site/static/usage/v6/modal/sheet/handle
-## Styling
+## Theming
-Modals are presented at the root of your application so they overlay your entire app. This behavior applies to both inline modals and modals presented from a controller. As a result, custom modal styles can not be scoped to a particular component as they will not apply to the modal. Instead, styles must be applied globally. For most developers, placing the custom styles in `global.css` is sufficient.
+[Controller modals](#controller-modals) are appended to the root of your application, outside of the component that presented them. Component-scoped styles will not reach these modals, so their styles must be applied globally, from a stylesheet that is not scoped to a component.
-:::note
-If you are building an Ionic Angular app, the styles need to be added to a global stylesheet file. Read [Style Placement](#style-placement) in the Angular section below for more information.
-:::
+[Inline modals](#inline-modals-recommended) stay where they are declared in your template, so component-scoped styles do apply to them.
+
+### Styling
+
+import ThemeExample from '@site/static/usage/v6/modal/styling/theming/index.mdx';
+
+
+
+### Stacked Modals
-:::note
`ion-modal` works under the assumption that stacked modals are the same size. As a result, each subsequent modal will have no box shadow and a backdrop opacity of `0`. This is to avoid the effect of shadows and backdrops getting darker with each added modal. This can be changed by setting the `--box-shadow` and `--backdrop-opacity` CSS variables:
-:::
-```
+```css
ion-modal.stack-modal {
--box-shadow: 0 28px 48px rgba(0, 0, 0, 0.4);
--backdrop-opacity: var(--ion-backdrop-opacity, 0.32);
}
```
-import ThemeExample from '@site/static/usage/v6/modal/styling/theming/index.mdx';
-
-
-
-### Animations
+## Animations
The enter and leave animations can be customized by using our animation builder and assigning animations to `enterAnimation` and `leaveAnimation`.
diff --git a/versioned_docs/version-v7/api/modal.mdx b/versioned_docs/version-v7/api/modal.mdx
index c098fe75b2..1ec436cd96 100644
--- a/versioned_docs/version-v7/api/modal.mdx
+++ b/versioned_docs/version-v7/api/modal.mdx
@@ -161,30 +161,30 @@ import SheetHandleBehaviorExample from '@site/static/usage/v7/modal/sheet/handle
-## Styling
+## Theming
-Modals are presented at the root of your application so they overlay your entire app. This behavior applies to both inline modals and modals presented from a controller. As a result, custom modal styles can not be scoped to a particular component as they will not apply to the modal. Instead, styles must be applied globally. For most developers, placing the custom styles in `global.css` is sufficient.
+[Controller modals](#controller-modals) are appended to the root of your application, outside of the component that presented them. Component-scoped styles will not reach these modals, so their styles must be applied globally, from a stylesheet that is not scoped to a component.
-:::note
-If you are building an Ionic Angular app, the styles need to be added to a global stylesheet file. Read [Style Placement](#style-placement) in the Angular section below for more information.
-:::
+[Inline modals](#inline-modals-recommended) stay where they are declared in your template, so component-scoped styles do apply to them.
+
+### Styling
+
+import ThemeExample from '@site/static/usage/v7/modal/styling/theming/index.mdx';
+
+
+
+### Stacked Modals
-:::note
`ion-modal` works under the assumption that stacked modals are the same size. As a result, each subsequent modal will have no box shadow and a backdrop opacity of `0`. This is to avoid the effect of shadows and backdrops getting darker with each added modal. This can be changed by setting the `--box-shadow` and `--backdrop-opacity` CSS variables:
-:::
-```
+```css
ion-modal.stack-modal {
--box-shadow: 0 28px 48px rgba(0, 0, 0, 0.4);
--backdrop-opacity: var(--ion-backdrop-opacity, 0.32);
}
```
-import ThemeExample from '@site/static/usage/v7/modal/styling/theming/index.mdx';
-
-
-
-### Animations
+## Animations
The enter and leave animations can be customized by using our animation builder and assigning animations to `enterAnimation` and `leaveAnimation`.
diff --git a/versioned_docs/version-v8/api/modal.mdx b/versioned_docs/version-v8/api/modal.mdx
index 8f38444c64..f352f77999 100644
--- a/versioned_docs/version-v8/api/modal.mdx
+++ b/versioned_docs/version-v8/api/modal.mdx
@@ -175,34 +175,30 @@ import SheetScrollingContentExample from '@site/static/usage/v8/modal/sheet/expa
-## Styling
+## Theming
-Modals are presented at the root of your application so they overlay your entire app. This behavior applies to both inline modals and modals presented from a controller. As a result, custom modal styles can not be scoped to a particular component as they will not apply to the modal. Instead, styles must be applied globally. For most developers, placing the custom styles in `global.css` is sufficient.
+[Controller modals](#controller-modals) are appended to the root of your application, outside of the component that presented them. Component-scoped styles will not reach these modals, so their styles must be applied globally, from a stylesheet that is not scoped to a component.
-:::note
+[Inline modals](#inline-modals-recommended) stay where they are declared in your template, so component-scoped styles do apply to them.
-If you are building an Ionic Angular app, the styles need to be added to a global stylesheet file. Read [Style Placement](#style-placement) in the Angular section below for more information.
+### Styling
-:::
+import ThemeExample from '@site/static/usage/v8/modal/styling/theming/index.mdx';
-:::note
+
-`ion-modal` works under the assumption that stacked modals are the same size. As a result, each subsequent modal will have no box shadow and a backdrop opacity of `0`. This is to avoid the effect of shadows and backdrops getting darker with each added modal. This can be changed by setting the `--box-shadow` and `--backdrop-opacity` CSS variables:
+### Stacked Modals
-:::
+`ion-modal` works under the assumption that stacked modals are the same size. As a result, each subsequent modal will have no box shadow and a backdrop opacity of `0`. This is to avoid the effect of shadows and backdrops getting darker with each added modal. This can be changed by setting the `--box-shadow` and `--backdrop-opacity` CSS variables:
-```
+```css
ion-modal.stack-modal {
--box-shadow: 0 28px 48px rgba(0, 0, 0, 0.4);
--backdrop-opacity: var(--ion-backdrop-opacity, 0.32);
}
```
-import ThemeExample from '@site/static/usage/v8/modal/styling/theming/index.mdx';
-
-
-
-### Animations
+## Animations
The enter and leave animations can be customized by using our animation builder and assigning animations to `enterAnimation` and `leaveAnimation`.