Component Library

← Back to gallery

Destructive confirm button

Category: Buttons · Tags: delete, confirm, destructive, inline, danger

Q3 launch plan

How to use it

Use for a destructive action on one item in a list or settings page where a full modal would be heavy, such as deleting a single project. Name the thing being deleted in the question and on the confirm button, and offer undo or a trash folder where you can. For deletes that cascade widely or need typed confirmation, use a modal dialog instead. Replace the demo handler with your real delete request and change the ids that link the group to its question.

Accessibility

Everything is native <button> elements. Pressing "Delete project" hides it and reveals a role="group" whose question ("Delete Q3 launch plan? This can't be undone.") labels the "Confirm delete" and "Cancel" buttons, and focus moves to Cancel so a second accidental Enter does nothing harmful. Cancel or Escape restores the Delete button and puts focus back on it; confirming replaces the controls with a message that receives focus (tabindex="-1") and is also a role="status" announcement. Every button shows a 2px focus outline. Text is #b91c1c on #ffffff (6.47:1) for Delete, #ffffff on #b91c1c (6.47:1) and on the #991b1b hover (8.31:1) for Confirm, and #0f172a on #ffffff (17.85:1) and on the #e2e8f0 hover (14.48:1) elsewhere.

Source (copy-ready)

<div class="dcl-delconfirm">
  <p class="dcl-delconfirm__name">Q3 launch plan</p>
  <div class="dcl-delconfirm__controls">
    <button type="button" class="dcl-delconfirm__delete">Delete project</button>
    <div class="dcl-delconfirm__ask" role="group" aria-labelledby="dcl-delconfirm-question" hidden>
      <p class="dcl-delconfirm__question" id="dcl-delconfirm-question">Delete Q3 launch plan? This can't be undone.</p>
      <button type="button" class="dcl-delconfirm__confirm">Confirm delete</button>
      <button type="button" class="dcl-delconfirm__cancel">Cancel</button>
    </div>
  </div>
  <p class="dcl-delconfirm__done" role="status" tabindex="-1"></p>
</div>
<style>
.dcl-delconfirm { max-width: 26rem; padding: 1rem; border: 1px solid #cbd5e1; border-radius: 0.75rem; font: inherit; color: #0f172a; background-color: #ffffff; }
.dcl-delconfirm__name { margin: 0 0 0.75rem; font-weight: 600; color: #0f172a; background-color: #ffffff; }
.dcl-delconfirm__controls { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.dcl-delconfirm__controls[hidden] { display: none; }
.dcl-delconfirm__ask { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
.dcl-delconfirm__ask[hidden] { display: none; }
.dcl-delconfirm__question { flex: 1 1 100%; margin: 0; color: #0f172a; background-color: #ffffff; }
.dcl-delconfirm__delete, .dcl-delconfirm__confirm, .dcl-delconfirm__cancel { min-height: 2.75rem; padding: 0 1rem; border-radius: 0.5rem; cursor: pointer; font: inherit; font-weight: 600; }
.dcl-delconfirm__delete[hidden] { display: none; }
.dcl-delconfirm__delete { border: 1px solid #b91c1c; color: #b91c1c; background-color: #ffffff; }
.dcl-delconfirm__delete:hover { color: #b91c1c; background-color: #fef2f2; }
.dcl-delconfirm__confirm { border: 1px solid #b91c1c; color: #ffffff; background-color: #b91c1c; }
.dcl-delconfirm__confirm:hover { color: #ffffff; background-color: #991b1b; }
.dcl-delconfirm__cancel { border: 1px solid #64748b; color: #0f172a; background-color: #ffffff; }
.dcl-delconfirm__cancel:hover { color: #0f172a; background-color: #e2e8f0; }
.dcl-delconfirm__delete:focus-visible, .dcl-delconfirm__confirm:focus-visible, .dcl-delconfirm__cancel:focus-visible { outline: 2px solid #0f172a; outline-offset: 2px; }
.dcl-delconfirm__done { margin: 0; color: #0f172a; background-color: #ffffff; }
.dcl-delconfirm__done:focus-visible { outline: 2px solid #1d4ed8; outline-offset: 2px; }
</style>
<script>
(function () {
  document.querySelectorAll(".dcl-delconfirm").forEach(function (root) {
    if (root.dataset.dclBound) { return; }
    root.dataset.dclBound = "true";
    var controls = root.querySelector(".dcl-delconfirm__controls");
    var del = root.querySelector(".dcl-delconfirm__delete");
    var ask = root.querySelector(".dcl-delconfirm__ask");
    var confirm = root.querySelector(".dcl-delconfirm__confirm");
    var cancel = root.querySelector(".dcl-delconfirm__cancel");
    var done = root.querySelector(".dcl-delconfirm__done");
    if (!del || !ask) { return; }
    function reset() {
      ask.hidden = true;
      del.hidden = false;
      del.focus();
    }
    del.addEventListener("click", function () {
      del.hidden = true;
      ask.hidden = false;
      if (cancel) { cancel.focus(); }
    });
    if (cancel) { cancel.addEventListener("click", reset); }
    ask.addEventListener("keydown", function (event) {
      if (event.key === "Escape") { event.preventDefault(); reset(); }
    });
    if (confirm) {
      confirm.addEventListener("click", function () {
        if (controls) { controls.hidden = true; }
        if (done) {
          done.textContent = "Project deleted.";
          done.focus();
        }
      });
    }
  });
})();
</script>

Was this useful? Send feedback.