mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-08-03 02:11:07 +05:30
5.3 KiB
5.3 KiB
Modal System Design
Date: 2026-03-24 Version: 4.0.0 Status: Approved
Overview
Replace the existing dialog implementations with a unified modal system that provides:
- Glassmorphism backdrop matching app aesthetic
- Full accessibility (ARIA, focus trap, keyboard navigation)
- Smooth fade + scale animations
- Consistent API via
ModalManagerclass
Decisions Made
| Decision | Choice | Rationale |
|---|---|---|
| Architecture | Unified ModalManager class |
Cleaner, consistent behavior across all modals |
| Backdrop style | Glassmorphism | Matches existing app design language |
| Focus management | Focus first interactive element | Standard, predictable behavior |
| Animation | Fade + scale (95% → 100%) | Modern, subtle effect |
| Implementation | Custom (not native <dialog>) |
Full control, no polyfill concerns |
Architecture
File Structure
src/
├── utils/
│ └── ModalManager.js # Core modal logic (~150 lines)
├── styles/
│ └── modal.css # Unified modal styles (~200 lines)
└── index.html # Updated dialog markup
ModalManager Class
class ModalManager {
constructor(element, options = {})
open() // Show modal with animation
close() // Hide modal with animation
destroy() // Cleanup event listeners
on(event, callback) // Event subscription
// Internal
#createBackdrop() // Create glassmorphism backdrop
#trapFocus() // Manage focus within modal
#handleKeydown(e) // Escape key handler
#getFocusableElements() // Query focusable children
}
Events
open— Fired after open animation completesclose— Fired after close animation completes
CSS Design
Variables (from tokens.css)
--z-modal: 200;
--transition-normal: 200ms cubic-bezier(0.4, 0, 0.2, 1);
--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1);
--radius-lg: 0.5rem;
Backdrop
.modal-backdrop {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.4);
backdrop-filter: blur(4px);
-webkit-backdrop-filter: blur(4px);
z-index: var(--z-modal);
}
Modal Container
.modal {
position: fixed;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
z-index: calc(var(--z-modal) + 1);
opacity: 0;
visibility: hidden;
transition: opacity var(--transition-normal),
visibility var(--transition-normal);
}
.modal.open {
opacity: 1;
visibility: visible;
}
Modal Content (with animation)
.modal-content {
background: hsl(var(--background));
border-radius: var(--radius-lg);
box-shadow: var(--shadow-xl);
max-width: 90vw;
max-height: 90vh;
overflow: hidden;
transform: scale(0.95);
transition: transform var(--transition-normal);
}
.modal.open .modal-content {
transform: scale(1);
}
HTML Structure
All dialogs convert to unified structure:
<div id="export-dialog"
class="modal"
role="dialog"
aria-modal="true"
aria-labelledby="export-dialog-title">
<div class="modal-backdrop" data-close></div>
<div class="modal-content">
<div class="modal-header">
<h3 id="export-dialog-title">Export Options</h3>
<button class="modal-close" aria-label="Close">×</button>
</div>
<div class="modal-body">
<!-- Dialog-specific content -->
</div>
<div class="modal-footer">
<button class="btn btn-secondary" data-close>Cancel</button>
<button class="btn btn-primary">Confirm</button>
</div>
</div>
</div>
Key Attributes
role="dialog"— Screen reader identificationaria-modal="true"— Prevents screen reader from accessing backgroundaria-labelledby— References the dialog titledata-close— Click handler for closing (backdrop, cancel buttons)
Accessibility Features
- Focus trap — Tab cycles within modal only
- Focus first element — Auto-focuses first input/button on open
- Escape key — Closes modal
- Click outside — Clicking backdrop closes modal
- Focus restoration — Returns focus to trigger element on close
- ARIA attributes — Proper screen reader support
Dialogs to Migrate
| Dialog ID | Current Class | Complexity |
|---|---|---|
find-dialog |
.find-dialog |
Simple |
export-dialog |
.export-dialog |
Complex (many sections) |
print-preview-overlay |
.export-dialog |
Medium |
table-generator-dialog |
.export-dialog |
Simple |
ascii-art-dialog |
.export-dialog |
Medium |
universal-converter-dialog |
.export-dialog |
Complex |
batch-dialog |
.batch-dialog |
Complex |
pdf-editor-dialog |
.export-dialog |
Complex |
header-footer-dialog |
.export-dialog |
Medium |
field-picker-dialog |
.export-dialog |
Simple |
Migration Steps
- Create
src/utils/ModalManager.js - Create
src/styles/modal.css - Update
index.htmlto include new stylesheet - Convert each dialog HTML to new structure
- Initialize
ModalManagerinstances inrenderer.js - Remove old CSS from
styles.css - Test all dialogs
Out of Scope
- Modal nesting (stacked modals) — can be added later if needed
- Animated backdrop (currently static blur)
- Modal size variants (small/large/fullscreen) — can use inline styles