Floating layers and status feedback

Modal Dialog

Accessible confirmation dialog implemented with the native dialog element.

HTMLCSSJavaScriptDialog
With JavaScript enabled, you can edit, run, and save this example. The fixed source is as follows:
<!doctype html>
<html lang="en" dir="ltr">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Modal Dialog</title>
  <style>
    * { box-sizing: border-box; }
    body {
      min-height: 100vh;
      margin: 0;
      display: grid;
      place-items: center;
      padding: 24px;
      color: #2a241f;
      font-family: "Avenir Next", "Microsoft YaHei", sans-serif;
      background: #efe9df;
      background-image: radial-gradient(#c7baaa 1px, transparent 1px);
      background-size: 22px 22px;
    }
    .card {
      width: min(680px, 100%);
      padding: clamp(28px, 6vw, 58px);
      border: 1px solid #d8cbbb;
      border-radius: 4px 38px 4px 38px;
      background: #fffdf8;
      box-shadow: 16px 18px 0 #d8cbbb;
    }
    .number { display: block; color: #9a4c33; font: 700 13px/1 Georgia, serif; letter-spacing: .18em; }
    h1 { max-width: 520px; margin: 16px 0 12px; font: 700 clamp(38px, 8vw, 68px)/.98 Georgia, "STSong", serif; }
    .card p { max-width: 520px; margin: 0 0 30px; color: #6f6359; line-height: 1.75; }
    button { border: 0; font: inherit; font-weight: 750; cursor: pointer; }
    .open-button { padding: 14px 22px; border-radius: 999px; color: #fffdf8; background: #2a241f; box-shadow: 0 8px 0 #9a4c33; transition: transform .15s ease, box-shadow .15s ease; }
    .open-button:active { transform: translateY(5px); box-shadow: 0 3px 0 #9a4c33; }
    button:focus-visible { outline: 3px solid #d27853; outline-offset: 3px; }
    dialog {
      width: min(470px, calc(100% - 32px));
      padding: 0;
      border: 0;
      border-radius: 28px;
      color: #2a241f;
      background: #fffdf8;
      box-shadow: 0 24px 90px rgba(33, 24, 19, .35);
    }
    dialog::backdrop { background: rgba(35, 27, 21, .68); backdrop-filter: blur(5px); }
    dialog[open] { animation: rise .28s ease both; }
    .dialog-body { padding: 34px; }
    .icon { display: grid; width: 54px; height: 54px; place-items: center; border-radius: 50%; color: #9a4c33; font-size: 25px; background: #f6dfd5; }
    h2 { margin: 20px 0 8px; font: 700 30px/1.15 Georgia, "STSong", serif; }
    .dialog-body p { margin: 0; color: #706258; line-height: 1.65; }
    .actions { display: flex; justify-content: flex-end; gap: 12px; padding: 18px 34px 28px; }
    .actions button { padding: 12px 18px; border-radius: 999px; }
    .cancel { color: #4f443c; background: #eee8df; }
    .confirm { color: white; background: #9a4c33; }
    .status { min-height: 24px; margin-top: 24px !important; color: #9a4c33 !important; font-weight: 700; }
    @keyframes rise { from { opacity: 0; transform: translateY(24px) scale(.96); } }
    @media (prefers-reduced-motion: reduce) { dialog[open] { animation: none; } }
  </style>
</head>
<body>
  <main class="card">
    <span class="number">COMPONENT 02</span>
    <h1>One clear confirmation</h1>
    <p>The native dialog automatically manages focus and background interaction. Click the button to view the confirmation flow, or press Esc to close it.</p>
    <button id="openDialog" class="open-button" type="button">Open dialog</button>
    <p id="status" class="status" role="status" aria-live="polite"></p>
  </main>

  <dialog id="confirmDialog" aria-labelledby="dialogTitle" aria-describedby="dialogDescription">
    <div class="dialog-body">
      <div class="icon" aria-hidden="true">✓</div>
      <h2 id="dialogTitle">Confirm saving this design?</h2>
      <p id="dialogDescription">After saving, the new version will replace the current draft. You can still recover the old version from the history.</p>
    </div>
    <div class="actions">
      <button class="cancel" type="button" data-dialog-result="cancel">Don’t Save Yet</button>
      <button class="confirm" type="button" data-dialog-result="confirm">Confirm Save</button>
    </div>
  </dialog>

  <script>
    const dialog = document.querySelector('#confirmDialog');
    const status = document.querySelector('#status');
    document.querySelector('#openDialog').addEventListener('click', () => {
      dialog.returnValue = '';
      dialog.showModal();
    });
    document.querySelectorAll('[data-dialog-result]').forEach(button => {
      button.addEventListener('click', () => dialog.close(button.dataset.dialogResult));
    });
    dialog.addEventListener('close', () => {
      status.textContent = dialog.returnValue === 'confirm' ? 'Design safely saved.' : 'The operation was canceled, and no changes were made.';
    });
  </script>
</body>
</html>
Page feedbackFound an issue or have suggestions for improvement?
Feedback features need to be used in the online version

Please enable JavaScript in the online version before submitting; local features of the current tool or example are not affected.

Go to the online version for feedback