Modal
Modals are dialog windows that overlay the main content, focusing user attention on a specific task or message. They're essential for confirmations, forms, alerts, and detailed views without leaving the current page context.
Key Features
- Semi-transparent backdrop that dims background content
- Keyboard navigation with Escape key to close
- Click outside (backdrop) to close option
- Smooth fade animations for opening and closing
- Multiple size variants (small, default, large)
- Event callbacks for lifecycle management
- Programmatic control (open, close, toggle)
When to Use Modals
- Confirmations: Delete actions, irreversible operations, important decisions
- Forms: Login, registration, quick edits, contact forms
- Detail Views: Product details, image galleries, extended information
- Alerts & Messages: Success notifications, error messages, warnings
- Multi-Step Processes: Wizards, guided tours, onboarding flows
Basic Usage
// HTML structure
<div id="my-modal" class="modal">
<div class="modal-header">
<h5 class="modal-title">Modal Title</h5>
<button class="modal-close">×</button>
</div>
<div class="modal-body">
Modal content goes here.
</div>
<div class="modal-footer">
<button class="btn btn-secondary modal-close-btn">Cancel</button>
<button class="btn btn-primary">Save</button>
</div>
</div>
// JavaScript
const modal = Domma.elements.modal('#my-modal', {
backdrop: true,
backdropClose: true,
keyboard: true
});
// Control the modal
modal.open(); // Opens the modal
modal.close(); // Closes the modal
modal.toggle(); // Toggles open/close
⚠️ Important: Domma vs Bootstrap Structure
Domma uses a FLAT structure - do NOT use Bootstrap's
.modal-dialog and .modal-content wrapper divs!
❌ WRONG (Bootstrap structure):
<div class="modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">...</div>
</div>
</div>
</div>
✅ CORRECT (Domma structure):
<div class="modal"> <div class="modal-header">...</div> <div class="modal-body">...</div> <div class="modal-footer">...</div> </div>
Quick Start
Create a simple modal in under a minute. This example includes a trigger button and basic modal structure.
Simplest Modal
// HTML
<button class="btn btn-primary" id="open-simple-modal">Open Modal</button>
<div id="simple-modal" class="modal">
<div class="modal-header">
<h5 class="modal-title">Welcome!</h5>
<button class="modal-close">×</button>
</div>
<div class="modal-body">
<p>This is a simple modal dialog.</p>
</div>
<div class="modal-footer">
<button class="btn btn-primary modal-close-btn">Got It</button>
</div>
</div>
// JavaScript
const modal = Domma.elements.modal('#simple-modal');
document.getElementById('open-simple-modal').addEventListener('click', () => {
modal.open();
});
Welcome!
This is a simple modal dialog. You can close it by clicking the X, the backdrop, or pressing Escape.
Step-by-Step Tutorial
Build a confirmation modal for deleting items, step by step.
Step 1: Create the HTML Structure
Start with the modal markup and trigger button:
<button class="btn btn-danger" id="delete-btn">Delete Item</button>
<div id="confirm-modal" class="modal">
<div class="modal-header">
<h5 class="modal-title">Confirm Deletion</h5>
<button class="modal-close">×</button>
</div>
<div class="modal-body">
<p>Are you sure you want to delete this item?</p>
</div>
<div class="modal-footer">
<button class="btn btn-secondary modal-close-btn">Cancel</button>
<button class="btn btn-danger" id="confirm-delete">Delete</button>
</div>
</div>
Step 2: Initialise the Modal
Create modal instance with default options:
const confirmModal = Domma.elements.modal('#confirm-modal', {
backdrop: true, // Show backdrop
backdropClose: true, // Allow closing by clicking backdrop
keyboard: true // Allow closing with Escape key
});
Step 3: Add Open Trigger
Make the delete button open the modal:
document.getElementById('delete-btn').addEventListener('click', () => {
confirmModal.open();
});
Step 4: Handle Confirmation
Add logic when user confirms deletion:
document.getElementById('confirm-delete').addEventListener('click', () => {
// Perform deletion
console.log('Item deleted');
// Close the modal
confirmModal.close();
// Show success message (could use Toast component)
alert('Item deleted successfully');
});
Step 5: Add Event Callbacks (Optional)
Track modal lifecycle with callbacks:
const confirmModal = Domma.elements.modal('#confirm-modal', {
backdrop: true,
backdropClose: true,
keyboard: true,
onOpen: () => {
console.log('Modal opened');
// Could disable page actions
},
onClose: () => {
console.log('Modal closing');
},
onClosed: () => {
console.log('Modal fully closed');
}
});
Step 6: Complete Working Demo
Confirm Deletion
Are you sure you want to delete this item?
This action cannot be undone.
Examples & Variations
Example 1: Small Modal
Compact modal for simple messages:
Quick Message
This is a small modal for brief messages.
Example 2: Large Modal with Form
Larger modal for complex content:
Contact Form
Example 3: No Backdrop Close
Modal that requires explicit action to close:
Important Notice
This modal requires you to click a button to close it.
You cannot close it by clicking outside or pressing Escape.
Example 4: Success Message
Modal for positive feedback:
✓ Success!
Your changes have been saved successfully.
Example 5: Scrollable Content
Modal with long content that scrolls:
Terms and Conditions
1. Introduction
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
2. User Obligations
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
3. Privacy Policy
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
4. Termination
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
5. Liability
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium.
Example 6: Nested Modals
Open a modal from within another modal:
Parent Modal
This is the parent modal.
Child Modal
This modal was opened from the parent modal.
Factory Mode - Programmatic Creation
Create modals programmatically without pre-existing HTML using createModal().
Perfect for dynamic content, notifications, and on-demand dialogs.
Example 1: Basic Factory Modal
Create a modal with title, content, and buttons programmatically:
const modal = Domma.elements.createModal({
title: 'Welcome',
content: '<p>This modal was created programmatically!</p>',
size: 'medium',
buttons: [
{id: 'close', text: 'Close', variant: 'primary'}
]
});
modal.open();
Example 2: Size Variants
Available sizes: small, medium, large, xl:
Domma.elements.createModal({
title: 'Large Modal',
content: '<p>This is a large modal for extensive content.</p>',
size: 'large'
}).open();
Example 3: Custom Buttons with Actions
Define multiple buttons with custom actions:
Domma.elements.createModal({
title: 'Custom Actions',
content: '<p>Choose an action to perform:</p>',
buttons: [
{id: 'cancel', text: 'Cancel', variant: 'secondary'},
{id: 'save', text: 'Save', variant: 'primary'},
{id: 'delete', text: 'Delete', variant: 'danger'}
],
onButtonClick: (buttonId, modal) => {
console.log('Button clicked:', buttonId);
if (buttonId === 'delete') {
Domma.elements.toast('Deleted!', {type: 'success'});
}
}
}).open();
Example 4: Dynamic Content Loading
Load content asynchronously and display in modal:
async function showUserProfile(userId) {
const modal = Domma.elements.createModal({
title: 'User Profile',
content: '<div class="text-center"><p>Loading...</p></div>',
size: 'large'
});
modal.open();
// Fetch user data
const user = await fetch(`/api/users/${userId}`).then(r => r.json());
// Update modal content
const content = `
<h4>${user.name}</h4>
<p>Email: ${user.email}</p>
<p>Role: ${user.role}</p>
`;
$('#' + modal.element.id + ' .dm-dialog-body').html(content);
}
Example 5: Scrollable Content
Create modal with scrollable body for long content:
Domma.elements.createModal({
title: 'Privacy Policy',
content: `<p>Long content...</p>`.repeat(20),
size: 'large',
scrollable: true,
buttons: [{id: 'accept', text: 'Accept', variant: 'primary'}]
}).open();
Promise Mode - Async/Await
Use showModal() for promise-based modals that resolve with the button ID clicked.
Perfect for confirmations, choices, and async workflows.
Example 1: Confirmation Dialog
Wait for user confirmation with async/await:
async function deleteItem() {
const result = await Domma.elements.showModal({
title: 'Confirm Deletion',
content: '<p>Are you sure you want to delete this item?</p>',
buttons: [
{id: 'cancel', text: 'Cancel', variant: 'secondary'},
{id: 'delete', text: 'Delete', variant: 'danger'}
]
});
if (result === 'delete') {
console.log('Item deleted!');
Domma.elements.toast('Item deleted successfully', {type: 'success'});
} else {
console.log('Deletion cancelled');
}
}
Example 2: Multiple Choice Selection
Let user choose from multiple options:
async function selectSize() {
const size = await Domma.elements.showModal({
title: 'Select Size',
content: '<p>Choose your preferred size:</p>',
buttons: [
{id: 'small', text: 'Small'},
{id: 'medium', text: 'Medium'},
{id: 'large', text: 'Large'},
{id: 'xl', text: 'Extra Large'}
]
});
console.log('Selected size:', size);
}
Example 3: Form Submission with Promise
Combine form input with promise resolution:
async function getUserInput() {
const modal = Domma.elements.createModal({
title: 'Enter Details',
content: `
<div class="form-group">
<label class="form-label">Name</label>
<input type="text" id="user-name" class="form-input">
</div>
<div class="form-group">
<label class="form-label">Email</label>
<input type="email" id="user-email" class="form-input">
</div>
`,
buttons: [
{id: 'cancel', text: 'Cancel', variant: 'secondary'},
{id: 'submit', text: 'Submit', variant: 'primary'}
]
});
return new Promise((resolve) => {
modal.options.onButtonClick = (buttonId) => {
if (buttonId === 'submit') {
const data = {
name: $('#user-name').val(),
email: $('#user-email').val()
};
resolve(data);
} else {
resolve(null);
}
};
modal.open();
});
}
// Usage
const userData = await getUserInput();
if (userData) {
console.log('User data:', userData);
}
Example 4: Chained Confirmations
Create multi-step confirmation flows:
async function dangerousOperation() {
// First confirmation
const firstConfirm = await Domma.elements.showModal({
title: 'Warning',
content: '<p>This will permanently delete all data.</p>',
buttons: [
{id: 'cancel', text: 'Cancel', variant: 'secondary'},
{id: 'continue', text: 'Continue', variant: 'warning'}
]
});
if (firstConfirm !== 'continue') return;
// Second confirmation
const secondConfirm = await Domma.elements.showModal({
title: 'Final Confirmation',
content: '<p class="text-danger"><strong>Are you absolutely sure?</strong></p>',
buttons: [
{id: 'no', text: 'No, Go Back', variant: 'secondary'},
{id: 'yes', text: 'Yes, Delete Everything', variant: 'danger'}
]
});
if (secondConfirm === 'yes') {
console.log('Performing dangerous operation...');
Domma.elements.toast('All data deleted', {type: 'error'});
}
}
Best Practices
Accessibility
- Focus Management: Automatically focus the first interactive element when modal opens, return focus to trigger when closing
- Keyboard Navigation: Support Escape key to close (use
keyboard: true), Tab to cycle through focusable elements - ARIA Attributes: Add
role="dialog",aria-modal="true",aria-labelledbypointing to title - Screen Readers: Announce modal opening, provide context about purpose and available actions
- Backdrop Semantics: Ensure backdrop is properly marked and doesn't interfere with screen reader navigation
- Close Button: Always provide visible close button with descriptive
aria-label="Close modal"
Performance
- Lazy Content: Load modal content only when opened for heavy forms or images
- Destroy on Close: For one-time modals, call
modal.destroy()after closing to free memory - Limit Nesting: Avoid deeply nested modals (max 2 levels) as it confuses users and impacts performance
- Animation Performance: Use CSS transforms and opacity for smooth 60fps animations
- Body Scroll Lock: Domma automatically prevents body scrolling when modal is open
Common Gotchas
| Issue | Solution |
|---|---|
| Modal appears below other content | Ensure modal has z-index higher than other positioned elements. Default z-index is
1050 for modal, 1040 for backdrop.
|
| Backdrop click not working | Check backdropClose: true is set. Ensure backdrop element isn't blocked by modal
content extending beyond modal bounds.
|
| Modal not closing programmatically | Store modal instance in accessible scope: const modal = Domma.elements.modal('#id'),
then call modal.close().
|
| Multiple modals open simultaneously | Manage modal state yourself or use nested modal pattern. Ensure only one primary modal is open at a time for UX clarity. |
| Form submission closing modal | Use e.preventDefault() in form submit handler to prevent default behaviour, then
manually close modal after async operations.
|
Tips & Tricks
- Auto-Open: Call
modal.open()immediately after creation for splash screens or important announcements - Programmatic Content: Populate modal content dynamically before opening:
$('#modal .modal-body').html(content); modal.open(); - Size Classes: Use
.modal-sm,.modal-lg, or.modal-xlfor different sizes - Loading States: Combine with Loader component for async operations inside modals
- Confirmation Pattern: Return Promise from modal actions for cleaner async/await code
- Mobile Optimization: On small screens, modals automatically use full width with appropriate padding
Config Engine Integration
Use Domma's Config Engine for declarative modal setup.
Single Modal
$.setup({
'#login-modal': {
component: 'modal',
options: {
backdrop: true,
backdropClose: false, // Require explicit action
keyboard: true,
onOpen: () => {
console.log('Login modal opened');
}
}
}
});
Multiple Modals
$.setup({
'#info-modal': {
component: 'modal',
options: {
backdrop: true,
animation: 'fade'
}
},
'#confirm-modal': {
component: 'modal',
options: {
backdropClose: false,
keyboard: false,
onClose: () => {
console.log('User made a choice');
}
}
}
});
With Event Handlers
$.setup({
'#contact-modal': {
component: 'modal',
options: {
backdrop: true,
onOpen: () => {
// Clear form fields
$('#contact-modal form')[0].reset();
},
onClosed: () => {
// Reset any state
console.log('Contact modal closed');
}
},
events: {
submit: (e) => {
e.preventDefault();
// Handle form submission
const formData = new FormData(e.target);
console.log('Submitting:', Object.fromEntries(formData));
}
}
}
});
Related Elements
Modals work great with these complementary components: