# Modal

**`sonic-modal`** shows focused content in a dialog with a backdrop, an enter / exit animation and a built-in close button. Compose it with **`sonic-modal-title`** · **`sonic-modal-subtitle`** · **`sonic-modal-content`** · **`sonic-modal-actions`**, open it with **`show()`** and close it with **`hide()`** or a **`hideModal`** button.

## Basic example

Backdrop click, Escape and the close button all close the modal. In **`sonic-modal-actions`**, a **`sonic-button`** with **`hideModal`** closes it on click.

<sonic-code>
  <template>
    <sonic-button onclick="document.getElementById('modalExample').show()">
      Open modal
    </sonic-button>
    <sonic-modal id="modalExample">
      <sonic-modal-title>
        Boarding pass ready
        <sonic-badge type="success" size="sm">New</sonic-badge>
      </sonic-modal-title>
      <sonic-modal-subtitle>Your check-in is complete.</sonic-modal-subtitle>
      <sonic-modal-content>
        Download your boarding pass now or review your seat before departure.
      </sonic-modal-content>
      <sonic-modal-actions>
        <sonic-button minWidth="8rem" hideModal>Later</sonic-button>
        <sonic-button minWidth="8rem" type="success" hideModal>Download</sonic-button>
      </sonic-modal-actions>
    </sonic-modal>
  </template>
</sonic-code>

**`hideModal`** only works on **`sonic-button`** elements that are direct children of **`sonic-modal-actions`**.

## Scrollable content

Long content scrolls inside the dialog once it reaches **`maxHeight`** (default `90vh`). Content is left-aligned by default; set **`align`** to `center` or `right`.

<sonic-code>
  <template>
    <sonic-button onclick="document.getElementById('modalText').show()">Baggage details</sonic-button>
    <sonic-modal id="modalText" maxHeight="20rem">
      <sonic-modal-title>Baggage allowance</sonic-modal-title>
      <sonic-modal-content>
        <div class="prose">
          <p><strong>Cabin bag</strong><br />One cabin bag and one personal item are included in every cabin class.</p>
          <p><strong>Checked baggage</strong><br />Drop checked bags at the counter at least one hour before departure.</p>
          <p><strong>Extra baggage</strong><br />Add extra baggage online up to 24 hours before the flight.</p>
          <p><strong>Contact</strong><br />support@example.com</p>
        </div>
      </sonic-modal-content>
    </sonic-modal>
  </template>
</sonic-code>

## Force action

With **`forceAction`**, backdrop click, Escape and the close button are disabled. Provide **`hideModal`** buttons so users can finish the flow. **`noCloseButton`** only hides the close button.

<sonic-code>
  <template>
    <sonic-button onclick="document.getElementById('force').show()">Cookie consent</sonic-button>
    <sonic-modal forceAction id="force">
      <sonic-modal-title>Allow analytics cookies?</sonic-modal-title>
      <sonic-modal-content>
        Analytics cookies measure traffic. You can change this later in settings.
      </sonic-modal-content>
      <sonic-modal-actions>
        <sonic-button hideModal minWidth="8rem">Decline</sonic-button>
        <sonic-button hideModal minWidth="8rem" type="success">Accept</sonic-button>
      </sonic-modal-actions>
    </sonic-modal>
  </template>
</sonic-code>

## Dimensions

Set **`width`** · **`height`** · **`maxWidth`** · **`maxHeight`** with CSS lengths. Below 768px the panel becomes a bottom sheet. **`rounded="none"`** removes the corner radius for edge-to-edge media.

<sonic-code>
  <template>
    <sonic-button onclick="document.getElementById('CustomWidth').show()">Image preview</sonic-button>
    <sonic-modal
      rounded="none"
      maxHeight="50vh"
      maxWidth="90vw"
      width="100%"
      height="100%"
      id="CustomWidth"
    >
      <sonic-image
        cover
        src="/img/concorde/plane_12.jpg"
        alt="Concorde at the gate with boarding stairs"
      ></sonic-image>
    </sonic-modal>
  </template>
</sonic-code>

## Fullscreen

**`fullscreen`** fills the viewport and hides the backdrop.

<sonic-code>
  <template>
    <sonic-button onclick="document.getElementById('Fullscreen').show()">Fullscreen</sonic-button>
    <sonic-modal fullscreen id="Fullscreen">
      <sonic-image
        cover
        src="/img/concorde/plane_10.jpg"
        alt="Concorde fleet parked in a star"
      ></sonic-image>
    </sonic-modal>
  </template>
</sonic-code>

## Programmatic creation

**`Modal.create(options)`** (also `window.SonicModal.create`) appends a modal to the theme pop container, opens it and returns the element. **`title`** · **`subtitle`** · **`content`** · **`actions`** accept HTML strings or Lit templates. Set **`removeOnHide: true`** to remove it from the DOM after closing.

<sonic-code>
  <template>
    <sonic-button
      onclick="window.SonicModal.create({
        title: 'Announce gate change?',
        subtitle: 'Passengers are notified immediately.',
        content: 'Flight CN 001 now departs from gate B12.',
        actions: '&lt;sonic-button hideModal minWidth=\'8rem\'&gt;Cancel&lt;/sonic-button&gt;&lt;sonic-button hideModal minWidth=\'8rem\' type=\'success\'&gt;Send&lt;/sonic-button&gt;',
        removeOnHide: true
      })"
    >
      Open programmatic modal
    </sonic-button>
  </template>
</sonic-code>

<sonic-code language="typescript">
  <template>
import { Modal } from "@supersoniks/concorde/ui/modal";

const modal = Modal.create({
  title: "Remove flight CN 001?",
  content: "It will no longer appear on the departures board.",
  actions: `
    &lt;sonic-button hideModal&gt;Cancel&lt;/sonic-button&gt;
    &lt;sonic-button hideModal type="danger"&gt;Remove&lt;/sonic-button&gt;
  `,
  removeOnHide: true,
  maxWidth: "min(100vw, 28rem)",
});

modal.addEventListener("hidden", () => console.log("closed"));
  </template>
</sonic-code>

## API reference

### `sonic-modal`

| Attribute | Values | Description |
|----------|---------|-------------|
| `align` | `left` (default) · `center` · `right` | Content alignment |
| `width` | CSS length (default `100%`) | Dialog width |
| `height` | CSS length (default `fit-content`) | Dialog height |
| `maxWidth` | CSS length (default `min(100vw, 40rem)`) | Dialog max width |
| `maxHeight` | CSS length (default `90vh`) | Dialog max height; content scrolls beyond |
| `paddingX` · `paddingY` | CSS length (default `1.5rem` · `2.5rem`) | Content padding |
| `zIndex` | number (default `990`) | Stacking order |
| `effect` | `slide` (default) · `fade` · `none` | Open / close animation |
| `fullscreen` | boolean | Fill the viewport, no backdrop |
| `rounded` | `none` | Remove the corner radius |
| `forceAction` | boolean | Disable backdrop click, Escape and close button |
| `noCloseButton` | boolean | Hide the close button only |
| `closeButtonType` | `sonic-button` type | Color of the close button |
| `visible` | boolean | Opens or closes the modal when toggled |
| `removeOnHide` | boolean | Remove the element after closing |
| `removeHashOnHide` | boolean | Clear the URL hash after closing |
| `closeOnLocationChange` | boolean | Close when the location changes |
| `resetDataProviderOnHide` | publisher id | Reset this data provider to `{}` after closing |

JavaScript-only properties: `options` (same keys as `Modal.create`) and `styleSheet` (`CSSResult` or `CSSStyleSheet`, or an array, adopted in the shadow root).

| Slot | Description |
|------|-------------|
| *(default)* | Modal content, usually the `sonic-modal-*` children |

| Method | Description |
|--------|-------------|
| `show()` | Open the modal |
| `hide()` | Close the modal (returns a promise) |
| `dispose()` | Close and remove the modal |
| `Modal.create(options)` | Static: create, append and open a modal |
| `Modal.disposeAll()` | Static: close and remove every modal |

| Event | Description |
|-------|-------------|
| `show` | Open animation finished |
| `hide` | Close started |
| `hidden` | Close finished |

### `sonic-modal-title` · `sonic-modal-subtitle` · `sonic-modal-content` · `sonic-modal-actions`

No attributes. Default slot only. In `sonic-modal-actions`, child `sonic-button` elements with `hideModal` close the modal.

### `sonic-modal-close`

Rendered automatically unless `forceAction` or `noCloseButton` is set. Place it manually to add a close button elsewhere.

| Attribute | Values | Description |
|----------|---------|-------------|
| `type` | `sonic-button` type | Button color |
| `reset` | publisher id | Passed to the inner `sonic-button` `reset` |
| `translation` | label object | Overrides the default `aria-label` |
