Usage

Use showModal() to open the dialog as a modal (with backdrop and focus trap). Use close() or a <form method="dialog"> to close it.

Dialog title

A short description of the dialog content.

Dialog body content goes here.

<button class="zui-button" onclick="document.getElementById('dialog-demo').showModal()">
Open dialog
</button>

<dialog id="dialog-demo" class="zui-dialog" closedby="any">
<div class="zui-dialog-header">
  <h2 class="zui-dialog-title">Dialog title</h2>
  <p class="zui-dialog-description">A short description of the dialog content.</p>
</div>
<div class="zui-dialog-body">
  <p>Dialog body content goes here.</p>
</div>
<div class="zui-dialog-footer">
  <form method="dialog">
    <button class="zui-button zui-button-variant-outline">Cancel</button>
  </form>
  <button class="zui-button" onclick="document.getElementById('dialog-demo').close()">Confirm</button>
</div>
</dialog>

Sizes

Control the dialog width with size variants.

Small dialog

Max width 24rem.

Medium dialog

Max width 32rem (default).

Large dialog

Max width 42rem.

Full dialog

Full viewport size.

<button class="zui-button" onclick="document.getElementById('dialog-sm').showModal()">Small</button>
<button class="zui-button" onclick="document.getElementById('dialog-md').showModal()">Medium (default)</button>
<button class="zui-button" onclick="document.getElementById('dialog-lg').showModal()">Large</button>
<button class="zui-button" onclick="document.getElementById('dialog-full').showModal()">Full</button>

<dialog id="dialog-sm" class="zui-dialog zui-dialog-size-sm">
<div class="zui-dialog-header">
  <h2 class="zui-dialog-title">Small dialog</h2>
</div>
<div class="zui-dialog-body"><p>Max width 24rem.</p></div>
<div class="zui-dialog-footer">
  <form method="dialog"><button class="zui-button">Close</button></form>
</div>
</dialog>

<dialog id="dialog-md" class="zui-dialog">
<div class="zui-dialog-header">
  <h2 class="zui-dialog-title">Medium dialog</h2>
</div>
<div class="zui-dialog-body"><p>Max width 32rem (default).</p></div>
<div class="zui-dialog-footer">
  <form method="dialog"><button class="zui-button">Close</button></form>
</div>
</dialog>

<dialog id="dialog-lg" class="zui-dialog zui-dialog-size-lg">
<div class="zui-dialog-header">
  <h2 class="zui-dialog-title">Large dialog</h2>
</div>
<div class="zui-dialog-body"><p>Max width 42rem.</p></div>
<div class="zui-dialog-footer">
  <form method="dialog"><button class="zui-button">Close</button></form>
</div>
</dialog>

<dialog id="dialog-full" class="zui-dialog zui-dialog-size-full">
<div class="zui-dialog-header">
  <h2 class="zui-dialog-title">Full dialog</h2>
</div>
<div class="zui-dialog-body"><p>Full viewport size.</p></div>
<div class="zui-dialog-footer">
  <form method="dialog"><button class="zui-button">Close</button></form>
</div>
</dialog>

Positions

Control where the dialog appears with the position variant. The default (center) uses margin: auto to center the dialog in the viewport. The other positions pin the dialog to a viewport edge and animate it into view from that side.

ValueDescription
centerCentered in the viewport (default)
centralCentered horizontally, anchored 60px from the top — useful for command palettes
leftFull-height drawer sliding in from the left
rightFull-height drawer sliding in from the right
topFull-width sheet sliding in from the top
bottomFull-width sheet sliding in from the bottom

central

Anchored 60px from the top of the viewport.

left

Slides in from the left edge.

right

Slides in from the right edge.

top

Slides in from the top edge.

bottom

Slides in from the bottom edge.

<button class="zui-button" onclick="document.getElementById('dialog-pos-central').showModal()">central</button>
<button class="zui-button" onclick="document.getElementById('dialog-pos-left').showModal()">left</button>
<button class="zui-button" onclick="document.getElementById('dialog-pos-right').showModal()">right</button>
<button class="zui-button" onclick="document.getElementById('dialog-pos-top').showModal()">top</button>
<button class="zui-button" onclick="document.getElementById('dialog-pos-bottom').showModal()">bottom</button>

<dialog id="dialog-pos-central" class="zui-dialog zui-dialog-position-central" closedby="any">
<div class="zui-dialog-header">
  <h2 class="zui-dialog-title">central</h2>
  <p class="zui-dialog-description">Anchored 60px from the top of the viewport.</p>
</div>
<div class="zui-dialog-footer">
  <form method="dialog"><button class="zui-button">Close</button></form>
</div>
</dialog>

<dialog id="dialog-pos-left" class="zui-dialog zui-dialog-position-left" closedby="any">
<div class="zui-dialog-header">
  <h2 class="zui-dialog-title">left</h2>
  <p class="zui-dialog-description">Slides in from the left edge.</p>
</div>
<div class="zui-dialog-footer">
  <form method="dialog"><button class="zui-button">Close</button></form>
</div>
</dialog>

<dialog id="dialog-pos-right" class="zui-dialog zui-dialog-position-right" closedby="any">
<div class="zui-dialog-header">
  <h2 class="zui-dialog-title">right</h2>
  <p class="zui-dialog-description">Slides in from the right edge.</p>
</div>
<div class="zui-dialog-footer">
  <form method="dialog"><button class="zui-button">Close</button></form>
</div>
</dialog>

<dialog id="dialog-pos-top" class="zui-dialog zui-dialog-position-top" closedby="any">
<div class="zui-dialog-header">
  <h2 class="zui-dialog-title">top</h2>
  <p class="zui-dialog-description">Slides in from the top edge.</p>
</div>
<div class="zui-dialog-footer">
  <form method="dialog"><button class="zui-button">Close</button></form>
</div>
</dialog>

<dialog id="dialog-pos-bottom" class="zui-dialog zui-dialog-position-bottom" closedby="any">
<div class="zui-dialog-header">
  <h2 class="zui-dialog-title">bottom</h2>
  <p class="zui-dialog-description">Slides in from the bottom edge.</p>
</div>
<div class="zui-dialog-footer">
  <form method="dialog"><button class="zui-button">Close</button></form>
</div>
</dialog>

Close button

Add a close button inside the dialog using .zui-dialog-close.

With close button

A close button is positioned in the top-right corner.

Content goes here.

<button class="zui-button" onclick="document.getElementById('dialog-close-btn').showModal()">
Open dialog
</button>

<dialog id="dialog-close-btn" class="zui-dialog">
<button
  class="zui-button zui-button-variant-ghost zui-button-size-sm zui-button-icon zui-dialog-close"
  onclick="document.getElementById('dialog-close-btn').close()"
  aria-label="Close"
>
  <i class="ph ph-x"></i>
</button>
<div class="zui-dialog-header">
  <h2 class="zui-dialog-title">With close button</h2>
  <p class="zui-dialog-description">A close button is positioned in the top-right corner.</p>
</div>
<div class="zui-dialog-body">
  <p>Content goes here.</p>
</div>
</dialog>

Closing behaviour

The closedby attribute controls how a dialog can be dismissed. ZUI defaults to closedby="any" on all components.

ValueBehaviour
anyClosed by backdrop click or Esc key (default)
closerequestOnly closed by Esc key — backdrop click does nothing
noneCannot be dismissed by backdrop or Esc — requires a button or script

closedby="any"

Click the backdrop or press Esc to close.

closedby="closerequest"

Only Esc closes this dialog — backdrop click does nothing.

closedby="none"

Backdrop and Esc are both disabled — use the button below.

<button class="zui-button" onclick="document.getElementById('dialog-closedby-any').showModal()">any (default)</button>
<button class="zui-button" onclick="document.getElementById('dialog-closedby-closerequest').showModal()">closerequest</button>
<button class="zui-button" onclick="document.getElementById('dialog-closedby-none').showModal()">none</button>

<dialog id="dialog-closedby-any" class="zui-dialog" closedby="any">
<div class="zui-dialog-header">
  <h2 class="zui-dialog-title">closedby="any"</h2>
  <p class="zui-dialog-description">Click the backdrop or press Esc to close.</p>
</div>
<div class="zui-dialog-footer">
  <form method="dialog"><button class="zui-button">Close</button></form>
</div>
</dialog>

<dialog id="dialog-closedby-closerequest" class="zui-dialog" closedby="closerequest">
<div class="zui-dialog-header">
  <h2 class="zui-dialog-title">closedby="closerequest"</h2>
  <p class="zui-dialog-description">Only Esc closes this dialog — backdrop click does nothing.</p>
</div>
<div class="zui-dialog-footer">
  <form method="dialog"><button class="zui-button">Close</button></form>
</div>
</dialog>

<dialog id="dialog-closedby-none" class="zui-dialog" closedby="none">
<div class="zui-dialog-header">
  <h2 class="zui-dialog-title">closedby="none"</h2>
  <p class="zui-dialog-description">Backdrop and Esc are both disabled — use the button below.</p>
</div>
<div class="zui-dialog-footer">
  <form method="dialog"><button class="zui-button">Close</button></form>
</div>
</dialog>

With form

Use <form method="dialog"> for native form submission that closes the dialog and sets returnValue.

Are you sure?

This action cannot be undone.

<button class="zui-button" onclick="document.getElementById('dialog-form').showModal()">
Delete item
</button>

<dialog id="dialog-form" class="zui-dialog">
<div class="zui-dialog-header">
  <h2 class="zui-dialog-title">Are you sure?</h2>
  <p class="zui-dialog-description">This action cannot be undone.</p>
</div>
<div class="zui-dialog-footer">
  <form method="dialog">
    <button class="zui-button zui-button-variant-outline" value="cancel">Cancel</button>
  </form>
  <form method="dialog">
    <button class="zui-button zui-button-color-destructive" value="confirm">Delete</button>
  </form>
</div>
</dialog>

CSS custom properties

PropertyDefaultDescription
--zui-dialog-bgvar(--color-surface)Background colour
--zui-dialog-bordervar(--color-border)Border colour
--zui-dialog-radiusvar(--radius-xl)Border radius
--zui-dialog-shadowvar(--shadow-2xl)Box shadow
--zui-dialog-paddingvar(--space-md)Padding inside the dialog
--zui-dialog-max-width32remMaximum width

Theme

Copy this CSS to your project: