The Forms 2.0 lightbox() method is fully functional, but commonly misused.[1] I’ve had a post in my drafts for ages — “Tips for a smoother lightbox() experience” — but in the meantime lightbox() has become unnecessary because all modern browsers support <dialog>.
IMO, the coolest things about <dialog> are:
- built-in responsiveness: desktop and mobile browsers automatically position the modal correctly
- native controls: the Escape key and back-swipe both close the dialog
- easier markup and styling: no need for extra
<div>s andz-indexjuggling, just style::backdropand::backdrop-filter
Secondarily, <dialog> minimizes the need for JavaScript. The JS in this post is only necessary because Safari doesn’t support Invoker Commands yet. In Chrome and Firefox, you can close and open the modal using HTML alone, no extra JS!
Here’s a modal Marketo form inside a <dialog>:

The markup is amazingly simple, just put the standard form embed inside a <dialog> and add a close <button>[2]:
<dialog id="mktoFormDialog_1549">
<!-- standard form embed -->
<script src="//app-sj01.marketo.com/js/forms2/js/forms2.min.js"></script>
<form id="mktoForm_1549"></form>
<script>MktoForms2.loadForm("//app-sj01.marketo.com", "123-XOR-456", 1549);</script>
<!-- /standard form embed -->
<button type="button" commandfor="mktoFormDialog_1549" command="close">✖</button>
</dialog>Then anywhere else on the page, add a <button> (or multiple <button>s) that open the <dialog>:
<button type="button" commandfor="mktoFormDialog_1549" command="show-modal">Show modal form</button>The above works without JS in Chrome/Edge/Firefox, including Android versions. Gotta support Safari too, though. This bit of code maps commandfor and command attributes to corresponding JS methods:
/* only required in Safari (a/o 2025-10-26) */
if( typeof CommandEvent !== "function" ) {
MktoForms2.whenReady(function(readyForm){
const formEl = readyForm.getFormElem()[0];
const dialog = formEl.closest("dialog");
if( dialog ) {
const invokers = document.querySelectorAll(`button[commandfor="${CSS.escape(dialog.id)}"]`);
invokers.forEach( (invoker) => {
switch(invoker.getAttribute("command")){
case "show-modal":
invoker.onclick = () => dialog.showModal();
break;
case "close":
invoker.onclick = () => dialog.close();
break;
}
});
}
});
}Note <dialog> on its own is supported on Safari, it’s the JS-less Invoker Commands that aren’t there yet.
Notes
[1] The common lightbox() mistake is not rendering the form until the modal is opened — in extreme cases, not even loading forms2.min.js until it’s opened! This adds a user-facing delay. You should instead render the form immediately, hiding it using CSS, and show it when the lightbox is open:
.mktoForm {
visibility: hidden;
position: absolute;
}
.mktoModal .mktoForm {
visibility: visible;
position: static;
}But lightbox() should really be considered obsolete.
[2] My only frustration with <dialog> is its lack of an automatically rendered close button. In the screenshot above, the <button command="close"> is positioned using CSS position: absolute; right: 0; top:0;. Easy enough, but would be nice to do something like <dialog closebutton="true"> for a built-in ❎. Not as customizable but might help adoption for those who know zero CSS.