Usage
<script src="/src/js/modal-init.js"></script>
<dialog class="modal">
<form method="dialog">
<button class="button theme">Close</button>
</form>
<div class="content">
<div class="l-stack">
<h2>Some Content</h2>
<p>Some text</p>
<a href="/">Call to action</a>
</div>
</div>
</dialog>
<button class="button theme showModal" style="margin-inline: auto;">Open Modal</button>
.modal {
top: 50%;
left: 50%;
translate: -50% 50%;
&, &::backdrop {
opacity: 0;
}
&::backdrop {
background: black;
}
&[open] {
opacity: 1;
translate: -50% -50%;
transition:
display 300ms linear allow-discrete,
overlay 300ms linear allow-discrete,
translate 250ms var(--ease-out-3) allow-discrete,
opacity 300ms linear;
&::backdrop {
opacity: 0.5;
}
}
@starting-style {
&[open],
&[open] {
opacity: 0;
translate: -50% calc(-50% + 20px);
}
}
}
import {
disableBodyScroll,
enableBodyScroll
} from "body-scroll-lock";
import {queryFocuseable, trapFocus} from "./utilities/accessibility";
export default class Modal {
constructor(dialog, openBtn) {
this.dialog = dialog;
this.openBtn = openBtn;
this.showModalBound = this.showModal.bind(this);
this.openBtn.addEventListener("click", this.showModalBound);
this.dialog.addEventListener("close", this.closeModal.bind(this));
document.addEventListener('keydown', this.closeOnEscapePress.bind(this));
}
showModal() {
this.dialog.showModal();
disableBodyScroll(this.dialog, { reserveScrollBarGap: true });
this.focuseable = queryFocuseable(this.dialog);
this.focuseable.firstFocuseable.focus();
this.boundTrapFocusHandler = this.trapFocusHandler.bind(this);
document.addEventListener("keydown", this.boundTrapFocusHandler);
}
closeModal() {
enableBodyScroll(this.dialog);
document.removeEventListener('keydown', this.boundTrapFocusHandler);
}
trapFocusHandler(event) {
trapFocus(event, this.focuseable);
}
closeOnEscapePress = (e) => {
if (e.key === "Escape" && this.dialog.hasAttribute('open')) {
this.dialog.close();
this.closeModal();
}
};
}
Document the approaches available of preventing scrolling when the modal is open. How to prevent layout shift when scrollbar disappears (more evident when the background content is visible)
The Dialog Element
Section titled “The Dialog Element”This implimentation of the modal component uses the dialog element.
You can set the color of the background behind the modal using the ::backdrop
psuedo-selector. However, it does not currently inherit custom properties.
Scrolling
Section titled “Scrolling”User scroll should be prevented when the modal is open. This implimentation uses the body-scroll-lock package, which handles scroll locking for a variety of browsers and device types.
Keyboard Navigation
Section titled “Keyboard Navigation”This implimentation provides focus trapping to keep the tabbing context within the modal.
Future Additions
Section titled “Future Additions”- document the styles that are included (ex. positioning)