Modal Simple
Modal
GSAP
JS
CMS
Preview Links
Custom JS
Enable custom code in preview or view on published site.
Enable custom code?
Last Updated: Dec 2, 2025
- Modal children that close the modal like the close button or backdrop get an attribute of data-modal-1-close
- The modal dialog has an attribute of data-modal-1-id="contact-modal".
- The open button has an attribute of data-modal-1-trigger="contact-modal".
- We can have multiple modals on the same page by changing the data-modal-id for each modal like shown in the example
- We can use modals with the CMS by connecting the data-modal-1-id to the cms item's slug like shown in the example
- To preview the modal, apply data-preview="true" to the dialog. Do not set the dialog manually to display block in the style panel since that overrides the dialog's native behavior
- We can run other code when the modal opens with this script.
window.addEventListener("modal-1-open", (e) => {
console.log(e.detail);
});- We can run more code when the modal closes with this script.
window.addEventListener("modal-1-close", (e) => {
console.log(e.detail);
});- We can send users to the page with the modal pre-opened by including this behind the page url: ?modal-1-id=contact-modal
<script src="https://cdn.jsdelivr.net/npm/gsap@3.13.0/dist/gsap.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function () {
document.querySelectorAll(".modal-1_dialog").forEach(function (component) {
if (component.hasAttribute("data-modal-1")) return;
component.setAttribute("data-modal-1", "");
let lastFocusedElement;
if (typeof gsap !== "undefined") {
gsap.context(() => {
component.tl = gsap.timeline({ paused: true, onReverseComplete: resetModal });
component.tl.from(".modal-1_backdrop", { opacity: 0, duration: 0.2 });
component.tl.from(".modal-1_content", { opacity: 0, y: "6rem", duration: 0.3 }, "<");
}, component);
}
function resetModal() {
typeof lenis !== "undefined" && lenis.start ? lenis.start() : (document.body.style.overflow = "");
component.close();
if (lastFocusedElement) lastFocusedElement.focus();
window.dispatchEvent(new CustomEvent("modal-1-close", { detail: { component } }));
}
function openModal() {
typeof lenis !== "undefined" && lenis.stop ? lenis.stop() : (document.body.style.overflow = "hidden");
lastFocusedElement = document.activeElement;
component.showModal();
if (typeof gsap !== "undefined") component.tl.play();
component.querySelectorAll(".modal-1_scroll").forEach((el) => (el.scrollTop = 0));
window.dispatchEvent(new CustomEvent("modal-1-open", { detail: { component } }));
}
function closeModal() {
typeof gsap !== "undefined" ? component.tl.reverse() : resetModal();
}
if (new URLSearchParams(location.search).get("modal-1-id") === component.getAttribute("data-modal-1-id")) openModal(), history.replaceState({}, "", ((u) => (u.searchParams.delete("modal-1-id"), u))(new URL(location.href)));
component.addEventListener("cancel", (e) => (e.preventDefault(), closeModal()));
component.addEventListener("click", (e) => e.target.closest("[data-modal-1-close]") && closeModal());
document.addEventListener("click", (e) => e.target.closest("[data-modal-1-trigger='" + component.getAttribute("data-modal-1-id") + "']") && openModal());
});
});
</script>