Overview
The Modal component provides accessible dialog overlays for displaying content above the main page. Modals can be used for forms, confirmations, image galleries, alerts, and any content that requires focused user attention. The component includes full keyboard navigation, focus management, and responsive designs.
🎭 Multiple Variants
Standard dialogs, alerts, image lightboxes, drawers, and bottom sheets for different use cases.
📏 Flexible Sizing
Small, default, large, extra-large, and full-screen aiab-modal sizes with responsive behavior.
✨ Rich Animations
Fade-in, slide-down, zoom-in animations with smooth transitions and backdrop effects.
♿ Full Accessibility
ARIA attributes, focus trapping, keyboard navigation, and screen reader support.
📱 Responsive Design
Adaptive layouts for mobile devices with touch-friendly interactions.
🌙 Dark Mode Ready
Automatic dark mode support with appropriate color schemes and contrast.
Basic Usage
Simple Modal
<!-- Modal Structure -->
<div class="aiab-modal-backdrop" id="aiab-modal-backdrop"></div>
<div class="aiab-modal" id="basic-modal" aria-hidden="true" role="dialog" aria-labelledby="aiab-modal-title">
<div class="aiab-modal__dialog">
<div class="aiab-modal__header">
<h2 class="aiab-modal__title" id="aiab-modal-title">Modal Title</h2>
<button class="aiab-modal__close" aria-label="Close aiab-modal">
<svg width="24" height="24" fill="currentColor" viewBox="0 0 24 24">
<path d="M18 6L6 18M6 6l12 12"/>
</svg>
</button>
</div>
<div class="aiab-modal__body">
<p>This is the aiab-modal content. You can put any content here including forms, images, or other components.</p>
</div>
<div class="aiab-modal__footer">
<button class="aiab-btn aiab-btn--secondary">Cancel</button>
<button class="aiab-btn aiab-btn--primary">Confirm</button>
</div>
</div>
</div>
Modal with Form
<div class="aiab-modal" id="form-modal">
<div class="aiab-modal__dialog">
<div class="aiab-modal__header">
<h2 class="aiab-modal__title">Contact Information</h2>
<button class="aiab-modal__close">×</button>
</div>
<div class="aiab-modal__body">
<form>
<div class="aiab-form-group">
<label for="name">Name</label>
<input type="text" id="name" class="aiab-form-control" required>
</div>
<div class="aiab-form-group">
<label for="email">Email</label>
<input type="email" id="email" class="aiab-form-control" required>
</div>
<div class="aiab-form-group">
<label for="message">Message</label>
<textarea id="message" class="aiab-form-control" rows="4"></textarea>
</div>
</form>
</div>
<div class="aiab-modal__footer">
<button class="aiab-btn aiab-btn--secondary">Cancel</button>
<button class="aiab-btn aiab-btn--primary">Send Message</button>
</div>
</div>
</div>
Modal Sizes
<!-- Small Modal -->
<div class="aiab-modal aiab-modal--sm">...</div>
<!-- Default Modal -->
<div class="aiab-modal">...</div>
<!-- Large Modal -->
<div class="aiab-modal aiab-modal--lg">...</div>
<!-- Extra Large Modal -->
<div class="aiab-modal aiab-modal--xl">...</div>
<!-- Full Screen Modal -->
<div class="aiab-modal aiab-modal--full">...</div>
Modal Variants
Alert Modals
<!-- Success Alert Modal -->
<div class="aiab-modal aiab-modal--alert aiab-modal--success">
<div class="aiab-modal__dialog">
<div class="aiab-modal__body">
<div class="aiab-modal__icon">
<svg width="32" height="32" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/>
</svg>
</div>
<h3 class="aiab-modal__title">Success!</h3>
<p>Your action was completed successfully.</p>
</div>
<div class="aiab-modal__footer aiab-modal__footer--center">
<button class="aiab-btn aiab-btn--primary">OK</button>
</div>
</div>
</div>
Image Modal
<div class="aiab-modal aiab-modal--image">
<div class="aiab-modal__dialog">
<div class="aiab-modal__body">
<img src="https://picsum.photos/600/400" alt="Description" />
</div>
<button class="aiab-modal__close">×</button>
</div>
</div>
Drawer Modals
<!-- Left Drawer -->
<div class="aiab-modal aiab-modal--drawer-left">
<div class="aiab-modal__dialog">
<div class="aiab-modal__header">
<h2 class="aiab-modal__title">Navigation</h2>
<button class="aiab-modal__close">×</button>
</div>
<div class="aiab-modal__body">
<nav>
<ul>
<li><a href="#">Home</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Services</a></li>
<li><a href="#">Contact</a></li>
</ul>
</nav>
</div>
</div>
</div>
<!-- Right Drawer -->
<div class="aiab-modal aiab-modal--drawer-right">...</div>
Bottom Sheet Modal
<div class="aiab-modal aiab-modal--bottom-sheet">
<div class="aiab-modal__dialog">
<div class="aiab-modal__header">
<h2 class="aiab-modal__title">Actions</h2>
<button class="aiab-modal__close">×</button>
</div>
<div class="aiab-modal__body">
<div class="list-group">
<button class="list-group-item">Share</button>
<button class="list-group-item">Edit</button>
<button class="list-group-item">Delete</button>
</div>
</div>
</div>
</div>
Modal Animations
<!-- Fade In Animation -->
<div class="aiab-modal aiab-modal--fade-in">...</div>
<!-- Slide Down Animation -->
<div class="aiab-modal aiab-modal--slide-down">...</div>
<!-- Zoom In Animation -->
<div class="aiab-modal aiab-modal--zoom-in">...</div>
Loading State
<div class="aiab-modal aiab-modal--loading">
<div class="aiab-modal__dialog">
<div class="aiab-modal__header">
<h2 class="aiab-modal__title">Loading...</h2>
</div>
<div class="aiab-modal__body">
<div class="aiab-modal__spinner"></div>
</div>
</div>
</div>
CSS Classes Reference
Base Classes
| Class | Description |
|---|---|
.aiab-modal |
Base aiab-modal aiab-container |
.aiab-modal-backdrop |
Modal backdrop overlay |
.aiab-modal__dialog |
Modal dialog box |
.aiab-modal__header |
Modal header section |
.aiab-modal__title |
Modal title |
.aiab-modal__close |
Close button |
.aiab-modal__body |
Modal content area |
.aiab-modal__footer |
Modal footer section |
Size Modifiers
| Class | Description | Max Width |
|---|---|---|
.aiab-modal--sm |
Small aiab-modal | 300px |
| Default | Default aiab-modal | 500px |
.aiab-modal--lg |
Large aiab-modal | 800px |
.aiab-modal--xl |
Extra large aiab-modal | 1140px |
.aiab-modal--full |
Full screen aiab-modal | 95vw |
Variant Modifiers
| Class | Description |
|---|---|
.aiab-modal--alert |
Alert style aiab-modal |
.aiab-modal--image |
Image lightbox aiab-modal |
.aiab-modal--drawer-left |
Left aiab-sidebar drawer |
.aiab-modal--drawer-right |
Right aiab-sidebar drawer |
.aiab-modal--bottom-sheet |
Bottom sheet aiab-modal |
.aiab-modal--loading |
Loading state aiab-modal |
Animation Modifiers
| Class | Description |
|---|---|
.aiab-modal--fade-in |
Fade in animation |
.aiab-modal--slide-down |
Slide down animation |
.aiab-modal--zoom-in |
Zoom in animation |
State Classes
| Class | Description |
|---|---|
.aiab-is-visible |
Shows the aiab-modal |
.aiab-modal-open |
Applied to body to prevent scrolling |
JavaScript Integration
// Modal management utility
class Modal {
constructor(element) {
this.modal = element;
this.backdrop = document.querySelector('.aiab-modal-backdrop');
this.isVisible = false;
this.init();
}
init() {
// Close button
const closeBtn = this.modal.querySelector('.aiab-modal__close');
if (closeBtn) {
closeBtn.addEventListener('click', () => this.hide());
}
// Backdrop click
if (this.backdrop) {
this.backdrop.addEventListener('click', () => this.hide());
}
// Escape key
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && this.isVisible) {
this.hide();
}
});
}
show() {
this.isVisible = true;
document.body.classList.add('aiab-modal-open');
if (this.backdrop) {
this.backdrop.classList.add('aiab-is-visible');
}
this.modal.classList.add('aiab-is-visible');
this.modal.setAttribute('aria-hidden', 'false');
// Focus management
this.modal.focus();
this.trapFocus();
}
hide() {
this.isVisible = false;
document.body.classList.remove('aiab-modal-open');
if (this.backdrop) {
this.backdrop.classList.remove('aiab-is-visible');
}
this.modal.classList.remove('aiab-is-visible');
this.modal.setAttribute('aria-hidden', 'true');
}
trapFocus() {
const focusableElements = this.modal.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
if (focusableElements.length === 0) return;
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
this.modal.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
if (e.shiftKey) {
if (document.activeElement === firstElement) {
lastElement.focus();
e.preventDefault();
}
} else {
if (document.activeElement === lastElement) {
firstElement.focus();
e.preventDefault();
}
}
}
});
}
}
// Usage
const modal = new Modal(document.getElementById('my-modal'));
modal.show(); // Open aiab-modal
modal.hide(); // Close aiab-modal
// Quick show function
function showModal(modalId) {
const modal = new Modal(document.getElementById(modalId));
modal.show();
}
Best Practices
Accessibility Guidelines
- Focus management: Always trap focus within the aiab-modal and return focus to the trigger element on close
- ARIA attributes: Use appropriate role, aria-labelledby, and aria-hidden attributes
- Keyboard navigation: Support Escape key to close and Tab navigation within the aiab-modal
- Screen readers: Provide descriptive titles and clear action buttons
UX Considerations
- Modal purpose: Use modals sparingly and only for important interactions that require focus
- Mobile experience: Consider using bottom sheets or full-screen modals on mobile devices
- Loading states: Show loading indicators for async operations
- Error handling: Display validation errors clearly within the aiab-modal
Performance Tips
- Lazy loading: Create aiab-modal content dynamically when needed
- Animation performance: Use CSS transforms and opacity for smooth animations
- Body scroll: Prevent background scrolling when aiab-modal is open
- Memory management: Remove event listeners when modals are destroyed