html|css|javascript 85 views

Interactive Modal with CSS Transitions

Create a stylish modal dialog that smoothly appears and disappears using CSS transitions and JavaScript.

By TWC Team • Feb 06, 2026

Code

<!--
  Interactive Modal with CSS Transitions (Accessible + reusable)
  Usage: call window.modal.open() / window.modal.close() or click the "Open modal" button.
-->
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>Interactive Modal with CSS Transitions</title>
  <style>
    :root { --dur: 240ms; --ease: cubic-bezier(.2,.8,.2,1); }

    /* Overlay uses opacity/visibility for smooth transitions (avoid display:none flicker). */
    .modal {
      position: fixed; inset: 0; z-index: 1000;
      display: grid; place-items: center;
      background: rgba(0,0,0,.55);
      opacity: 0; visibility: hidden; pointer-events: none;
      transition: opacity var(--dur) var(--ease), visibility 0s linear var(--dur);
    }
    .modal.is-open {
      opacity: 1; visibility: visible; pointer-events: auto;
      transition: opacity var(--dur) var(--ease);
    }
    .modal-content {
      width: min(560px, 92vw);
      background: #fff; border: 1px solid rgba(0,0,0,.12);
      border-radius: 14px; padding: 18px 18px 16px;
      box-shadow: 0 18px 60px rgba(0,0,0,.25);
      transform: translateY(10px) scale(.98);
      transition: transform var(--dur) var(--ease);
    }
    .modal.is-open .modal-content { transform: translateY(0) scale(1); }

    .close {
      appearance: none; border: 0; background: transparent; cursor: pointer;
      float: right; line-height: 1; padding: 6px;
      font-size: 28px; font-weight: 700; color: #8a8a8a;
    }
    .close:hover { color: #333; }

    /* Respect reduced motion preferences. */
    @media (prefers-reduced-motion: reduce) {
      .modal, .modal-content { transition: none !important; }
    }
  </style>
</head>
<body>
  <button type="button" id="openModalBtn">Open modal</button>

  <!-- Required structure (kept), with accessibility attributes added. -->
  <div id="myModal" class="modal" role="dialog" aria-modal="true" aria-labelledby="modalTitle" aria-hidden="true">
    <div class="modal-content" role="document" tabindex="-1">
      <button class="close" type="button" aria-label="Close modal">&times;</button>
      <h2 id="modalTitle">Modal Header</h2>
      <p>This is a simple modal dialog!</p>
    </div>
  </div>

  <script>
    /* Lightweight modal controller: handles open/close, ESC, outside click, and focus management. */
    (() => {
      const modal = document.getElementById('myModal');
      const content = modal.querySelector('.modal-content');
      const closeBtn = modal.querySelector('.close');
      const openBtn = document.getElementById('openModalBtn');
      if (!modal || !content || !closeBtn) return;

      let lastActiveEl = null;

      const setOpen = (open) => {
        modal.classList.toggle('is-open', open);
        modal.setAttribute('aria-hidden', String(!open));

        if (open) {
          lastActiveEl = document.activeElement;
          // Next frame ensures transitions apply consistently.
          requestAnimationFrame(() => content.focus({ preventScroll: true }));
        } else if (lastActiveEl && typeof lastActiveEl.focus === 'function') {
          lastActiveEl.focus({ preventScroll: true });
          lastActiveEl = null;
        }
      };

      const open = () => setOpen(true);
      const close = () => setOpen(false);

      // Close on ESC (common expected behavior).
      document.addEventListener('keydown', (e) => {
        if (e.key === 'Escape' && modal.classList.contains('is-open')) close();
      });

      // Close when clicking the dimmed overlay (but not when clicking the dialog).
      modal.addEventListener('click', (e) => { if (e.target === modal) close(); });

      openBtn?.addEventListener('click', open);
      closeBtn.addEventListener('click', close);

      // Example usage:
      // open(); // show on page load
      window.modal = { open, close };
    })();
  </script>
</body>
</html>
Back to Snippets