Modal Component #
Description #
The Modal component provides an overlay window that can contain content, messages, or actions. It supports both static and draggable types, can be bounded within the viewport, and optionally remembers its last position when closed and reopened.
Examples and Classes #
Static Modal Example
<button class="modal-toggle btn btn-success" data-modal-id="example1">open</button>
<dialog id="example1" class="modal-container">
<div class="modal-content">
<div class="modal-header">
<i class="modal-close secondary-color fa-solid fa-close w-fit-content"></i>
</div>
<div class="modal-body">
<p class="txt-high-title secondary-color">Modal Title</p>
<p class="txt-normal secondary-color ">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam pulvinar
risus non risus hendrerit venenatis. Pellentesque sit amet hendrerit risus, sed porttitor quam.Lorem
ipsum dolor sit amet, consectetur adipiscing elit. Nullam pulvinar risus non risus hendrerit venenatis.
Pellentesque sit amet hendrerit risus, sed porttitor quam.Magna exercitation reprehenderit magna aute
tempor cupidatat consequat elit dolor adipisicing. Mollit dolor eiusmod sunt ex incididunt cillum quis.
Velit duis sit officia eiusmod Lorem aliqua enim laboris do dolor eiusmod. Et mollit incididunt nisi
consectetur esse laborum eiusmod pariatur proident Lorem eiusmod et. Culpa deserunt nostrud ad veniam.
</p>
<div class="d-flex align-items-center gap-2">
<button class="btn btn-outline-danger modal-close">close</button>
<button class="btn btn-secondary">link</button>
</div>
</div>
</div>
</dialog>
Draggable Modal Example
<button class="modal-toggle btn btn-success" data-modal-id="example2">open</button>
<dialog id="example2" class="modal-container modal-draggable">
<div class="modal-content">
<div class="modal-header">
<i class="modal-close secondary-color fa-solid fa-close w-fit-content"></i>
</div>
<div class="modal-body">
<p class="txt-high-title secondary-color">Modal Title</p>
<p class="txt-normal secondary-color">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam pulvinar
risus non risus hendrerit venenatis. Pellentesque sit amet hendrerit risus, sed porttitor quam.Lorem
ipsum dolor sit amet, consectetur adipiscing elit. Nullam pulvinar risus non risus hendrerit venenatis.
Pellentesque sit amet hendrerit risus, sed porttitor quam.Magna exercitation reprehenderit magna aute
tempor cupidatat consequat elit dolor adipisicing. Mollit dolor eiusmod sunt ex incididunt cillum quis.
Velit duis sit officia eiusmod Lorem aliqua enim laboris do dolor eiusmod. Et mollit incididunt nisi
consectetur esse laborum eiusmod pariatur proident Lorem eiusmod et. Culpa deserunt nostrud ad veniam.
</p>
<div class="d-flex align-items-center gap-2">
<button class="btn btn-outline-danger modal-close">close</button>
<button class="btn btn-secondary">link</button>
</div>
</div>
</div>
</dialog>
Draggable Modal With Bound Example
<button class="modal-toggle btn btn-success" data-modal-id="example3">open</button>
<dialog id="example3" class="modal-container modal-draggable modal-bounds">
<div class="modal-content">
<div class="modal-header">
<i class="modal-close secondary-color fa-solid fa-close w-fit-content"></i>
</div>
<div class="modal-body">
<p class="txt-high-title secondary-color">Modal Title</p>
<p class="txt-normal secondary-color ">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam pulvinar
risus non risus hendrerit venenatis. Pellentesque sit amet hendrerit risus, sed porttitor quam.Lorem
ipsum dolor sit amet, consectetur adipiscing elit. Nullam pulvinar risus non risus hendrerit venenatis.
Pellentesque sit amet hendrerit risus, sed porttitor quam.Magna exercitation reprehenderit magna aute
tempor cupidatat consequat elit dolor adipisicing. Mollit dolor eiusmod sunt ex incididunt cillum quis.
Velit duis sit officia eiusmod Lorem aliqua enim laboris do dolor eiusmod. Et mollit incididunt nisi
consectetur esse laborum eiusmod pariatur proident Lorem eiusmod et. Culpa deserunt nostrud ad veniam.
</p>
<div class="d-flex align-items-center gap-2">
<button class="btn btn-outline-danger modal-close">close</button>
<button class="btn btn-secondary">link</button>
</div>
</div>
</div>
</dialog>
Draggable Modal With Remember Position Example
<button class="modal-toggle btn btn-success" data-modal-id="example4">open</button>
<dialog id="example4" class="modal-container modal-draggable modal-remember-position">
<div class="modal-content">
<div class="modal-header">
<i class="modal-close secondary-color fa-solid fa-close w-fit-content"></i>
</div>
<div class="modal-body">
<p class="txt-high-title secondary-color">Modal Title</p>
<p class="txt-normal secondary-color ">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam pulvinar
risus non risus hendrerit venenatis. Pellentesque sit amet hendrerit risus, sed porttitor quam.Lorem
ipsum dolor sit amet, consectetur adipiscing elit. Nullam pulvinar risus non risus hendrerit venenatis.
Pellentesque sit amet hendrerit risus, sed porttitor quam.Magna exercitation reprehenderit magna aute
tempor cupidatat consequat elit dolor adipisicing. Mollit dolor eiusmod sunt ex incididunt cillum quis.
Velit duis sit officia eiusmod Lorem aliqua enim laboris do dolor eiusmod. Et mollit incididunt nisi
consectetur esse laborum eiusmod pariatur proident Lorem eiusmod et. Culpa deserunt nostrud ad veniam.
</p>
<div class="d-flex align-items-center gap-2">
<button class="btn btn-outline-danger modal-close">close</button>
<button class="btn btn-secondary">link</button>
</div>
</div>
</div>
</dialog>
Draggable Modal With Bound And Remember Position Example
<button class="modal-toggle btn btn-success" data-modal-id="example5">open</button>
<dialog id="example5" class="modal-container modal-draggable modal-bounds modal-remember-position">
<div class="modal-content">
<div class="modal-header">
<i class="modal-close secondary-color fa-solid fa-close w-fit-content"></i>
</div>
<div class="modal-body">
<p class="txt-high-title secondary-color">Modal Title</p>
<p class="txt-normal secondary-color ">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam pulvinar
risus non risus hendrerit venenatis. Pellentesque sit amet hendrerit risus, sed porttitor quam.Lorem
ipsum dolor sit amet, consectetur adipiscing elit. Nullam pulvinar risus non risus hendrerit venenatis.
Pellentesque sit amet hendrerit risus, sed porttitor quam.Magna exercitation reprehenderit magna aute
tempor cupidatat consequat elit dolor adipisicing. Mollit dolor eiusmod sunt ex incididunt cillum quis.
Velit duis sit officia eiusmod Lorem aliqua enim laboris do dolor eiusmod. Et mollit incididunt nisi
consectetur esse laborum eiusmod pariatur proident Lorem eiusmod et. Culpa deserunt nostrud ad veniam.
</p>
<div class="d-flex align-items-center gap-2">
<button class="btn btn-outline-danger modal-close">close</button>
<button class="btn btn-secondary">link</button>
</div>
</div>
</div>
</dialog>
| Class | Description | Example |
|---|---|---|
| .modal-container | Defines the main modal wrapper using the native <dialog> element. Acts as the base container for all modal content and supports styling, positioning, and animation. | <dialog class="modal-container">...</dialog> |
| .modal-content | Wraps all modal content including header, body, and footer with consistent padding, background, and shadow. | <div class="modal-content">...</div> |
| .modal-header | Defines the header area of the modal, typically containing the title and close icon. | <div class="modal-header"><h3>Title</h3><i class="modal-close fa-solid fa-close"></i></div> |
| .modal-body | Represents the main content area of the modal where text, forms, or other elements appear. | <div class="modal-body">...</div> |
| .modal-footer | Defines the footer area, typically used for action buttons such as “Cancel” or “Save”. | <div class="modal-footer"><button>Save</button></div> |
| .modal-close | Used for elements that close the modal. Automatically closes its parent modal when clicked. | <i class="modal-close fa-solid fa-close"></i> |
| .modal-draggable | Makes the modal draggable inside the viewport. Works with .modal-bounds. | <dialog class="modal-container modal-draggable"></dialog> |
| .modal-bounds | Limits the draggable area to the visible screen boundaries. | <dialog class="modal-container modal-bounds"></dialog> |
| .modal-remember-position | Stores the last dragged position and restores it when reopened. | <dialog class="modal-container modal-remember-position"></dialog> |
| .modal-toggle | Used on buttons to open or close a modal by referencing its ID via data-target. | <button class="modal-toggle" data-target="#modalStatic">Open Modal</button> |
File Locations #
| Type | Path |
|---|---|
| SCSS | miz/themes/mizoon/components/modal/_index.scss |
| HTML Example | miz/themes/mizoon/components/modal/index.html |
| JavaScript | miz/themes/mizoon/components/modal/script.js |
SCSS Variables #
SCSS
$modal-container-custom-classes: ".bg-transparent-color, .justify-content-center, .align-items-center";
$modal-content-custom-classes: ".d-flex, .flex-column, .bg-section-color, .radius-all-small, .overflow-hidden, .w-30";
$modal-content-header-custom-classes: ".d-flex, .flex-column, .gap-2, .p-2, .bg-on-secondary-color";
$modal-content-body-custom-classes: ".d-flex, .flex-column, .gap-2, .p-2";
$modal-content-footer-custom-classes: ".d-flex, .flex-column, .gap-2, .p-2";
$modal-close-custom-classes: ".cursor-pointer";
$modal-content-animation: 0.3s ease-out forwards;
$modal-dragging-transition: 0.07s ease-out;
JavaScript #
Below is an example of initializing and managing modal behavior using JavaScript.
JavaScript
class Modal {
constructor() {
this.initialPositions = new Map()
this.init()
}
init() {
this.initToggles()
this.initClose()
this.initDraggable()
}
initToggles() {
document.querySelectorAll(".modal-toggle").forEach(btn => {
const id = btn.dataset.modalId
btn.addEventListener("click", () => {
const modal = document.getElementById(id)
if (!modal) return
const modalContent = modal.querySelector(".modal-content")
modal.style.display = "flex"
modal.classList.remove("hide")
modal.classList.add("show")
modalContent.addEventListener("animationend", () => {
if (modal.classList.contains("modal-remember-position") && modal.dataset.position) {
try {
const { top, left } = JSON.parse(modal.dataset.position)
modalContent.style.position = "absolute"
modalContent.style.top = `${top}px`
modalContent.style.left = `${left}px`
return
} catch (e) { }
}
if (!this.initialPositions.has(modal)) {
const rect = modalContent.getBoundingClientRect()
const scrollTop = window.scrollY || window.pageYOffset
const scrollLeft = window.scrollX || window.pageXOffset
this.initialPositions.set(modal, {
top: rect.top + scrollTop,
left: rect.left + scrollLeft
})
}
const initial = this.initialPositions.get(modal)
if (initial) {
modalContent.style.position = "absolute"
modalContent.style.top = `${initial.top}px`
modalContent.style.left = `${initial.left}px`
}
}, { once: true })
})
})
}
initClose() {
document.querySelectorAll(".modal-close").forEach(btn => {
btn.addEventListener("click", () => {
const modal = btn.closest(".modal-container, .modal-draggable")
if (!modal) return
const modalContent = modal.querySelector(".modal-content")
modal.classList.remove("show")
modal.classList.add("hide")
modal.addEventListener("animationend", () => {
if (!modal.classList.contains("hide")) return
if (!modal.classList.contains("modal-remember-position") && modalContent) {
const initial = this.initialPositions.get(modal)
if (initial) {
modalContent.style.top = `${initial.top}px`
modalContent.style.left = `${initial.left}px`
}
}
modal.style.display = "none"
modal.classList.remove("hide")
}, { once: true })
})
})
}
initDraggable() {
document.querySelectorAll(".modal-draggable .modal-content .modal-header").forEach(header => {
let isDragging = false
let offsetX = 0
let offsetY = 0
const modalContent = header.closest(".modal-content")
const modalContainer = header.closest(".modal-draggable")
header.addEventListener("mousedown", e => {
if (e.target.classList.contains("modal-close") || e.target.tagName === "BUTTON") return
isDragging = true
modalContent.classList.add("dragging")
const rect = modalContent.getBoundingClientRect()
modalContent.style.left = `${rect.left + (window.scrollX || window.pageXOffset)}px`
modalContent.style.top = `${rect.top + (window.scrollY || window.pageYOffset)}px`
modalContent.style.position = "absolute"
modalContent.style.margin = "0"
modalContent.style.zIndex = 9999
offsetX = e.clientX - rect.left
offsetY = e.clientY - rect.top
})
document.addEventListener("mousemove", e => {
if (!isDragging) return
let newLeft = e.clientX - offsetX
let newTop = e.clientY - offsetY
if (modalContainer.classList.contains("modal-bounds")) {
const modalRect = modalContent.getBoundingClientRect()
const winWidth = window.innerWidth
const winHeight = window.innerHeight
if (newLeft < 0) newLeft = 0
if (newTop < 0) newTop = 0
if (newLeft + modalRect.width > winWidth) newLeft = winWidth - modalRect.width
if (newTop + modalRect.height > winHeight) newTop = winHeight - modalRect.height
}
modalContent.style.left = `${newLeft}px`
modalContent.style.top = `${newTop}px`
})
document.addEventListener("mouseup", () => {
if (!isDragging) return
isDragging = false
modalContent.classList.remove("dragging")
if (modalContainer.classList.contains("modal-remember-position")) {
modalContainer.dataset.position = JSON.stringify({
top: parseFloat(modalContent.style.top),
left: parseFloat(modalContent.style.left)
})
}
})
})
}
}
new Modal()
To use this component, you need to:
Include the MIZCHIN JavaScript file in your page.