Modals Dialogs and Alerts

<dialog> demos and exploration to solving different problems.

Basic Dialog

A dialog is a basic popup window.

  • Opened with .show()
  • It doesn’t take over the main content, focus is not trapped
  • There is no backdrop
  • Not on top layer

Login

<script>
	const trigger = document.querySelector('#demo-dialog-open');
	const dialog = document.querySelector('#demo-dialog');
	trigger.addEventListener('click', () => {
		dialog.show();
	});
</script>

<button id="demo-dialog-open">Open</button>
<dialog id="demo-dialog">
	<h3>Login</h3>
	<form method="dialog">
		<input type="text" placeholder="username" />
		<input type="password" placeholder="password" />
		<button type="submit">Login</button>
	</form>
</dialog>

Basic Modal

A dialog that takes over.

  • Opened with .showModal()
  • Blocks interaction of rest of page like alert()
  • Focus is trapped withing the dialog
  • On top layer
  • Esc closes

Login

<script>
	const trigger = document.querySelector('#demo-modal-open');
	const dialog = document.querySelector('#demo-modal');
	trigger.addEventListener('click', () => {
		dialog.showModal();
	});
</script>

<button id="demo-modal-open">Open</button>
<dialog id="demo-modal">
	<p>Login</p>
	<form method="dialog">
		<input type="text" placeholder="username" />
		<input type="password" placeholder="password" />
		<button type="submit">Login</button>
	</form>
</dialog>

Side Note - Top Layer

Top layer is the fix for your z-index woes. It takes your content entirely out of the context of stacking order of CSS. This puts anything on top layer on top of all elements regardless of z-index. It creates a new “stacking context”.

  • .showModal() puts in top layer
  • .show() uses z-index

Alert

Nothing too fancy to make an alert, the dialog with showModal is the way to go here.

Stay Up to Date

<script>
	const trigger = document.getElementById('demo-dialog-alert-open');
	const dialog = document.getElementById('demo-dialog-alert');
	trigger.addEventListener('click', () => {
		dialog.showModal();
	});
</script>

<button id="demo-dialog-alert-open">Remind Me</button>
<dialog id="demo-dialog-alert">
	<h3>Stay Up to Date</h3>
	<form method="dialog">
		<button type="submit">Allow Notifications</button>
		<button class="close" type="reset">Close</button>
	</form>
</dialog>

Can I use Dialog today?

✅ YES 96% Support


Animation

The JavaScript Solution

To animate a dialog with JS, you can use any number of your own JS framework libraries, or you can do it directly with the view transitions API. This approach is much less supported but requires no additional libraries.

Oh hey, I didn't see you there. I'm looking for some new 😎 code, can you help?

<script>
	const dialog = document.querySelector('#demo-dialog-vt');
	const open = document.querySelector('#demo-dialog-vt-open');
	const form = document.querySelector('#demo-dialog-vt form');

	open.addEventListener('click', open_dialog);
	form.addEventListener('submit', close_dialog);

	function open_dialog() {
		document.startViewTransition(() => {
			dialog.showModal();
		});
	}

	function close_dialog(e) {
		e.preventDefault();
		const form = e.target;
		document.startViewTransition(() => {
			form.submit();
		});
	}
</script>

<button id="demo-dialog-vt-open">Open Dialog</button>
<dialog id="demo-dialog-vt">
	<p>Oh hey, I didn't see you there. I'm looking for some new 😎 code, can you help?</p>
	<form method="dialog">
		<button>OK 👍</button>
	</form>
</dialog>

The CSS Solution

Oh hey, I didn't see you there. I'm looking for some new 😎 code, can you help?

<script>
	const dialog = document.getElementById('demo-dialog-css');
	const open = document.getElementById('demo-dialog-css-open');
	open.addEventListener('click', open_dialog);
	function open_dialog() {
		dialog.showModal();
	}
</script>

<button id="demo-dialog-css-open">Open Dialog</button>
<dialog id="demo-dialog-css">
	<p>Oh hey, I didn't see you there. I'm looking for some new 😎 code, can you help?</p>
	<form method="dialog">
		<button>OK 👍</button>
	</form>
</dialog>

<style>
	#demo-dialog-css {
		&,
		::backdrop {
			transition:
				display 0.3s allow-discrete,
				overlay 0.3s allow-discrete,
				translate 0.3s,
				opacity 0.3s;
			opacity: 0;
			translate: 0 20px;
		}

		&[open],
		&[open]::backdrop {
			opacity: 1;
			translate: 0 0;
		}

		@starting-style {
			&[open],
			&[open]::backdrop {
				opacity: 0;
				translate: 0 20px;
			}
		}
	}
</style>

Side Note - Starting Style

@starting-style allows you to set the initial state of something that will transition in when it’s added to the DOM. Why would you need this? Image a situation where a new DOM element is plopped into the page, Right now the solution is to add a pre-transition-in class then a transition-in class with JavaScript after time. Now you can do that all with CSS. Also this enables one of the holy grails of CSS, animating display:none; 🤯

It can wrap selectors or exist inside of selectors.

Pop this in with <Dialog> or a popover and you’ve got a stew goin’.

Can I use @starting-style? - ❔ Possibly 70% support

Some nuance there. It’s not supported in Firefox and given Firefox’s pace lately, who knows. It will gracefully fallback and not animate, so in that regard, only FF users on desktop aren’t getting the full experience and that’s ok with me.

Side Note - allow-discrete

You may have noticed display 1s allow-discrete in the previous example. Try running that code and removing the allow-discrete in your own code. You’ll see that at first glance the animation still works. So what gives?

Well, close the dialog and see the issue. When dialog is toggled, display:none; is removed. Since it’s instantly removed, the intro animation still works with just @starting-style, however when closing the dialog, display:none; is applied immediately and therefore there is no animation on close. ☹️.

Can I use allow-discrete? - YES Possibly 77% support

Firefox support coming August 6th, is in Nightly already.

Interesting Reading

Should we deprecate dialog.show()