<!doctype html>
<html>
	<head>
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<link href="/assets/reset.css" rel="stylesheet">
		<link href="setup.css" rel="stylesheet">
		<link href="style.css" rel="stylesheet">
		<script defer src="script.js"></script>
	</head>
	<body>
		<button id="modal">Click here!</button>
		<dialog id="dialog">
			<p>This is a modal with an overlay!</p>
			<button>Close it!</button>
		</dialog>
	</body>
</html>

		
index.html
			// Similar to before, setting up variables.
let modalButton = document.querySelector('#modal') // The thing we’re clicking.
let modalDialog = document.querySelector('#dialog') // Now one for our `dialog`.
let closeButton = modalDialog.querySelector('button') // Only looking within `modalDialog`.

modalButton.addEventListener('click', () => { // “Listen” for clicks.
	modalDialog.showModal() // This opens it up.
})

closeButton.addEventListener('click', () => {
	modalDialog.close() // And this closes it!
})

// Listen to *all* clicks, now including the `event` parameter…
document.addEventListener('click', (event) => {
	// Only clicks on the page itself behind the `dialog`.
	if (event.target == document.documentElement) {
		modalDialog.close() // Close it too then.
	}
})

		
script.js
			html, body { block-size: 100%; }

body {
	--base: 1rem;

	display: grid;
	font-family: sans-serif;
	padding: var(--base);
	place-content: center;
	place-items: center;
	row-gap: var(--base);
}

button {
	background-color: deepskyblue;
	border-radius: calc(var(--base) / 2);
	cursor: pointer; /* Show the Mickey-Mouse hand! */
	padding: calc(var(--base) / 2);
}

		
setup.css
			/* Stops the page from scrolling when there is an open one: */
body:has(dialog[open]) { overflow: hidden; }

dialog {
	/* Style the `dialog` itself, which is `display: none;` to start. */
	background-color: white;
	border-radius: var(--base);
	filter: drop-shadow(0 0.5rem 1rem rgb(0 0 0 / 50%)); /* Some depth. */
	gap: calc(2 * var(--base));
	justify-items: center; /* Center the children. */
	padding: var(--base);

	/* Defaults to `position: absolute;`, this centers it: */
	inset-block-start: 50%;
	inset-inline-start: 50%;
	position: fixed; /* In the viewport. */
	translate: -50% -50%;

	/* When JS adds the `open` attribute… */
	&[open] { display: grid } /* `block` is default, but we can override. */

	button { background-color: tomato; } /* Always have a clear “close” `button`! */

	&::backdrop {
		background-color: rgb(0 0 0 / 66%); /* A dark overlay. */
		pointer-events: none; /* Let clicks pass through to our `document` listener. */
	}
}

		
style.css