Last active
March 14, 2025 19:06
-
-
Save elycruz/4fa4ee1e346ceb415dde7d98fd4200e8 to your computer and use it in GitHub Desktop.
Modal dialog '::backdrop' replacement.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Custom modal "backdrop" animation replacement, until `::backdrop` animations are supported. | |
*/ | |
document.body.innerHTML = ` | |
<style> | |
body { | |
display: grid; | |
place-content: center; | |
} | |
:root { | |
--modal-bg-speed: 0.21s; | |
--dialog-speed: 0.5s; | |
} | |
dialog { | |
position: fixed; | |
display: grid; | |
min-width: 233px; | |
max-width: 987px; | |
margin: auto; | |
inset: 0; | |
} | |
/** | |
* "Slide-out" state. | |
*/ | |
dialog:not([open], [inert]) { | |
animation: | |
var(--dialog-speed) slide-out-to-bottom, | |
var(--dialog-speed) fade-out | |
; | |
transform: translateY(100%); | |
opacity: 0; | |
} | |
/** | |
* Ensure we don't "offscreen" the dialog until "out" animation is done/we add \`inert\` prop. | |
*/ | |
dialog:not([inert]) { | |
z-index: 9999; | |
} | |
/** | |
* "Slide-in" state. | |
*/ | |
dialog[open] { | |
animation: | |
var(--dialog-speed) slide-in-from-bottom, | |
var(--dialog-speed) fade-in; | |
animation-delay: var(--modal-bg-speed); | |
animation-fill-mode: backwards; | |
transform: translateY(0); | |
opacity: 1; | |
} | |
.dialog-header { | |
display: flex; | |
flex-flow: row nowrap; | |
justify-content: space-between; | |
} | |
/** | |
* Custom modal backdrop. | |
*/ | |
dialog::backdrop { | |
background: rgba(0, 0, 0, 0); | |
} | |
.modal-backdrop { | |
position: fixed; | |
background: rgba(0,0,0,0.25); | |
inset: 0; | |
user-select: none; | |
animation: var(--modal-bg-speed) fade-out; | |
animation-fill-mode: forwards; | |
animation-delay: var(--dialog-speed); | |
pointer-events: none; | |
} | |
dialog[open] + .modal-backdrop { | |
animation: var(--modal-bg-speed) fade-in; | |
z-index: 9998; | |
} | |
/** | |
* Inert/offscreen state - Added (by us) on page load, and on 'close' animation end. | |
*/ | |
:where(dialog, .modal-backdrop):is([inert]) { | |
display: none; | |
pointer-events: none; | |
z-index: -1; | |
} | |
/** | |
* Keyframes. | |
*/ | |
@keyframes fade-in { | |
from { | |
opacity: 0; | |
} | |
to { | |
opacity: 1; | |
} | |
} | |
@keyframes fade-out { | |
from { | |
opacity: 1; | |
} | |
to { | |
opacity: 0; | |
} | |
} | |
@keyframes slide-in-from-bottom { | |
from { | |
transform: translateY(100%); | |
} | |
to { | |
transform: translateY(0); | |
} | |
} | |
@keyframes slide-out-to-bottom { | |
from { | |
transform: translateY(0); | |
} | |
to { | |
transform: translateY(100%); | |
} | |
} | |
</style> | |
<dialog inert> | |
<header class="dialog-header"> | |
<span>Title</span> | |
<button type="button" class="close-btn">x</button> | |
</header> | |
<section> | |
<p>Content.</p> | |
</section> | |
</dialog> | |
<div class="modal-backdrop" aria-hidden="true" inert></div> | |
<button type="button" class="open-btn">Open</button> | |
`; | |
const $ = (sel, root = document) => root.querySelector(sel), | |
dialog = $('dialog'), | |
modalBg = $('.modal-backdrop'); | |
$('.close-btn').addEventListener('click', () => { | |
dialog.close(); | |
}); | |
$('.open-btn').addEventListener('click', () => { | |
if (dialog.inert) { | |
modalBg.hidden = | |
modalBg.inert = | |
dialog.hidden = | |
dialog.inert = false; | |
} | |
dialog.showModal(); | |
}); | |
dialog.addEventListener('mouseup', (e) => { | |
const box = dialog.getBoundingClientRect(); | |
if (e.clientX < box.left || | |
e.clientX > box.right || | |
e.clientY < box.top || | |
e.clientY > box.bottom | |
) { | |
e.preventDefault(); | |
dialog.close(); | |
} | |
}); | |
dialog.addEventListener('close', e => { | |
modalBg.addEventListener('animationend', () => { | |
modalBg.hidden = | |
modalBg.inert = | |
dialog.hidden = | |
dialog.inert = true; | |
}, {once: true}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment