Category · Interactive Difficulty Level · Intermediate Published on · January 15, 2024

Modal & Popup Components

A comprehensive collection of modern modal dialogs and popup components with smooth animations, accessibility features, and customizable designs

#modal #popup #dialog #overlay #components #ui

Responsive Design

Yes

Dark Mode Support

No

lines

11

Browser Compatibility

No

Live Preview

Interact with the component without leaving the page.

600px

Modal & Popup Components

A comprehensive collection of modern modal dialogs and popup components featuring smooth animations, accessibility support, keyboard navigation, and customizable designs for enhanced user interaction.

Features

  • Multiple Modal Types: Basic modals, confirmation dialogs, image galleries, and forms
  • Smooth Animations: CSS-based transitions with customizable timing
  • Accessibility Support: ARIA attributes, keyboard navigation, and focus management
  • Responsive Design: Adapts to all screen sizes and orientations
  • Backdrop Control: Customizable backdrop behavior and click-to-close
  • Stacking Support: Multiple modals with proper z-index management
  • Auto-positioning: Smart positioning to avoid viewport overflow
  • Modern Design: Clean, minimalist aesthetics with subtle effects

Demo

<div class="modal-demo-container">
  <!-- Modal Triggers -->
  <div class="modal-triggers">
    <h3>Modal Types</h3>
    <div class="trigger-grid">
      <button class="trigger-btn" data-modal="basic-modal">
        <span class="trigger-icon">📄</span>
        Basic Modal
      </button>
      
      <button class="trigger-btn" data-modal="confirm-modal">
        <span class="trigger-icon">❓</span>
        Confirmation
      </button>
      
      <button class="trigger-btn" data-modal="form-modal">
        <span class="trigger-icon">📝</span>
        Form Modal
      </button>
      
      <button class="trigger-btn" data-modal="image-modal">
        <span class="trigger-icon">🖼️</span>
        Image Gallery
      </button>
      
      <button class="trigger-btn" data-modal="video-modal">
        <span class="trigger-icon">🎥</span>
        Video Modal
      </button>
      
      <button class="trigger-btn" data-modal="fullscreen-modal">
        <span class="trigger-icon">⛶</span>
        Fullscreen
      </button>
    </div>
  </div>

  <!-- Popup Triggers -->
  <div class="popup-triggers">
    <h3>Popup Types</h3>
    <div class="trigger-grid">
      <button class="trigger-btn" data-popup="tooltip-popup">
        <span class="trigger-icon">💬</span>
        Tooltip
      </button>
      
      <button class="trigger-btn" data-popup="notification-popup">
        <span class="trigger-icon">🔔</span>
        Notification
      </button>
      
      <button class="trigger-btn" data-popup="dropdown-popup">
        <span class="trigger-icon">📋</span>
        Dropdown
      </button>
      
      <button class="trigger-btn" data-popup="context-popup">
        <span class="trigger-icon">⚙️</span>
        Context Menu
      </button>
    </div>
  </div>
</div>

<!-- Basic Modal -->
<div class="modal-overlay" id="basic-modal">
  <div class="modal-container">
    <div class="modal-header">
      <h2 class="modal-title">Basic Modal</h2>
      <button class="modal-close" aria-label="Close modal">
        <svg viewBox="0 0 24 24" fill="currentColor">
          <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
        </svg>
      </button>
    </div>
    <div class="modal-body">
      <p>This is a basic modal dialog with smooth animations and accessibility features. It includes proper focus management and keyboard navigation support.</p>
      <p>You can customize the appearance, animations, and behavior to match your design requirements.</p>
    </div>
    <div class="modal-footer">
      <button class="btn btn-secondary modal-cancel">Cancel</button>
      <button class="btn btn-primary">Confirm</button>
    </div>
  </div>
</div>

<!-- Confirmation Modal -->
<div class="modal-overlay" id="confirm-modal">
  <div class="modal-container modal-small">
    <div class="modal-header">
      <div class="modal-icon warning">
        <svg viewBox="0 0 24 24" fill="currentColor">
          <path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/>
        </svg>
      </div>
      <h2 class="modal-title">Confirm Action</h2>
      <button class="modal-close" aria-label="Close modal">
        <svg viewBox="0 0 24 24" fill="currentColor">
          <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
        </svg>
      </button>
    </div>
    <div class="modal-body">
      <p>Are you sure you want to delete this item? This action cannot be undone.</p>
    </div>
    <div class="modal-footer">
      <button class="btn btn-secondary modal-cancel">Cancel</button>
      <button class="btn btn-danger">Delete</button>
    </div>
  </div>
</div>

<!-- Form Modal -->
<div class="modal-overlay" id="form-modal">
  <div class="modal-container">
    <div class="modal-header">
      <h2 class="modal-title">Contact Form</h2>
      <button class="modal-close" aria-label="Close modal">
        <svg viewBox="0 0 24 24" fill="currentColor">
          <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
        </svg>
      </button>
    </div>
    <div class="modal-body">
      <form class="modal-form">
        <div class="form-group">
          <label for="modal-name">Name</label>
          <input type="text" id="modal-name" name="name" required>
        </div>
        <div class="form-group">
          <label for="modal-email">Email</label>
          <input type="email" id="modal-email" name="email" required>
        </div>
        <div class="form-group">
          <label for="modal-message">Message</label>
          <textarea id="modal-message" name="message" rows="4" required></textarea>
        </div>
      </form>
    </div>
    <div class="modal-footer">
      <button class="btn btn-secondary modal-cancel">Cancel</button>
      <button class="btn btn-primary" type="submit">Send Message</button>
    </div>
  </div>
</div>

<!-- Image Modal -->
<div class="modal-overlay" id="image-modal">
  <div class="modal-container modal-image">
    <button class="modal-close" aria-label="Close modal">
      <svg viewBox="0 0 24 24" fill="currentColor">
        <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
      </svg>
    </button>
    <div class="image-gallery">
      <div class="gallery-main">
        <img src="https://picsum.photos/800/600?random=1" alt="Gallery image" class="gallery-image">
        <div class="gallery-controls">
          <button class="gallery-btn prev" aria-label="Previous image">
            <svg viewBox="0 0 24 24" fill="currentColor">
              <path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/>
            </svg>
          </button>
          <button class="gallery-btn next" aria-label="Next image">
            <svg viewBox="0 0 24 24" fill="currentColor">
              <path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/>
            </svg>
          </button>
        </div>
      </div>
      <div class="gallery-info">
        <h3>Image Gallery</h3>
        <p>Image 1 of 5</p>
      </div>
    </div>
  </div>
</div>

<!-- Video Modal -->
<div class="modal-overlay" id="video-modal">
  <div class="modal-container modal-video">
    <button class="modal-close" aria-label="Close modal">
      <svg viewBox="0 0 24 24" fill="currentColor">
        <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
      </svg>
    </button>
    <div class="video-container">
      <iframe 
        src="https://www.youtube.com/embed/dQw4w9WgXcQ" 
        frameborder="0" 
        allowfullscreen
        title="Video player">
      </iframe>
    </div>
  </div>
</div>

<!-- Fullscreen Modal -->
<div class="modal-overlay" id="fullscreen-modal">
  <div class="modal-container modal-fullscreen">
    <div class="modal-header">
      <h2 class="modal-title">Fullscreen Modal</h2>
      <button class="modal-close" aria-label="Close modal">
        <svg viewBox="0 0 24 24" fill="currentColor">
          <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
        </svg>
      </button>
    </div>
    <div class="modal-body">
      <div class="fullscreen-content">
        <h3>Fullscreen Experience</h3>
        <p>This modal takes up the entire viewport, perfect for immersive content, detailed forms, or complex interfaces.</p>
        <div class="content-grid">
          <div class="content-card">
            <h4>Feature 1</h4>
            <p>Detailed description of the first feature with comprehensive information.</p>
          </div>
          <div class="content-card">
            <h4>Feature 2</h4>
            <p>Detailed description of the second feature with comprehensive information.</p>
          </div>
          <div class="content-card">
            <h4>Feature 3</h4>
            <p>Detailed description of the third feature with comprehensive information.</p>
          </div>
        </div>
      </div>
    </div>
    <div class="modal-footer">
      <button class="btn btn-secondary modal-cancel">Close</button>
      <button class="btn btn-primary">Save Changes</button>
    </div>
  </div>
</div>

<!-- Tooltip Popup -->
<div class="popup-container" id="tooltip-popup">
  <div class="popup-content tooltip">
    <div class="tooltip-arrow"></div>
    <p>This is a helpful tooltip that provides additional information about the element.</p>
  </div>
</div>

<!-- Notification Popup -->
<div class="popup-container" id="notification-popup">
  <div class="popup-content notification success">
    <div class="notification-icon">
      <svg viewBox="0 0 24 24" fill="currentColor">
        <path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>
      </svg>
    </div>
    <div class="notification-content">
      <h4>Success!</h4>
      <p>Your action has been completed successfully.</p>
    </div>
    <button class="notification-close" aria-label="Close notification">
      <svg viewBox="0 0 24 24" fill="currentColor">
        <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
      </svg>
    </button>
  </div>
</div>

<!-- Dropdown Popup -->
<div class="popup-container" id="dropdown-popup">
  <div class="popup-content dropdown">
    <div class="dropdown-header">
      <h4>Options</h4>
    </div>
    <div class="dropdown-list">
      <a href="#" class="dropdown-item">
        <span class="dropdown-icon">📄</span>
        View Details
      </a>
      <a href="#" class="dropdown-item">
        <span class="dropdown-icon">✏️</span>
        Edit Item
      </a>
      <a href="#" class="dropdown-item">
        <span class="dropdown-icon">📋</span>
        Copy Link
      </a>
      <div class="dropdown-divider"></div>
      <a href="#" class="dropdown-item danger">
        <span class="dropdown-icon">🗑️</span>
        Delete Item
      </a>
    </div>
  </div>
</div>

<!-- Context Menu Popup -->
<div class="popup-container" id="context-popup">
  <div class="popup-content context-menu">
    <div class="context-list">
      <button class="context-item">
        <span class="context-icon">📋</span>
        Copy
        <span class="context-shortcut">Ctrl+C</span>
      </button>
      <button class="context-item">
        <span class="context-icon">📄</span>
        Paste
        <span class="context-shortcut">Ctrl+V</span>
      </button>
      <button class="context-item">
        <span class="context-icon">✂️</span>
        Cut
        <span class="context-shortcut">Ctrl+X</span>
      </button>
      <div class="context-divider"></div>
      <button class="context-item">
        <span class="context-icon">🔄</span>
        Refresh
        <span class="context-shortcut">F5</span>
      </button>
      <button class="context-item">
        <span class="context-icon">⚙️</span>
        Settings
      </button>
    </div>
  </div>
</div>
.modal-demo-container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 2rem;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}

.modal-triggers,
.popup-triggers {
  margin-bottom: 3rem;
  padding: 2rem;
  background: white;
  border-radius: 12px;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
  border: 1px solid #e5e7eb;
}

.modal-triggers h3,
.popup-triggers h3 {
  margin: 0 0 2rem 0;
  font-size: 1.5rem;
  font-weight: 700;
  color: #1a1a1a;
  text-align: center;
}

.trigger-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 1rem;
}

.trigger-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  padding: 1.5rem;
  border: 2px solid #e5e7eb;
  background: white;
  border-radius: 12px;
  cursor: pointer;
  transition: all 0.3s ease;
  font-weight: 600;
  color: #374151;
}

.trigger-btn:hover {
  border-color: #3b82f6;
  background: #f8fafc;
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(59, 130, 246, 0.15);
}

.trigger-icon {
  font-size: 2rem;
  margin-bottom: 0.5rem;
}

/* Modal Overlay */
.modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(4px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  opacity: 0;
  visibility: hidden;
  transition: all 0.3s ease;
  padding: 1rem;
}

.modal-overlay.active {
  opacity: 1;
  visibility: visible;
}

.modal-overlay.active .modal-container {
  transform: scale(1) translateY(0);
  opacity: 1;
}

/* Modal Container */
.modal-container {
  background: white;
  border-radius: 16px;
  box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
  max-width: 500px;
  width: 100%;
  max-height: 90vh;
  overflow: hidden;
  transform: scale(0.9) translateY(20px);
  opacity: 0;
  transition: all 0.3s ease;
  position: relative;
}

.modal-container.modal-small {
  max-width: 400px;
}

.modal-container.modal-large {
  max-width: 800px;
}

.modal-container.modal-fullscreen {
  max-width: 95vw;
  max-height: 95vh;
  width: 95vw;
  height: 95vh;
}

.modal-container.modal-image {
  max-width: 90vw;
  max-height: 90vh;
  background: transparent;
  box-shadow: none;
}

.modal-container.modal-video {
  max-width: 80vw;
  max-height: 80vh;
  background: #000;
  border-radius: 8px;
}

/* Modal Header */
.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1.5rem 2rem;
  border-bottom: 1px solid #e5e7eb;
  background: #f9fafb;
}

.modal-title {
  margin: 0;
  font-size: 1.25rem;
  font-weight: 700;
  color: #1a1a1a;
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.modal-icon {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-right: 1rem;
}

.modal-icon.warning {
  background: #fef3c7;
  color: #d97706;
}

.modal-icon.success {
  background: #d1fae5;
  color: #059669;
}

.modal-icon.error {
  background: #fee2e2;
  color: #dc2626;
}

.modal-icon svg {
  width: 20px;
  height: 20px;
}

.modal-close {
  width: 40px;
  height: 40px;
  border: none;
  background: none;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: #6b7280;
  transition: all 0.2s ease;
}

.modal-close:hover {
  background: #f3f4f6;
  color: #374151;
}

.modal-close svg {
  width: 20px;
  height: 20px;
}

/* Modal Body */
.modal-body {
  padding: 2rem;
  overflow-y: auto;
  max-height: 60vh;
}

.modal-body p {
  margin: 0 0 1rem 0;
  line-height: 1.6;
  color: #374151;
}

.modal-body p:last-child {
  margin-bottom: 0;
}

/* Modal Footer */
.modal-footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 1rem;
  padding: 1.5rem 2rem;
  border-top: 1px solid #e5e7eb;
  background: #f9fafb;
}

/* Form Styles */
.modal-form {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

.form-group {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.form-group label {
  font-weight: 600;
  color: #374151;
  font-size: 0.875rem;
}

.form-group input,
.form-group textarea {
  padding: 0.75rem;
  border: 2px solid #e5e7eb;
  border-radius: 8px;
  font-size: 1rem;
  transition: border-color 0.2s ease;
}

.form-group input:focus,
.form-group textarea:focus {
  outline: none;
  border-color: #3b82f6;
  box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}

/* Button Styles */
.btn {
  padding: 0.75rem 1.5rem;
  border: none;
  border-radius: 8px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.2s ease;
  font-size: 0.875rem;
}

.btn-primary {
  background: #3b82f6;
  color: white;
}

.btn-primary:hover {
  background: #2563eb;
  transform: translateY(-1px);
}

.btn-secondary {
  background: #f3f4f6;
  color: #374151;
  border: 1px solid #d1d5db;
}

.btn-secondary:hover {
  background: #e5e7eb;
}

.btn-danger {
  background: #ef4444;
  color: white;
}

.btn-danger:hover {
  background: #dc2626;
  transform: translateY(-1px);
}

/* Image Gallery */
.image-gallery {
  position: relative;
  background: #000;
  border-radius: 8px;
  overflow: hidden;
}

.gallery-main {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 400px;
}

.gallery-image {
  max-width: 100%;
  max-height: 70vh;
  object-fit: contain;
}

.gallery-controls {
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  transform: translateY(-50%);
  display: flex;
  justify-content: space-between;
  padding: 0 1rem;
  pointer-events: none;
}

.gallery-btn {
  width: 50px;
  height: 50px;
  border: none;
  background: rgba(255, 255, 255, 0.9);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all 0.2s ease;
  pointer-events: auto;
}

.gallery-btn:hover {
  background: white;
  transform: scale(1.1);
}

.gallery-btn svg {
  width: 24px;
  height: 24px;
  color: #374151;
}

.gallery-info {
  padding: 1rem;
  background: rgba(0, 0, 0, 0.8);
  color: white;
  text-align: center;
}

.gallery-info h3 {
  margin: 0 0 0.5rem 0;
  font-size: 1.125rem;
}

.gallery-info p {
  margin: 0;
  color: #d1d5db;
  font-size: 0.875rem;
}

/* Video Container */
.video-container {
  position: relative;
  width: 100%;
  height: 0;
  padding-bottom: 56.25%; /* 16:9 aspect ratio */
}

.video-container iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

/* Fullscreen Content */
.fullscreen-content {
  padding: 2rem 0;
}

.content-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 2rem;
  margin-top: 2rem;
}

.content-card {
  padding: 2rem;
  background: #f9fafb;
  border-radius: 12px;
  border: 1px solid #e5e7eb;
}

.content-card h4 {
  margin: 0 0 1rem 0;
  font-size: 1.125rem;
  font-weight: 700;
  color: #1a1a1a;
}

.content-card p {
  margin: 0;
  color: #6b7280;
  line-height: 1.6;
}

/* Popup Container */
.popup-container {
  position: fixed;
  z-index: 1100;
  opacity: 0;
  visibility: hidden;
  transition: all 0.2s ease;
  pointer-events: none;
}

.popup-container.active {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}

.popup-content {
  background: white;
  border-radius: 8px;
  box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
  border: 1px solid #e5e7eb;
  transform: scale(0.95) translateY(-10px);
  transition: all 0.2s ease;
}

.popup-container.active .popup-content {
  transform: scale(1) translateY(0);
}

/* Tooltip */
.tooltip {
  padding: 0.75rem 1rem;
  max-width: 250px;
  font-size: 0.875rem;
  color: #374151;
  line-height: 1.4;
  position: relative;
}

.tooltip-arrow {
  position: absolute;
  bottom: -6px;
  left: 50%;
  transform: translateX(-50%);
  width: 0;
  height: 0;
  border-left: 6px solid transparent;
  border-right: 6px solid transparent;
  border-top: 6px solid white;
}

/* Notification */
.notification {
  padding: 1rem 1.5rem;
  display: flex;
  align-items: center;
  gap: 1rem;
  min-width: 300px;
  max-width: 400px;
  position: relative;
}

.notification.success {
  background: #f0fdf4;
  border-left: 4px solid #22c55e;
}

.notification.warning {
  background: #fffbeb;
  border-left: 4px solid #f59e0b;
}

.notification.error {
  background: #fef2f2;
  border-left: 4px solid #ef4444;
}

.notification-icon {
  width: 24px;
  height: 24px;
  flex-shrink: 0;
}

.notification.success .notification-icon {
  color: #22c55e;
}

.notification.warning .notification-icon {
  color: #f59e0b;
}

.notification.error .notification-icon {
  color: #ef4444;
}

.notification-content h4 {
  margin: 0 0 0.25rem 0;
  font-size: 0.875rem;
  font-weight: 600;
  color: #1a1a1a;
}

.notification-content p {
  margin: 0;
  font-size: 0.875rem;
  color: #6b7280;
}

.notification-close {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  width: 24px;
  height: 24px;
  border: none;
  background: none;
  cursor: pointer;
  color: #9ca3af;
  transition: color 0.2s ease;
}

.notification-close:hover {
  color: #6b7280;
}

.notification-close svg {
  width: 16px;
  height: 16px;
}

/* Dropdown */
.dropdown {
  min-width: 200px;
  padding: 0.5rem 0;
}

.dropdown-header {
  padding: 0.75rem 1rem;
  border-bottom: 1px solid #e5e7eb;
  margin-bottom: 0.5rem;
}

.dropdown-header h4 {
  margin: 0;
  font-size: 0.875rem;
  font-weight: 600;
  color: #374151;
}

.dropdown-list {
  display: flex;
  flex-direction: column;
}

.dropdown-item {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.75rem 1rem;
  text-decoration: none;
  color: #374151;
  font-size: 0.875rem;
  transition: background-color 0.2s ease;
}

.dropdown-item:hover {
  background: #f3f4f6;
}

.dropdown-item.danger {
  color: #dc2626;
}

.dropdown-item.danger:hover {
  background: #fef2f2;
}

.dropdown-icon {
  font-size: 1rem;
}

.dropdown-divider {
  height: 1px;
  background: #e5e7eb;
  margin: 0.5rem 0;
}

/* Context Menu */
.context-menu {
  min-width: 180px;
  padding: 0.5rem 0;
}

.context-list {
  display: flex;
  flex-direction: column;
}

.context-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.5rem 1rem;
  border: none;
  background: none;
  text-align: left;
  cursor: pointer;
  font-size: 0.875rem;
  color: #374151;
  transition: background-color 0.2s ease;
}

.context-item:hover {
  background: #f3f4f6;
}

.context-icon {
  margin-right: 0.75rem;
  font-size: 1rem;
}

.context-shortcut {
  font-size: 0.75rem;
  color: #9ca3af;
}

.context-divider {
  height: 1px;
  background: #e5e7eb;
  margin: 0.5rem 0;
}

/* Dark Theme */
.dark .modal-container {
  background: #1f2937;
  color: #e5e7eb;
}

.dark .modal-header,
.dark .modal-footer {
  background: #111827;
  border-color: #374151;
}

.dark .modal-title {
  color: #f9fafb;
}

.dark .modal-body p {
  color: #d1d5db;
}

.dark .form-group input,
.dark .form-group textarea {
  background: #374151;
  border-color: #4b5563;
  color: #e5e7eb;
}

.dark .form-group input:focus,
.dark .form-group textarea:focus {
  border-color: #3b82f6;
}

.dark .content-card {
  background: #111827;
  border-color: #374151;
}

.dark .popup-content {
  background: #1f2937;
  border-color: #374151;
  color: #e5e7eb;
}

.dark .dropdown-item,
.dark .context-item {
  color: #d1d5db;
}

.dark .dropdown-item:hover,
.dark .context-item:hover {
  background: #374151;
}

/* Responsive Design */
@media (max-width: 768px) {
  .modal-demo-container {
    padding: 1rem;
  }
  
  .modal-triggers,
  .popup-triggers {
    padding: 1.5rem;
  }
  
  .trigger-grid {
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  }
  
  .modal-overlay {
    padding: 0.5rem;
  }
  
  .modal-container {
    max-width: 100%;
    margin: 0;
  }
  
  .modal-container.modal-fullscreen {
    max-width: 100vw;
    max-height: 100vh;
    width: 100vw;
    height: 100vh;
    border-radius: 0;
  }
  
  .modal-header,
  .modal-footer {
    padding: 1rem 1.5rem;
  }
  
  .modal-body {
    padding: 1.5rem;
  }
  
  .content-grid {
    grid-template-columns: 1fr;
  }
  
  .notification {
    min-width: 280px;
    max-width: 320px;
  }
}

@media (max-width: 480px) {
  .trigger-grid {
    grid-template-columns: 1fr;
  }
  
  .trigger-btn {
    padding: 1rem;
  }
  
  .modal-header,
  .modal-footer {
    padding: 1rem;
  }
  
  .modal-body {
    padding: 1rem;
  }
  
  .modal-footer {
    flex-direction: column;
    gap: 0.5rem;
  }
  
  .btn {
    width: 100%;
  }
  
  .gallery-controls {
    padding: 0 0.5rem;
  }
  
  .gallery-btn {
    width: 40px;
    height: 40px;
  }
}

/* Reduced Motion */
@media (prefers-reduced-motion: reduce) {
  .modal-overlay,
  .modal-container,
  .popup-container,
  .popup-content {
    transition: none;
  }
  
  .modal-overlay.active .modal-container {
    transform: none;
  }
  
  .popup-container.active .popup-content {
    transform: none;
  }
}

/* Focus Styles */
.modal-close:focus,
.btn:focus,
.dropdown-item:focus,
.context-item:focus {
  outline: 2px solid #3b82f6;
  outline-offset: 2px;
}

/* Animation Classes */
.fade-in {
  animation: fadeIn 0.3s ease;
}

.slide-up {
  animation: slideUp 0.3s ease;
}

.scale-in {
  animation: scaleIn 0.3s ease;
}

@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes slideUp {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes scaleIn {
  from {
    opacity: 0;
    transform: scale(0.9);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}
class ModalPopupComponents {
  constructor() {
    this.modals = new Map();
    this.popups = new Map();
    this.activeModal = null;
    this.activePopup = null;
    this.modalStack = [];
    this.focusableElements = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
    
    this.init();
  }

  init() {
    this.setupEventListeners();
    this.initializeModals();
    this.initializePopups();
    this.setupKeyboardNavigation();
    this.setupAccessibility();
  }

  setupEventListeners() {
    // Modal triggers
    document.addEventListener('click', (e) => {
      const trigger = e.target.closest('[data-modal]');
      if (trigger) {
        e.preventDefault();
        const modalId = trigger.dataset.modal;
        this.openModal(modalId);
      }
      
      // Popup triggers
      const popupTrigger = e.target.closest('[data-popup]');
      if (popupTrigger) {
        e.preventDefault();
        const popupId = popupTrigger.dataset.popup;
        this.openPopup(popupId, popupTrigger);
      }
      
      // Close buttons
      if (e.target.closest('.modal-close, .modal-cancel')) {
        e.preventDefault();
        this.closeActiveModal();
      }
      
      if (e.target.closest('.notification-close')) {
        e.preventDefault();
        this.closeActivePopup();
      }
      
      // Backdrop click
      if (e.target.classList.contains('modal-overlay')) {
        this.closeActiveModal();
      }
    });

    // Context menu
    document.addEventListener('contextmenu', (e) => {
      const contextTrigger = e.target.closest('[data-context]');
      if (contextTrigger) {
        e.preventDefault();
        this.openContextMenu(e, 'context-popup');
      }
    });

    // Close popups on outside click
    document.addEventListener('click', (e) => {
      if (this.activePopup && !e.target.closest('.popup-container')) {
        this.closeActivePopup();
      }
    });
  }

  initializeModals() {
    const modalElements = document.querySelectorAll('.modal-overlay');
    modalElements.forEach(modal => {
      const modalId = modal.id;
      this.modals.set(modalId, {
        element: modal,
        container: modal.querySelector('.modal-container'),
        isOpen: false,
        previousFocus: null
      });
    });
  }

  initializePopups() {
    const popupElements = document.querySelectorAll('.popup-container');
    popupElements.forEach(popup => {
      const popupId = popup.id;
      this.popups.set(popupId, {
        element: popup,
        content: popup.querySelector('.popup-content'),
        isOpen: false,
        trigger: null
      });
    });
  }

  setupKeyboardNavigation() {
    document.addEventListener('keydown', (e) => {
      // Escape key
      if (e.key === 'Escape') {
        if (this.activePopup) {
          this.closeActivePopup();
        } else if (this.activeModal) {
          this.closeActiveModal();
        }
      }
      
      // Tab navigation within modal
      if (e.key === 'Tab' && this.activeModal) {
        this.handleTabNavigation(e);
      }
    });
  }

  setupAccessibility() {
    // Add ARIA attributes
    this.modals.forEach((modal, id) => {
      modal.element.setAttribute('role', 'dialog');
      modal.element.setAttribute('aria-modal', 'true');
      modal.element.setAttribute('aria-hidden', 'true');
      
      const title = modal.element.querySelector('.modal-title');
      if (title) {
        title.id = `${id}-title`;
        modal.element.setAttribute('aria-labelledby', `${id}-title`);
      }
    });

    this.popups.forEach((popup, id) => {
      popup.element.setAttribute('role', 'tooltip');
      popup.element.setAttribute('aria-hidden', 'true');
    });
  }

  openModal(modalId, options = {}) {
    const modal = this.modals.get(modalId);
    if (!modal || modal.isOpen) return;

    // Store previous focus
    modal.previousFocus = document.activeElement;

    // Close any active popup
    if (this.activePopup) {
      this.closeActivePopup();
    }

    // Add to modal stack
    this.modalStack.push(modalId);
    this.activeModal = modalId;

    // Show modal
    modal.element.style.display = 'flex';
    modal.element.setAttribute('aria-hidden', 'false');
    
    // Trigger animation
    requestAnimationFrame(() => {
      modal.element.classList.add('active');
      if (options.animation) {
        modal.container.classList.add(options.animation);
      }
    });

    // Focus management
    setTimeout(() => {
      this.focusFirstElement(modal.element);
    }, 100);

    // Prevent body scroll
    document.body.style.overflow = 'hidden';
    modal.isOpen = true;

    // Dispatch event
    this.dispatchEvent('modalOpen', { modalId, modal: modal.element });
  }

  closeModal(modalId) {
    const modal = this.modals.get(modalId);
    if (!modal || !modal.isOpen) return;

    // Remove from stack
    const stackIndex = this.modalStack.indexOf(modalId);
    if (stackIndex > -1) {
      this.modalStack.splice(stackIndex, 1);
    }

    // Update active modal
    this.activeModal = this.modalStack.length > 0 ? this.modalStack[this.modalStack.length - 1] : null;

    // Hide modal
    modal.element.classList.remove('active');
    modal.element.setAttribute('aria-hidden', 'true');

    setTimeout(() => {
      modal.element.style.display = 'none';
      modal.container.className = modal.container.className.replace(/\b(fade-in|slide-up|scale-in)\b/g, '');
    }, 300);

    // Restore focus
    if (modal.previousFocus) {
      modal.previousFocus.focus();
      modal.previousFocus = null;
    }

    // Restore body scroll if no modals are open
    if (this.modalStack.length === 0) {
      document.body.style.overflow = '';
    }

    modal.isOpen = false;

    // Dispatch event
    this.dispatchEvent('modalClose', { modalId, modal: modal.element });
  }

  closeActiveModal() {
    if (this.activeModal) {
      this.closeModal(this.activeModal);
    }
  }

  openPopup(popupId, trigger, options = {}) {
    const popup = this.popups.get(popupId);
    if (!popup || popup.isOpen) return;

    // Close any active popup
    if (this.activePopup) {
      this.closeActivePopup();
    }

    this.activePopup = popupId;
    popup.trigger = trigger;

    // Position popup
    this.positionPopup(popup, trigger, options.position);

    // Show popup
    popup.element.style.display = 'block';
    popup.element.setAttribute('aria-hidden', 'false');
    
    requestAnimationFrame(() => {
      popup.element.classList.add('active');
    });

    popup.isOpen = true;

    // Auto-close for notifications
    if (popup.element.querySelector('.notification') && options.autoClose !== false) {
      setTimeout(() => {
        this.closePopup(popupId);
      }, options.duration || 5000);
    }

    // Dispatch event
    this.dispatchEvent('popupOpen', { popupId, popup: popup.element, trigger });
  }

  closePopup(popupId) {
    const popup = this.popups.get(popupId);
    if (!popup || !popup.isOpen) return;

    popup.element.classList.remove('active');
    popup.element.setAttribute('aria-hidden', 'true');

    setTimeout(() => {
      popup.element.style.display = 'none';
    }, 200);

    if (this.activePopup === popupId) {
      this.activePopup = null;
    }

    popup.isOpen = false;
    popup.trigger = null;

    // Dispatch event
    this.dispatchEvent('popupClose', { popupId, popup: popup.element });
  }

  closeActivePopup() {
    if (this.activePopup) {
      this.closePopup(this.activePopup);
    }
  }

  openContextMenu(event, popupId) {
    const popup = this.popups.get(popupId);
    if (!popup) return;

    // Close any active popup
    if (this.activePopup) {
      this.closeActivePopup();
    }

    this.activePopup = popupId;

    // Position at cursor
    popup.element.style.position = 'fixed';
    popup.element.style.left = `${event.clientX}px`;
    popup.element.style.top = `${event.clientY}px`;

    // Adjust position if near viewport edges
    const rect = popup.content.getBoundingClientRect();
    const viewportWidth = window.innerWidth;
    const viewportHeight = window.innerHeight;

    if (event.clientX + rect.width > viewportWidth) {
      popup.element.style.left = `${event.clientX - rect.width}px`;
    }

    if (event.clientY + rect.height > viewportHeight) {
      popup.element.style.top = `${event.clientY - rect.height}px`;
    }

    // Show popup
    popup.element.style.display = 'block';
    popup.element.setAttribute('aria-hidden', 'false');
    
    requestAnimationFrame(() => {
      popup.element.classList.add('active');
    });

    popup.isOpen = true;
  }

  positionPopup(popup, trigger, position = 'bottom') {
    const triggerRect = trigger.getBoundingClientRect();
    const popupRect = popup.content.getBoundingClientRect();
    const viewportWidth = window.innerWidth;
    const viewportHeight = window.innerHeight;
    const scrollX = window.pageXOffset;
    const scrollY = window.pageYOffset;

    let left, top;

    switch (position) {
      case 'top':
        left = triggerRect.left + (triggerRect.width / 2) - (popupRect.width / 2);
        top = triggerRect.top - popupRect.height - 10;
        break;
      case 'bottom':
        left = triggerRect.left + (triggerRect.width / 2) - (popupRect.width / 2);
        top = triggerRect.bottom + 10;
        break;
      case 'left':
        left = triggerRect.left - popupRect.width - 10;
        top = triggerRect.top + (triggerRect.height / 2) - (popupRect.height / 2);
        break;
      case 'right':
        left = triggerRect.right + 10;
        top = triggerRect.top + (triggerRect.height / 2) - (popupRect.height / 2);
        break;
      default:
        left = triggerRect.left;
        top = triggerRect.bottom + 10;
    }

    // Adjust for viewport boundaries
    if (left < 0) left = 10;
    if (left + popupRect.width > viewportWidth) left = viewportWidth - popupRect.width - 10;
    if (top < 0) top = 10;
    if (top + popupRect.height > viewportHeight) top = viewportHeight - popupRect.height - 10;

    popup.element.style.position = 'fixed';
    popup.element.style.left = `${left}px`;
    popup.element.style.top = `${top}px`;
  }

  handleTabNavigation(event) {
    const modal = this.modals.get(this.activeModal);
    if (!modal) return;

    const focusableElements = modal.element.querySelectorAll(this.focusableElements);
    const firstElement = focusableElements[0];
    const lastElement = focusableElements[focusableElements.length - 1];

    if (event.shiftKey) {
      if (document.activeElement === firstElement) {
        event.preventDefault();
        lastElement.focus();
      }
    } else {
      if (document.activeElement === lastElement) {
        event.preventDefault();
        firstElement.focus();
      }
    }
  }

  focusFirstElement(container) {
    const focusableElements = container.querySelectorAll(this.focusableElements);
    if (focusableElements.length > 0) {
      focusableElements[0].focus();
    }
  }

  dispatchEvent(eventName, detail) {
    const event = new CustomEvent(eventName, {
      detail,
      bubbles: true,
      cancelable: true
    });
    document.dispatchEvent(event);
  }

  // Public API
  show(id, options = {}) {
    if (this.modals.has(id)) {
      this.openModal(id, options);
    } else if (this.popups.has(id)) {
      // For popups, we need a trigger element
      const trigger = options.trigger || document.body;
      this.openPopup(id, trigger, options);
    }
  }

  hide(id) {
    if (this.modals.has(id)) {
      this.closeModal(id);
    } else if (this.popups.has(id)) {
      this.closePopup(id);
    }
  }

  hideAll() {
    // Close all modals
    this.modalStack.forEach(modalId => {
      this.closeModal(modalId);
    });
    
    // Close active popup
    if (this.activePopup) {
      this.closeActivePopup();
    }
  }

  isOpen(id) {
    const modal = this.modals.get(id);
    const popup = this.popups.get(id);
    return (modal && modal.isOpen) || (popup && popup.isOpen);
  }

  showNotification(message, type = 'success', options = {}) {
    const notification = this.createNotification(message, type, options);
    document.body.appendChild(notification);
    
    // Position notification
    this.positionNotification(notification, options.position);
    
    // Show with animation
    requestAnimationFrame(() => {
      notification.classList.add('active');
    });
    
    // Auto-remove
    setTimeout(() => {
      notification.classList.remove('active');
      setTimeout(() => {
        if (notification.parentNode) {
          notification.parentNode.removeChild(notification);
        }
      }, 300);
    }, options.duration || 5000);
    
    return notification;
  }

  createNotification(message, type, options) {
    const notification = document.createElement('div');
    notification.className = `popup-container notification-popup`;
    
    const icons = {
      success: '<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>',
      warning: '<path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/>',
      error: '<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>'
    };
    
    notification.innerHTML = `
      <div class="popup-content notification ${type}">
        <div class="notification-icon">
          <svg viewBox="0 0 24 24" fill="currentColor">
            ${icons[type] || icons.success}
          </svg>
        </div>
        <div class="notification-content">
          <h4>${options.title || type.charAt(0).toUpperCase() + type.slice(1)}</h4>
          <p>${message}</p>
        </div>
        <button class="notification-close" aria-label="Close notification">
          <svg viewBox="0 0 24 24" fill="currentColor">
            <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
          </svg>
        </button>
      </div>
    `;
    
    // Add close functionality
    notification.querySelector('.notification-close').addEventListener('click', () => {
      notification.classList.remove('active');
      setTimeout(() => {
        if (notification.parentNode) {
          notification.parentNode.removeChild(notification);
        }
      }, 300);
    });
    
    return notification;
  }

  positionNotification(notification, position = 'top-right') {
    const positions = {
      'top-left': { top: '20px', left: '20px' },
      'top-right': { top: '20px', right: '20px' },
      'top-center': { top: '20px', left: '50%', transform: 'translateX(-50%)' },
      'bottom-left': { bottom: '20px', left: '20px' },
      'bottom-right': { bottom: '20px', right: '20px' },
      'bottom-center': { bottom: '20px', left: '50%', transform: 'translateX(-50%)' }
    };
    
    Object.assign(notification.style, {
      position: 'fixed',
      zIndex: '1200',
      ...positions[position]
    });
  }

  destroy() {
    // Clean up event listeners and close all modals/popups
    this.hideAll();
    document.body.style.overflow = '';
  }
}

// Initialize the modal and popup system
const modalPopupSystem = new ModalPopupComponents();

// Export for use in other scripts
if (typeof module !== 'undefined' && module.exports) {
  module.exports = ModalPopupComponents;
} else if (typeof window !== 'undefined') {
  window.ModalPopupComponents = ModalPopupComponents;
  window.modalPopupSystem = modalPopupSystem;
}

Usage Examples

Basic Modal

// Open a modal
modalPopupSystem.show('basic-modal');

// Close a modal
modalPopupSystem.hide('basic-modal');

// Check if modal is open
if (modalPopupSystem.isOpen('basic-modal')) {
  console.log('Modal is open');
}

Notification System

// Show success notification
modalPopupSystem.showNotification('Operation completed successfully!', 'success');

// Show warning with custom options
modalPopupSystem.showNotification('Please check your input', 'warning', {
  title: 'Validation Error',
  duration: 3000,
  position: 'top-center'
});

// Show error notification
modalPopupSystem.showNotification('Something went wrong', 'error', {
  duration: 0 // Don't auto-close
});

Event Handling

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

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

// Listen for popup events
document.addEventListener('popupOpen', (e) => {
  console.log('Popup opened:', e.detail.popupId);
});

Custom Modal Creation

// Create a custom modal programmatically
function createCustomModal(id, title, content) {
  const modal = document.createElement('div');
  modal.className = 'modal-overlay';
  modal.id = id;
  modal.innerHTML = `
    <div class="modal-container">
      <div class="modal-header">
        <h2 class="modal-title">${title}</h2>
        <button class="modal-close" aria-label="Close modal">
          <svg viewBox="0 0 24 24" fill="currentColor">
            <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
          </svg>
        </button>
      </div>
      <div class="modal-body">
        ${content}
      </div>
      <div class="modal-footer">
        <button class="btn btn-secondary modal-cancel">Cancel</button>
        <button class="btn btn-primary">Confirm</button>
      </div>
    </div>
  `;
  
  document.body.appendChild(modal);
  
  // Register with the system
  modalPopupSystem.modals.set(id, {
    element: modal,
    container: modal.querySelector('.modal-container'),
    isOpen: false,
    previousFocus: null
  });
  
  return modal;
}

API Reference

Methods

MethodParametersDescription
show(id, options)id: string, options: objectOpens a modal or popup
hide(id)id: stringCloses a specific modal or popup
hideAll()-Closes all open modals and popups
isOpen(id)id: stringReturns boolean indicating if modal/popup is open
showNotification(message, type, options)message: string, type: string, options: objectShows a notification
destroy()-Cleans up the system and closes all modals

Events

EventDetailDescription
modalOpen{ modalId, modal }Fired when a modal opens
modalClose{ modalId, modal }Fired when a modal closes
popupOpen{ popupId, popup, trigger }Fired when a popup opens
popupClose{ popupId, popup }Fired when a popup closes

Options

{
  animation: 'fade-in' | 'slide-up' | 'scale-in', // Animation type
  backdrop: true | false,                          // Enable backdrop
  keyboard: true | false,                          // Enable keyboard navigation
  focus: true | false                              // Auto-focus first element
}
{
  position: 'top' | 'bottom' | 'left' | 'right',  // Popup position
  trigger: HTMLElement,                            // Trigger element
  autoClose: true | false,                         // Auto-close behavior
  duration: number                                 // Auto-close duration (ms)
}

Notification Options

{
  title: string,                                   // Notification title
  duration: number,                                // Display duration (ms)
  position: 'top-left' | 'top-right' | 'top-center' | 
           'bottom-left' | 'bottom-right' | 'bottom-center'
}

Customization

CSS Variables

:root {
  --modal-backdrop-color: rgba(0, 0, 0, 0.5);
  --modal-border-radius: 16px;
  --modal-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
  --modal-animation-duration: 0.3s;
  --popup-border-radius: 8px;
  --popup-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
  --notification-success-color: #22c55e;
  --notification-warning-color: #f59e0b;
  --notification-error-color: #ef4444;
}

Animation Customization

/* Custom slide animation */
@keyframes slideInFromRight {
  from {
    opacity: 0;
    transform: translateX(100%);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

.modal-container.slide-right {
  animation: slideInFromRight 0.3s ease;
}

Accessibility

  • ARIA Support: Proper ARIA attributes for screen readers
  • Keyboard Navigation: Full keyboard support with Tab and Escape keys
  • Focus Management: Automatic focus trapping and restoration
  • Screen Reader: Descriptive labels and announcements
  • High Contrast: Support for high contrast mode
  • Reduced Motion: Respects prefers-reduced-motion setting

Browser Support

  • Modern Browsers: Chrome 60+, Firefox 55+, Safari 12+, Edge 79+
  • Mobile: iOS Safari 12+, Chrome Mobile 60+
  • Features: CSS Grid, Flexbox, ES6 Classes, Custom Events
  • Fallbacks: Graceful degradation for older browsers

Performance Considerations

  • Lazy Loading: Modals are initialized only when needed
  • Event Delegation: Efficient event handling with delegation
  • Memory Management: Proper cleanup to prevent memory leaks
  • Animation Optimization: CSS transforms for smooth animations
  • Debouncing: Debounced resize and scroll events

Integration Examples

React Integration

import { useEffect, useRef } from 'react';

function ModalComponent({ isOpen, onClose, children }) {
  const modalRef = useRef();
  
  useEffect(() => {
    if (isOpen) {
      modalPopupSystem.show('react-modal');
    } else {
      modalPopupSystem.hide('react-modal');
    }
  }, [isOpen]);
  
  useEffect(() => {
    const handleModalClose = (e) => {
      if (e.detail.modalId === 'react-modal') {
        onClose();
      }
    };
    
    document.addEventListener('modalClose', handleModalClose);
    return () => document.removeEventListener('modalClose', handleModalClose);
  }, [onClose]);
  
  return (
    <div className="modal-overlay" id="react-modal" ref={modalRef}>
      <div className="modal-container">
        {children}
      </div>
    </div>
  );
}

Vue Integration

<template>
  <div class="modal-overlay" :id="modalId" v-show="isOpen">
    <div class="modal-container">
      <slot></slot>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    modalId: String,
    isOpen: Boolean
  },
  watch: {
    isOpen(newVal) {
      if (newVal) {
        modalPopupSystem.show(this.modalId);
      } else {
        modalPopupSystem.hide(this.modalId);
      }
    }
  },
  mounted() {
    document.addEventListener('modalClose', this.handleModalClose);
  },
  beforeDestroy() {
    document.removeEventListener('modalClose', this.handleModalClose);
  },
  methods: {
    handleModalClose(e) {
      if (e.detail.modalId === this.modalId) {
        this.$emit('close');
      }
    }
  }
};
</script>

HTML

4

lines

CSS

7

lines


                <div class="modal-container">
  <h2>Modal & Popup Components</h2>
  <p>Modern modal dialogs and popup components</p>
</div>

              
4lines
124characters
HTMLLanguage

Related Code Snippets

Explore template packs

Need larger building blocks? Browse responsive landing pages and component bundles.

Open HTML Template Library →