@terrahq/modal
v0.0.15
Published
A lightweight and extensible modal library designed for modern front-end workflows. It provides **simple configuration**, **robust callbacks**, and **debugging tools** out of the box.
Downloads
462
Keywords
Readme
🪟 Modal.js — by Terra
A lightweight and extensible modal library designed for modern front-end workflows.
It provides simple configuration, robust callbacks, and debugging tools out of the box.
✨ Features
- 🧩 Plug & Play: Works with minimal setup and clean HTML attributes.
- ⚙️ Configurable: Customize triggers, class names, and behaviors.
- 🔁 Lifecycle Hooks: Use callbacks (
beforeOpen,onShow,beforeClose,onClose) to extend functionality. - 🧩 Plugins: Extend a modal via the
pluginsoption — e.g. the optional Expand plugin adds a fullscreen toggle, with mobile-safe dynamic viewport (dvh) sizing. - 🔒 Scroll Locking: Prevent background scroll when modals are open.
- 🪶 Lightweight: No dependencies, framework-agnostic.
- 🐞 Debug Mode: Console feedback for easy testing and troubleshooting.
📦 Installation
Using npm
npm install @terrahq/modalOr via CDN
<!-- Core -->
<script src="https://unpkg.com/@terrahq/modal/dist/Modal.umd.js"></script>
<!-- Optional Expand plugin -->
<script src="https://unpkg.com/@terrahq/modal/dist/plugins/expand.umd.js"></script>🚀 Quick Start
<!-- Trigger -->
<button data-modal-open="my-modal">Open Modal</button>
<!-- Modal -->
<div id="my-modal" class="c--modal-a" aria-hidden="true">
<div class="c--modal-a__overlay" data-modal-close></div>
<div class="c--modal-a__item" role="dialog" aria-modal="true" aria-labelledby="modal-title">
<h2 id="modal-title">Basic Modal</h2>
<p>This is a simple modal example.</p>
<button data-modal-close>Close</button>
</div>
</div>JS
import Modal from "@terrahq/modal";
const modal = new Modal({
selector: document.getElementById('my-modal'),
debug: true,
onShow: (modal, trigger) => console.log(`Modal ${modal.id} is now visible`, trigger),
onClose: (modal, trigger) => console.log(`Modal ${modal.id} is now hidden`, trigger),
});⚙️ Constructor Parameters
| Option | Type | Default | Description |
| --------------- | ------------------ | --------------------- | ------------------------------------------------------------------------------- |
| selector | HTMLElement | — (required) | The DOM element representing the modal container (Element only, no strings). |
| onShow | Function \| null | null | Callback executed after the modal is opened. (modal, triggerInfo) => void |
| onClose | Function \| null | null | Callback executed after the modal is closed. (modal, triggerInfo) => void |
| beforeOpen | Function \| null | null | Callback executed before opening. (modal, triggerInfo) => boolean \| void |
| beforeClose | Function \| null | null | Callback executed before closing. (modal, triggerInfo) => boolean \| void |
| openTrigger | string | data-modal-open | Attribute used on buttons/links that open the modal. |
| closeTrigger | string | data-modal-close | Attribute used on buttons/links that close the modal. |
| openClass | string | c--modal-a--is-open | Class added to the modal element when it is open. |
| disableScroll | boolean | true | Prevents <body> scrolling while the modal is open. |
| plugins | Array | [] | Plugins to extend this instance, e.g. [ Expand() ]. See Plugins below. |
| debug | boolean | false | Enables helpful console logs for debugging and testing. |
🧪 Programmatic Control
Open / Close / Toggle
const myModal = new Modal({ selector: document.getElementById('my-modal') });
// Using the instance’s own element:
myModal.open();
myModal.close();
myModal.toggle();With custom trigger info (analytics/debug)
document.querySelector('.js--fire').addEventListener('click', (e) => {
e.preventDefault();
myModal.open(undefined, {
type: 'custom',
element: e.currentTarget,
source: 'js--fire',
method: 'click'
});
});🔁 Lifecycle Hooks (quick examples)
new Modal({
selector: document.getElementById('modal-hooks'),
beforeOpen: (modal, trigger) => {
console.log('beforeOpen', trigger);
},
onShow: (modal, trigger) => {
console.log('onShow', trigger);
},
beforeClose: (modal, trigger) => {
console.log('beforeClose', trigger);
},
onClose: (modal, trigger) => {
console.log('onClose', trigger);
},
});Callbacks are wrapped safely. If a callback throws, it logs an error but won’t break the modal.
🧩 Plugins
The core stays lean; opt-in features ship as plugins you attach via the plugins option. A
plugin is a small factory that returns an object with a single install(modal) method, called
once during init:
{
name: 'my-plugin',
install(modal) {
// wire up DOM/listeners here
},
}Inside install, a plugin uses what the instance already exposes:
| Member | Purpose |
| ---------------------------- | ---------------------------------------------------------------------------------------- |
| modal.DOM.modal | The modal root element. |
| modal.settings | The resolved instance settings (e.g. settings.openClass). |
| modal._safe(fn, ...args) | Invoke a user callback inside the modal's error-safe wrapper. |
| modal.eventListeners | Push { element, type, listener } here and the modal's destroy() removes it for you. |
⛶ Expand (fullscreen)
The Expand plugin adds an opt-in expand button that toggles the modal between its default size
and the full viewport (mobile-safe dvh sizing). Add a button with data-modal-expand inside
the modal and attach the plugin:
<div id="my-modal" class="c--modal-a" aria-hidden="true">
<div class="c--modal-a__overlay" data-modal-close></div>
<div class="c--modal-a__item" role="dialog" aria-modal="true" aria-labelledby="modal-title">
<h2 id="modal-title">Expandable Modal</h2>
<button aria-label="Toggle fullscreen" data-modal-expand>⤢</button>
<button aria-label="Close modal" data-modal-close>×</button>
<p>Click ⤢ to expand to the full viewport, then again to collapse.</p>
</div>
</div>import Modal from '@terrahq/modal';
import Expand from '@terrahq/modal/plugins/expand';
new Modal({
selector: document.getElementById('my-modal'),
plugins: [
Expand({
onExpand: (modal, trigger) => console.log('expanded', trigger),
onCollapse: (modal, trigger) => console.log('collapsed', trigger),
}),
],
});Expand plugin options: expandTrigger (default data-modal-expand), expandedClass
(default c--modal-a--is-expanded), onExpand, onCollapse.
Provide the fullscreen styles yourself (the package ships JS only):
.c--modal-a--is-expanded .c--modal-a__item {
max-width: 100%;
width: 100%;
max-height: 100vh; // fallback
max-height: 100dvh; // dynamic viewport
height: 100dvh;
border-radius: 0;
}🧯 Destroy (SPA / transitions)
const m = new Modal({ selector: document.getElementById('modal-spa') });
// ...
m.destroy(); // removes all listeners, ESC binding, etc.
// Safe to call multiple times; subsequent calls do nothing.🧱 Minimal CSS (example)
.c--modal-a {
position: fixed;
inset: 0;
background: rgba(0,0,0,.5);
opacity: 0;
pointer-events: none;
transition: opacity .2s ease;
display: grid;
place-items: center;
}
.c--modal-a--is-open {
opacity: 1;
pointer-events: auto;
}
.c--modal-a__overlay {
position: absolute;
inset: 0;
}
.c--modal-a__item {
position: relative;
background: #fff;
padding: 1.25rem;
border-radius: .5rem;
max-width: 560px;
width: 90%;
box-shadow: 0 10px 30px rgba(0,0,0,.2);
max-height: 80vh;
overflow: auto;
}ChangeLog
0.0.13 - Added an optional button to expand the modal to fullscreen.
0.0.09 - Enhanced Error Handling & Stability
- Added safe error handling when opening or closing modals. The instance no longer throws runtime errors if the selector is invalid or
null; instead, a clear console message is displayed (e.g., “No modal Element found...”). - Added internal
_report()helper to produce readable error messages and optionally triggeronError. - Added
_disabledflag to safely prevent actions when initialization fails. open()andclose()now validate the provided modal element before accessing its properties.- All logic remains backward-compatible with previous versions.
- Refactored Escape key handling so that the listener is only attached while the modal is open, improving performance and preventing leaks.
0.0.08 - All callback functions (onShow, onClose, beforeOpen, beforeClose) receive two parameters:
- modal: The modal element
- triggerInfo: Object containing information about what triggered the modal action
0.0.07 - Refactor doc
0.0.05 - Refactor callbacks
0.0.04 - Version update
0.0.03 - Update dist from custom triggers
0.0.02 - Update callbacks when using custom triggers
0.0.01 - First Release
