Modal

Display a separate dialog to focus attention on the current task.

Live preview
Interact with the component to see results here.
View code
HTML
<sy-modal
  closable
  oktext="Confirm"
  canceltext="Cancel"
  width="300"
  variant="dialog"
  animation="none"
  id="sample"
><span slot="header">Project settings</span>
  <p slot="body">Review the settings before continuing. Data outside this
    example is unaffected.</p>
</sy-modal>
<div class="demo-actions"
  style="display:flex;flex-wrap:wrap;gap:8px;margin-top:16px"
>
  <sy-button id="action-0" size="small">Open dialog</sy-button>
</div>
<output id="demo-result" aria-live="polite"></output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? 'Done';
  const actionTarget0 = document.querySelector("#sample");
  document.querySelector('#action-0').addEventListener('click', async () => {
    const result = await actionTarget0.setOpen();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
</script>

Usage guide

#

Use when users must pause their current task to confirm a decision or complete limited input. Use Popconfirm for brief confirmations and Modeless for parallel work.

  1. Present the information needed for a decision

    Separate header, body, and footer slots, and describe actions clearly with okText and cancelText. Use hideFooter to hide the default footer.

  2. Open and close a dialog

    Open with setOpen() and finish with setOk(), setCancel(), or setClose().

  3. Handle the reason for closing

    Read ok, cancel, or close from closed.detail.event, along with value.

Examples

#

Confirmation dialog

Distinguish confirmation from cancellation results.

View code
HTML
<sy-modal
  closable
  oktext="Confirm"
  canceltext="Cancel"
  width="300"
  id="sample"
><span slot="header">Project settings</span>
  <p slot="body">Review the settings before continuing. Data outside this
    example is unaffected.</p>
</sy-modal>
<div class="demo-actions"
  style="display:flex;flex-wrap:wrap;gap:8px;margin-top:16px"
>
  <sy-button id="action-0" size="small">Open dialog</sy-button>
</div>
<output id="demo-result" aria-live="polite"></output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? 'Done';
  const actionTarget0 = document.querySelector("#sample");
  document.querySelector('#action-0').addEventListener('click', async () => {
    const result = await actionTarget0.setOpen();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
</script>

Dismissal behavior

Allow dismissal using the close button or backdrop click.

View code
HTML
<sy-modal
  closable
  oktext="Confirm"
  canceltext="Cancel"
  width="300"
  maskclosable
  id="sample"
><span slot="header">Project settings</span>
  <p slot="body">Review the settings before continuing. Data outside this
    example is unaffected.</p>
</sy-modal>
<div class="demo-actions"
  style="display:flex;flex-wrap:wrap;gap:8px;margin-top:16px"
>
  <sy-button id="action-0" size="small">Open dialog</sy-button>
</div>
<output id="demo-result" aria-live="polite"></output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? 'Done';
  const actionTarget0 = document.querySelector("#sample");
  document.querySelector('#action-0').addEventListener('click', async () => {
    const result = await actionTarget0.setOpen();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
</script>

Maximize

Enable the maximize button so users can view more content.

View code
HTML
<sy-modal
  closable
  oktext="Confirm"
  canceltext="Cancel"
  width="300"
  enablemodalmaximize
  id="sample"
><span slot="header">Project settings</span>
  <p slot="body">Review the settings before continuing. Data outside this
    example is unaffected.</p>
</sy-modal>
<div class="demo-actions"
  style="display:flex;flex-wrap:wrap;gap:8px;margin-top:16px"
>
  <sy-button id="action-0" size="small">Open dialog</sy-button>
</div>
<output id="demo-result" aria-live="polite"></output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? 'Done';
  const actionTarget0 = document.querySelector("#sample");
  document.querySelector('#action-0').addEventListener('click', async () => {
    const result = await actionTarget0.setOpen();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
</script>

Return a result

Close programmatically while returning a confirmation result.

View code
HTML
<sy-modal
  closable
  width="300"
  hidefooter
  id="sample"
><span slot="header">Return a result</span>
  <div slot="body">
    <p>Click the button to return the example result.</p>
    <sy-button id="confirm-modal">Selection complete</sy-button>
  </div>
</sy-modal>
<div class="demo-actions"
  style="display:flex;flex-wrap:wrap;gap:8px;margin-top:16px"
>
  <sy-button id="action-0" size="small">Open dialog</sy-button>
</div>
<output id="demo-result" aria-live="polite"></output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? 'Done';
  const actionTarget0 = document.querySelector("#sample");
  document.querySelector('#action-0').addEventListener('click', async () => {
    const result = await actionTarget0.setOpen();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  await ((root) => {
    const modal = root.querySelector('#sample');
    modal.querySelector('#confirm-modal').addEventListener('click', () => modal.setOk({
      selected: 'design'
    }));
  })(document);
</script>

Transition effect

Apply effects as the dialog enters and exits.

View code
HTML
<sy-modal
  closable
  oktext="Confirm"
  canceltext="Cancel"
  width="300"
  animation="fade-scale"
  id="sample"
><span slot="header">Project settings</span>
  <p slot="body">Review the settings before continuing. Data outside this
    example is unaffected.</p>
</sy-modal>
<div class="demo-actions"
  style="display:flex;flex-wrap:wrap;gap:8px;margin-top:16px"
>
  <sy-button id="action-0" size="small">Open dialog</sy-button>
</div>
<output id="demo-result" aria-live="polite"></output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? 'Done';
  const actionTarget0 = document.querySelector("#sample");
  document.querySelector('#action-0').addEventListener('click', async () => {
    const result = await actionTarget0.setOpen();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
</script>

Events and payloads

Interact with the component and inspect the latest event name and detail. The code includes subscriptions to events emitted by this component and its children.

View code
HTML
<sy-modal
  closable
  oktext="Confirm"
  canceltext="Cancel"
  width="300"
  variant="dialog"
  animation="none"
  id="sample"
><span slot="header">Project settings</span>
  <p slot="body">Review the settings before continuing. Data outside this
    example is unaffected.</p>
</sy-modal>
<div class="demo-actions"
  style="display:flex;flex-wrap:wrap;gap:8px;margin-top:16px"
>
  <sy-button id="action-0" size="small">Open dialog</sy-button>
</div>
<output id="demo-result" aria-live="polite"></output>
<output id="demo-events" aria-live="polite">Interact with the component to see
  its latest event and payload.</output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? 'Done';
  const actionTarget0 = document.querySelector("#sample");
  document.querySelector('#action-0').addEventListener('click', async () => {
    const result = await actionTarget0.setOpen();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  for (const name of ["closed"]) {
    document.addEventListener(name, event => {
      document.querySelector('#demo-events').textContent =
        `${event.target.localName} · ${name}: ${describeResult(event.detail)}`;
    });
  }
</script>

Dialog height

Set height to control the dialog's height. Leave enough room for the body to scroll when content is long.

View code
HTML
<sy-modal
  closable
  oktext="Confirm"
  canceltext="Cancel"
  width="300"
  variant="dialog"
  animation="none"
  height="320"
  id="sample"
><span slot="header">Project settings</span>
  <p slot="body">Review the settings before continuing. Data outside this
    example is unaffected.</p>
</sy-modal>
<div class="demo-actions"
  style="display:flex;flex-wrap:wrap;gap:8px;margin-top:16px"
>
  <sy-button id="action-0" size="small">Open dialog</sy-button>
</div>
<output id="demo-result" aria-live="polite"></output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? 'Done';
  const component1 = document.querySelector("#sample");
  Object.assign(component1, {
    "height": 320
  });
  const actionTarget0 = document.querySelector("#sample");
  document.querySelector('#action-0').addEventListener('click', async () => {
    const result = await actionTarget0.setOpen();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
</script>

Content-only dialog

Hide footer actions and keep the close control in the dialog header.

View code
HTML
<sy-modal
  closable
  oktext="Confirm"
  canceltext="Cancel"
  width="420"
  variant="dialog"
  hidefooter
  animation="none"
  id="sample"
><span slot="header">Project settings</span>
  <p slot="body">Review the settings before continuing. Data outside this
    example is unaffected.</p>
</sy-modal>
<div class="demo-actions"
  style="display:flex;flex-wrap:wrap;gap:8px;margin-top:16px"
>
  <sy-button id="action-0" size="small">Open dialog</sy-button>
</div>
<output id="demo-result" aria-live="polite"></output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? 'Done';
  const component1 = document.querySelector("#sample");
  Object.assign(component1, {
    "hideFooter": true,
    "closable": true,
    "width": 420
  });
  const actionTarget0 = document.querySelector("#sample");
  document.querySelector('#action-0').addEventListener('click', async () => {
    const result = await actionTarget0.setOpen();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
</script>

API reference

#

Hover over a name or focus it with the keyboard to see the code. On touch screens, tap the name.

<sy-modal>

Properties

Attribute / PropertyTypeDefaultDescription
string''Cancel button text.
booleanfalseShow the close button.
booleanfalseAllow the dialog to be maximized.
booleanfalseHide the default footer actions.
booleanfalseClose when the backdrop is clicked.
booleanfalseOmit the backdrop mask.
string''Confirmation button text.
booleanfalseWhether the submenu is expanded.
number0Dialog width in pixels. 0 uses the default size.
number0Dialog height in pixels. 0 fits the content.
string'-1'Position from the top of the screen. -1 centers it automatically.
string'-1'Position from the left of the screen. -1 centers it automatically.
'modal' | 'dialog''dialog'modal or dialog style.
| 'none' | 'fade' | 'fade-scale' | 'zoom' | 'pop' | 'slide-top' | 'slide-bottom' | 'slide-left' | 'slide-right' | 'slide-fade-top' | 'slide-fade-bottom' | 'slide-fade-left' | 'slide-fade-right' | 'reveal-top' | 'reveal-bottom' | 'reveal-left' | 'reveal-right' | 'flip-x' | 'flip-y' | 'bounce' | 'spin''none'Enter/exit animation type. `none` (default) toggles instantly. Direction-aware values are explicit (`slide-top`, `slide-fade-left`, `reveal-bottom`, …) because the modal has no `position` attribute to derive direction from. Direction-less values (`fade`, `pop`, `flip-*`, `bounce`, `spin`) play the same regardless of orientation.
'x-fast' | 'fast' | 'medium' | 'slow' | 'x-slow''fast'Animation playback speed token. Maps to `--transition-*` global tokens.

Events

EventDetailDescription
CustomEvent<{ event: 'ok' | 'cancel' | 'close'; value: any; maximized: boolean; position: { top: string; left: string }; }>Fires when the modal closes. `detail.event` distinguishes ok / cancel / close.

Methods

MethodReturnsDescription
Promise<void>Expand the submenu.
Promise<void>Close the item.
Promise<void>Close the dialog with a cancellation result.
Promise<void>Close the dialog with a confirmation result.
Promise<void>Maximize the window.

Slots

SlotDescription
Content displayed in the header area.
Content displayed in the body area.
Content displayed in the footer area.