GitHub

modal / modalTrigger

A <dialog>-based modal with backdrop, animated open/close, and keyboard dismiss. The Pulse runtime handles open and close natively — no extra script needed.

Confirm action

Are you sure you want to delete this item? This action cannot be undone.

// Render the dialog somewhere on the page
modal({
  id:      'confirm-delete',
  title:   'Confirm action',
  content: '<p>Are you sure? This cannot be undone.</p>',
  footer:
    button({ label: 'Cancel', variant: 'secondary' }) +
    button({ label: 'Delete', variant: 'danger' }),
})

// Open it with a trigger button (or any element with data-dialog-open)
modalTrigger({ target: 'confirm-delete', label: 'Delete item', variant: 'danger' })

Sizes

Small modal

A compact dialog for quick confirmations.

Large modal

Use large modals for forms, rich content, or detail views that need more space.

modal({ id: 'my-modal', title: 'Large modal', size: 'lg', content: '...' })
PropTypeDefault
idstringUnique ID — required for triggers to target this dialog
titlestring
levelnumber2Heading tag for the title (1–6). Visual style is unchanged.
contentstring (HTML)Body HTML
footerstring (HTML)Footer HTML — typically button() calls
sizesm | md | lg | xlmd
classstring

modalTrigger props

PropTypeDefault
targetstringThe modal's id
labelstringOpen
variantprimary | secondary | ghost | dangerprimary
sizesm | md | lgmd
classstring

Custom triggers

Any element with data-dialog-open="<id>" opens the dialog when clicked. Use data-dialog-close on any element inside or outside the dialog to close it programmatically:

<button data-dialog-open="my-modal">Open</button>
<button data-dialog-close>Cancel</button>
The dialog also closes on ESC, backdrop click, and <form method="dialog"> submit — all native browser behaviour, no JavaScript needed.

Forms inside a modal

modal() wraps all content in <form method="dialog"> for native close behaviour. You cannot nest a <form data-action="..."> inside it — browsers silently discard nested forms, so the action will never fire.

When a modal button needs to trigger a Pulse action, place the form outside the modal and use the HTML form attribute to associate the button with it:

// The action form lives outside the modal — hidden, no visible fields needed
<form id="delete-form" data-action="deleteAccount" style="display:none"></form>

${modal({
  id:      'confirm-delete',
  title:   'Delete account?',
  content: '<p>This cannot be undone.</p>',
  footer:
    // type="submit" with no form= closes the modal natively (submits <form method="dialog">)
    button({ label: 'Cancel',         variant: 'secondary', type: 'submit' }) +
    // form="delete-form" associates this button with the external form → fires the action
    button({ label: 'Confirm delete', variant: 'danger', attrs: { form: 'delete-form', type: 'submit' } }),
})}
The hidden form needs no visible fields. onStart receives state and formData — read anything you need from state directly.