Fylgja Confirmation Modal

A confirmation modal asks the reader to make a choice before something continues. It suits moments that need a clear yes or no, like a cookie consent prompt, where the reader should not drift past the question without answering.

Like the Modal, it is built on the native <dialog> element and opened with an invoker command. Two things set it apart: the buttons sit in a <form method="dialog">, so pressing one both closes the dialog and records the reader’s choice, and closedby="none" disables casual dismissal, so neither Escape nor a backdrop click closes it. The content is classless, with the two choices sharing the row as equal-width buttons.

Preview

We use cookies to improve your experience and analyze site traffic. Do you accept?

<button type="button" commandfor="cookie-consent" command="show-modal">Cookie settings</button>

<dialog
	id="cookie-consent"
	closedby="none"
	aria-labelledby="cookie-consent-title"
	style="--my: auto var(--size-4); --md_my: auto var(--size-8)"
>
	<form method="dialog" class="flow">
		<h2 id="cookie-consent-title">Accept cookies?</h2>
		<p>We use cookies to improve your experience and analyze site traffic. Do you accept?</p>
		<div class="flex-wrap gap">
			<button value="decline" class="flex-auto">Decline</button>
			<button value="accept" class="flex-auto">Accept</button>
		</div>
	</form>
</dialog>

Usage

  1. A trigger button with commandfor and command="show-modal" opens the dialog, no JavaScript required.
  2. Inside the dialog, a <form method="dialog"> wraps the question and the two choices. Each choice is a <button> with a value.
  3. Pressing either button submits the form, which closes the dialog and stores the pressed button’s value on dialog.returnValue.

The two choices sit in a <div class="flex-wrap gap">, and each button gets flex-auto so they share the row as equal-width actions and wrap on narrow screens. Give each choice a clear, specific label (“Accept”, “Decline”) rather than a generic “OK”, so the answer is unambiguous.

closedby="none" stops the dialog closing on Escape or a backdrop click, so the reader cannot skip past the question without answering. Because you are removing the usual escape hatch, make sure one option is a safe, non-committal choice (here, Decline), and reserve this pattern for cases where a decision is genuinely required. For an everyday, freely dismissible dialog, use the plain Modal.

The Alpine version

The Alpine tab above opens the same <dialog> from Alpine state with x-htmldialog. That directive comes from the AlpineJS Dialog plugin, whose docs cover installing it and the modifiers it takes.

Reacting to the choice

Closing the dialog is declarative, but acting on the answer needs a small <script>. Listen for the close event and check returnValue.

<script>
  const dialog = document.getElementById("cookie-consent");
  dialog.addEventListener("close", () => {
    if (dialog.returnValue === "accept") {
      // Enable analytics and store the consent cookie here.
    }
  });
</script>

Anatomy

Part Responsibility
Trigger <button command> Opens the dialog via command="show-modal" and commandfor.
<dialog> The container, rendered in the top layer over an inert page.
<form method="dialog"> Closes the dialog on submit and reports the chosen value.
<button value> One choice. Its value becomes dialog.returnValue.
.flex-wrap.gap + .flex-auto Row wrapper; flex-auto makes the choices equal-width.

Accessibility

  • Opened as a modal, the dialog makes the rest of the page inert and traps focus.
  • On close, focus returns to the trigger automatically.
  • closedby="none" intentionally disables Escape and backdrop dismissal, so the reader has to pick an option. Make sure one of them is a safe, non-committal choice (here, Decline), since it is the only way to leave without opting in.
  • Label the dialog with aria-labelledby pointing at the heading, and make the question state plainly what each choice does.

FAQ

How is this different from a plain modal?

A plain Modal shows content the reader simply dismisses. A confirmation modal asks a yes or no question, such as a cookie consent, and reports which choice was made. Use it when the reader has to decide before continuing.

How does the modal know which button was pressed?

The buttons live inside a <form method="dialog"> and each has a value. Submitting the form closes the dialog and stores that value on the dialog's returnValue, which you can read in a close event. See Reacting to the choice.

Do I need JavaScript for the buttons to close the dialog?

No. Buttons inside a <form method="dialog"> close the dialog when pressed, with no script. JavaScript is only needed to react to the choice after the dialog closes.

What does the Alpine version need to work?

The x-htmldialog directive comes from the @fylgja/alpinejs-dialog plugin, which makes x-show call showModal instead of toggling display, so you keep the top layer, the backdrop and the focus trap. The example markup leaves the setup out on purpose; install and registration live in the AlpineJS Dialog docs.