Skip to main content

Modal Component

Fully accessible modal dialog with keyboard navigation, focus management, and animation support

Live Demo

Loading demo...

Documentation

# Modal Component

A fully accessible modal dialog component with multiple variants, keyboard navigation, focus management, and animation support. Built with vanilla JavaScript and modern CSS.

Features

  • Multiple Variants: Basic, form, confirmation, fullscreen, scrollable
  • Accessibility: ARIA attributes, focus trap, screen reader support
  • Keyboard Navigation: Escape to close, Tab cycling within modal
  • Focus Management: Auto-focus first element, restore focus on close
  • Animation: Smooth open/close transitions
  • Backdrop: Click-to-close with blur effect
  • Programmatic API: Create modals dynamically
  • Form Support: Unsaved changes warning
  • Responsive: Mobile-optimized fullscreen on small screens

Usage Examples

Basic Modal

Programmatic Modal

// Create alert
modalComponent.alert('Operation completed successfully!', 'Success');

// Confirm dialog
const confirmed = await modalComponent.confirm('Delete this item?');
if (confirmed) {
// Perform deletion
}

// Prompt for input
const name = await modalComponent.prompt('Enter your name:', 'Welcome');

Custom Modal Creation

const modal = modalComponent.createModal({
title: 'Custom Modal',
content: '

Custom content here

',
size: 'wide',
buttons: [
{ text: 'Cancel', variant: 'secondary', action: 'dismiss' },
{ text: 'Save', variant: 'primary', id: 'save-btn' }
]
});

modalComponent.openModal(modal.id);

Event Handling

// Listen for modal events
document.addEventListener('modal:opened', (e) => {
console.log('Modal opened:', e.detail.modalId);
});

document.addEventListener('modal:closed', (e) => {
console.log('Modal closed:', e.detail.modalId);
});

// Trigger via events
document.dispatchEvent(new CustomEvent('modal:open', {
detail: { modalId: 'modal-basic' }
}));

Modal Types

Basic Modal


Standard modal with header, body, and footer sections.

Form Modal


Modal containing form elements with validation support.

Confirmation Modal


Alert-style modal for confirming actions with danger styling.

Fullscreen Modal


Takes up entire viewport, ideal for complex content.

Scrollable Modal


Fixed header/footer with scrollable body content.

API Methods

MethodDescriptionParametersReturns
openModal(id, options)Open modal by IDid: string, options: objectvoid
closeModal(id, options)Close modalid: string, options: objectvoid
toggleModal(id)Toggle modal stateid: stringvoid
createModal(options)Create modal dynamicallyoptions: objectHTMLElement
alert(message, title)Show alert modalmessage: string, title: stringHTMLElement
confirm(message, title)Show confirmationmessage: string, title: stringPromise
prompt(message, title, default)Show input promptmessage: string, title: string, default: stringPromisenull>

Events

EventDescriptionDetail
modal:openedFired when modal opens{ modalId, modal }
modal:closedFired when modal closes{ modalId, modal }
modal:openTrigger modal open{ modalId, options }
modal:closeTrigger modal close{ modalId, options }
modal:toggleToggle modal state{ modalId }

Accessibility Notes

  • Role & ARIA: Proper role="dialog" with aria-labelledby and aria-describedby
  • Focus Management: Automatic focus trap within modal, restores focus on close
  • Keyboard Navigation:
- Escape - Close modal
- Tab - Cycle through focusable elements
- Shift+Tab - Reverse cycle
  • Screen Reader: Announces modal opening/closing with proper context
  • Backdrop: Semi-transparent with blur for visual separation
  • Alert Dialogs: Uses role="alertdialog" for critical confirmations
  • Form Labels: All form inputs have associated labels

CSS Variables

/ Customize modal appearance /
--modal-bg: white;
--modal-backdrop: rgba(0, 0, 0, 0.5);
--modal-border-radius: 0.75rem;
--modal-shadow: 0 20px 25px rgba(0, 0, 0, 0.15);
--modal-max-width: 32rem;
--modal-z-index: 9999;

Best Practices

1. Always provide meaningful titles and descriptions
2. Use appropriate modal size for content
3. Avoid nested modals
4. Provide clear action buttons
5. Handle unsaved form data before closing
6. Use confirmation modals for destructive actions
7. Ensure content is scrollable if needed
8. Test keyboard navigation thoroughly

Component Code

HTML Structure
<!-- Modal Component HTML -->
<div class="modal-showcase">
  <!-- Trigger Buttons -->
  <button class="modal-trigger" data-modal="modal-basic" aria-label="Open basic modal">
    Open Basic Modal
  </button>

  <button class="modal-trigger" data-modal="modal-form" aria-label="Open form modal">
    Open Form Modal
  </button>

  <button class="modal-trigger" data-modal="modal-confirm" aria-label="Open confirmation modal">
    Open Confirmation Modal
  </button>

  <button class="modal-trigger" data-modal="modal-fullscreen" aria-label="Open fullscreen modal">
    Open Fullscreen Modal
  </button>

  <button class="modal-trigger" data-modal="modal-scrollable" aria-label="Open scrollable modal">
    Open Scrollable Modal
  </button>

  <!-- Basic Modal -->
  <div id="modal-basic" class="modal" role="dialog" aria-labelledby="modal-basic-title" aria-describedby="modal-basic-desc" aria-hidden="true">
    <div class="modal-backdrop"></div>
    <div class="modal-container">
      <div class="modal-content">
        <header class="modal-header">
          <h2 id="modal-basic-title" class="modal-title">Basic Modal</h2>
          <button class="modal-close" aria-label="Close modal">
            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
              <line x1="18" y1="6" x2="6" y2="18"/>
              <line x1="6" y1="6" x2="18" y2="18"/>
            </svg>
          </button>
        </header>
        <div class="modal-body" id="modal-basic-desc">
          <p>This is a basic modal with header, body, and footer sections. It can be closed by clicking the X button, pressing Escape, or clicking outside the modal.</p>
        </div>
        <footer class="modal-footer">
          <button class="modal-button modal-button-secondary" data-modal-dismiss>Cancel</button>
          <button class="modal-button modal-button-primary">Confirm</button>
        </footer>
      </div>
    </div>
  </div>

  <!-- Form Modal -->
  <div id="modal-form" class="modal" role="dialog" aria-labelledby="modal-form-title" aria-hidden="true">
    <div class="modal-backdrop"></div>
    <div class="modal-container">
      <div class="modal-content modal-content-wide">
        <header class="modal-header">
          <h2 id="modal-form-title" class="modal-title">Contact Form</h2>
          <button class="modal-close" aria-label="Close modal">
            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
              <line x1="18" y1="6" x2="6" y2="18"/>
              <line x1="6" y1="6" x2="18" y2="18"/>
            </svg>
          </button>
        </header>
        <div class="modal-body">
          <form class="modal-form">
            <div class="form-group">
              <label for="name" class="form-label">Name</label>
              <input type="text" id="name" class="form-input" required aria-required="true">
            </div>
            <div class="form-group">
              <label for="email" class="form-label">Email</label>
              <input type="email" id="email" class="form-input" required aria-required="true">
            </div>
            <div class="form-group">
              <label for="message" class="form-label">Message</label>
              <textarea id="message" class="form-textarea" rows="4" required aria-required="true"></textarea>
            </div>
          </form>
        </div>
        <footer class="modal-footer">
          <button class="modal-button modal-button-secondary" data-modal-dismiss>Cancel</button>
          <button class="modal-button modal-button-primary" type="submit">Submit</button>
        </footer>
      </div>
    </div>
  </div>

  <!-- Confirmation Modal -->
  <div id="modal-confirm" class="modal modal-small" role="alertdialog" aria-labelledby="modal-confirm-title" aria-describedby="modal-confirm-desc" aria-hidden="true">
    <div class="modal-backdrop"></div>
    <div class="modal-container">
      <div class="modal-content">
        <header class="modal-header modal-header-danger">
          <div class="modal-icon modal-icon-danger">
            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
              <circle cx="12" cy="12" r="10"/>
              <line x1="12" y1="8" x2="12" y2="12"/>
              <line x1="12" y1="16" x2="12.01" y2="16"/>
            </svg>
          </div>
          <h2 id="modal-confirm-title" class="modal-title">Confirm Action</h2>
        </header>
        <div class="modal-body" id="modal-confirm-desc">
          <p>Are you sure you want to delete this item? This action cannot be undone.</p>
        </div>
        <footer class="modal-footer modal-footer-center">
          <button class="modal-button modal-button-secondary" data-modal-dismiss>Cancel</button>
          <button class="modal-button modal-button-danger">Delete</button>
        </footer>
      </div>
    </div>
  </div>

  <!-- Fullscreen Modal -->
  <div id="modal-fullscreen" class="modal modal-fullscreen" role="dialog" aria-labelledby="modal-fullscreen-title" aria-hidden="true">
    <div class="modal-backdrop"></div>
    <div class="modal-container">
      <div class="modal-content">
        <header class="modal-header">
          <h2 id="modal-fullscreen-title" class="modal-title">Fullscreen Modal</h2>
          <button class="modal-close" aria-label="Close modal">
            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
              <line x1="18" y1="6" x2="6" y2="18"/>
              <line x1="6" y1="6" x2="18" y2="18"/>
            </svg>
          </button>
        </header>
        <div class="modal-body">
          <p>This modal takes up the full screen, perfect for complex forms, media viewers, or detailed content that requires maximum space.</p>
          <p>The content area is scrollable if needed, and the modal can still be closed using the usual methods.</p>
        </div>
        <footer class="modal-footer">
          <button class="modal-button modal-button-primary" data-modal-dismiss>Close</button>
        </footer>
      </div>
    </div>
  </div>

  <!-- Scrollable Modal -->
  <div id="modal-scrollable" class="modal" role="dialog" aria-labelledby="modal-scrollable-title" aria-hidden="true">
    <div class="modal-backdrop"></div>
    <div class="modal-container">
      <div class="modal-content">
        <header class="modal-header modal-header-sticky">
          <h2 id="modal-scrollable-title" class="modal-title">Terms and Conditions</h2>
          <button class="modal-close" aria-label="Close modal">
            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
              <line x1="18" y1="6" x2="6" y2="18"/>
              <line x1="6" y1="6" x2="18" y2="18"/>
            </svg>
          </button>
        </header>
        <div class="modal-body modal-body-scrollable">
          <h3>1. Introduction</h3>
          <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>

          <h3>2. Terms of Use</h3>
          <p>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>

          <h3>3. Privacy Policy</h3>
          <p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p>

          <h3>4. User Responsibilities</h3>
          <p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>

          <h3>5. Disclaimers</h3>
          <p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium.</p>

          <h3>6. Contact Information</h3>
          <p>At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum.</p>
        </div>
        <footer class="modal-footer modal-footer-sticky">
          <button class="modal-button modal-button-secondary" data-modal-dismiss>Decline</button>
          <button class="modal-button modal-button-primary">Accept</button>
        </footer>
      </div>
    </div>
  </div>
</div>

Properties & Features

Tags

modaldialogoverlayaccessiblekeyboardfocus-trap

Features

  • Multiple Variants
  • Accessibility
  • Keyboard Navigation
  • Focus Management
  • Animation
  • Backdrop
  • Programmatic API
  • Form Support
  • Responsive

Browser Support

Chrome 90+Firefox 88+Safari 14+Edge 90+

Last Updated

2025-10-30

Usage Tips

Installation

Copy the HTML, CSS, and JavaScript code above into your project files.

Customization

Modify CSS variables and classes to match your design system.

Accessibility

Ensure all interactive elements have proper ARIA labels and keyboard support.

Responsive Design

Test the component across different screen sizes and devices.