Vanilla UI Kit Modal v1.0.0 · toast → · datepicker →

Dialogs, one file.

Zero-dependency modals built on the native <dialog> element — top layer, focus handled, promises included. Click anything below — every card is live.

<script src="https://cdn.jsdelivr.net/gh/vanilla-ui-kit/components/modal/modal.js"></script>

Basic

Title, text content, a footer — Esc, backdrop, and ✕ all close it.

Modal.open({
  title: 'Welcome aboard',
  content: 'Plain string, rendered as text.',
  buttons: [{ label: 'Got it', variant: 'primary' }]
})

Sizes

Four widths; full takes over the viewport.

Modal.open({ title: 'Large', size: 'lg', … })

Confirm & prompt

Promises resolve to boolean / string|null — the result lands below.

Modal.confirm('Delete this file?')
  .then(ok => ok && remove())

Modal.prompt({ message: 'New name:',
               value: 'untitled.txt' })
  .then(name => name !== null && rename(name))

Custom footer buttons

Variants, close values, and onClick returning false to keep it open.

buttons: [
  { label: 'Discard', variant: 'danger', close: 'discarded' },
  { label: 'Keep editing', onClick: () => false },
  { label: 'Save', variant: 'primary', close: 'saved' }
]

Declarative

No JS: data-vmd-open on the trigger, data-vmd-close inside. The hidden element is adopted and put back on close.

<button data-vmd-open="#newsletter-modal">…</button>

<div id="newsletter-modal" hidden
     data-vmd-title="Stay in the loop">
  …
  <button data-vmd-close>Maybe later</button>
</div>

Non-dismissible

No Esc, no backdrop click, no ✕ — only an explicit choice closes it.

Modal.open({
  title: 'Session expired',
  dismissible: false,
  buttons: [{ label: 'Sign in', variant: 'primary' }]
})