確認ダイアログ

削除など破壊的な操作の前に表示する確認ダイアログ。オーバーレイの外側クリックやEscキーでも閉じられ、誤操作を防ぎます。

#css#js#alert#modal

ライブデモ

コード

HTML
<!-- 確認ダイアログ: 破壊的操作(削除)の前にオーバーレイで確認する。ui-modalとは別実装で、
     role="alertdialog"を用いる。Esc/オーバーレイクリック/キャンセルのいずれでも閉じる。 -->
<div class="cd-stage">
  <div class="cd-card">
    <h2 class="cd-card__title">プロジェクト「2026年度キャンペーン」</h2>
    <p class="cd-card__desc">最終更新: 3日前</p>
    <button type="button" class="cd-trigger" id="cdOpen">プロジェクトを削除</button>
  </div>
</div>

<div class="cd-overlay" id="cdOverlay" hidden>
  <div
    class="cd-dialog"
    id="cdDialog"
    role="alertdialog"
    aria-modal="true"
    aria-labelledby="cdTitle"
    aria-describedby="cdDesc"
    tabindex="-1"
  >
    <span class="cd-dialog__icon" aria-hidden="true">!</span>
    <h2 class="cd-dialog__title" id="cdTitle">本当に削除しますか?</h2>
    <p class="cd-dialog__desc" id="cdDesc">
      「2026年度キャンペーン」を削除すると元に戻せません。関連するファイルもすべて完全に削除されます。
    </p>
    <div class="cd-dialog__actions">
      <button type="button" class="cd-btn cd-btn--ghost" id="cdCancel">キャンセル</button>
      <button type="button" class="cd-btn cd-btn--danger" id="cdConfirm">削除する</button>
    </div>
  </div>
</div>
CSS
/* 確認ダイアログ: エラー色(赤)の破壊的操作確認。ui-modal(ダーク×indigo)とは異なる
   ライト基調のカードで統一し、独自のクラス名/構造にしている */
* { box-sizing: border-box; }
:root{
  --cd-danger: #dc2626;
  --cd-danger-dark: #b91c1c;
  --cd-ink: #1f2430;
  --cd-muted: #6b7280;
}
body{
  margin: 0;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: "Segoe UI", system-ui, -apple-system, "Hiragino Sans", sans-serif;
  background:
    radial-gradient(900px 460px at 50% -10%, #eef1fb, transparent 60%),
    #f5f6fa;
  color: var(--cd-ink);
}

.cd-stage{ width: min(360px, 90vw); }
.cd-card{
  background: #fff;
  border: 1px solid #e6e8f0;
  border-radius: 16px;
  padding: 24px;
  text-align: center;
  box-shadow: 0 20px 40px -24px rgba(20, 24, 40, .18);
}
.cd-card__title{ margin: 0 0 4px; font-size: 1.02rem; }
.cd-card__desc{ margin: 0 0 18px; font-size: .82rem; color: var(--cd-muted); }
.cd-trigger{
  font: inherit;
  font-weight: 600;
  font-size: .88rem;
  padding: 11px 22px;
  border-radius: 10px;
  border: 1px solid rgba(220, 38, 38, .35);
  background: #fff5f5;
  color: var(--cd-danger-dark);
  cursor: pointer;
  transition: background .2s ease;
  /* 待機中も控えめに危険性を示す呼吸アニメ */
  animation: cdHint 3.4s ease-in-out infinite;
}
.cd-trigger:hover{ background: #ffe8e8; }
.cd-trigger:focus-visible{ outline: 2px solid var(--cd-danger); outline-offset: 2px; }
@keyframes cdHint{
  0%, 100%{ box-shadow: 0 0 0 0 rgba(220, 38, 38, .22); }
  50%{ box-shadow: 0 0 0 6px rgba(220, 38, 38, 0); }
}

.cd-overlay{
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(17, 20, 30, .5);
  backdrop-filter: blur(3px);
  padding: 20px;
  z-index: 80;
  animation: cdFadeIn .2s ease;
}
.cd-overlay[hidden]{ display: none; }
@keyframes cdFadeIn{ from{ opacity: 0; } to{ opacity: 1; } }

.cd-dialog{
  width: min(380px, 100%);
  background: #fff;
  border-radius: 18px;
  padding: 30px 26px 26px;
  text-align: center;
  box-shadow: 0 40px 80px -24px rgba(0, 0, 0, .35);
  animation: cdPop .28s cubic-bezier(.2,.9,.3,1.2);
}
.cd-dialog:focus-visible,
.cd-dialog:focus{ outline: none; }
@keyframes cdPop{
  from{ opacity: 0; transform: translateY(10px) scale(.96); }
  to{ opacity: 1; transform: none; }
}

.cd-dialog__icon{
  display: inline-grid;
  place-items: center;
  width: 52px; height: 52px;
  margin: 0 auto 14px;
  border-radius: 50%;
  background: #fde8e8;
  color: var(--cd-danger);
  font-size: 1.5rem;
  font-weight: 800;
  font-style: normal;
}
.cd-dialog__title{ margin: 0 0 8px; font-size: 1.12rem; }
.cd-dialog__desc{ margin: 0 0 22px; font-size: .88rem; line-height: 1.65; color: var(--cd-muted); }
.cd-dialog__actions{ display: flex; gap: 10px; }
.cd-btn{
  flex: 1;
  font: inherit;
  font-weight: 600;
  font-size: .88rem;
  padding: 11px;
  border-radius: 10px;
  border: none;
  cursor: pointer;
  transition: background .2s ease, transform .15s ease;
}
.cd-btn:active{ transform: translateY(1px); }
.cd-btn:focus-visible{ outline: 2px solid var(--cd-danger); outline-offset: 2px; }
.cd-btn--ghost{ background: #f0f1f6; color: var(--cd-ink); }
.cd-btn--ghost:hover{ background: #e5e7f0; }
.cd-btn--danger{ background: var(--cd-danger); color: #fff; }
.cd-btn--danger:hover{ background: var(--cd-danger-dark); }

@media (prefers-reduced-motion: reduce){
  .cd-trigger{ animation: none; }
  .cd-overlay, .cd-dialog{ animation: none; }
}
JavaScript
// 確認ダイアログ: オーバーレイ+キャンセル/削除。Escで閉じ、閉じたら元のボタンへフォーカスを戻す
(() => {
  const openBtn = document.getElementById('cdOpen');
  const overlay = document.getElementById('cdOverlay');
  const dialog = document.getElementById('cdDialog');
  const cancelBtn = document.getElementById('cdCancel');
  const confirmBtn = document.getElementById('cdConfirm');
  if (!openBtn || !overlay || !dialog) return; // null安全

  let lastFocused = null;

  const onKeydown = (e) => {
    if (e.key === 'Escape') {
      e.preventDefault();
      close();
    }
  };

  const open = () => {
    lastFocused = document.activeElement;
    overlay.hidden = false;
    dialog.focus();
    document.addEventListener('keydown', onKeydown);
  };

  function close() {
    overlay.hidden = true;
    document.removeEventListener('keydown', onKeydown);
    if (lastFocused && typeof lastFocused.focus === 'function') lastFocused.focus();
  }

  openBtn.addEventListener('click', open);
  if (cancelBtn) cancelBtn.addEventListener('click', close);
  if (confirmBtn) {
    confirmBtn.addEventListener('click', () => {
      // デモのため実際の削除は行わず、ダイアログを閉じるのみ
      close();
    });
  }

  overlay.addEventListener('click', (e) => {
    if (e.target === overlay) close();
  });
})();

🤖 AIエージェント用プロンプト

このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「確認ダイアログ」の効果を追加してください。

# 追加してほしい効果
確認ダイアログ(アラート & 空状態)
削除など破壊的な操作の前に表示する確認ダイアログ。オーバーレイの外側クリックやEscキーでも閉じられ、誤操作を防ぎます。

# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】

# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- 確認ダイアログ: 破壊的操作(削除)の前にオーバーレイで確認する。ui-modalとは別実装で、
     role="alertdialog"を用いる。Esc/オーバーレイクリック/キャンセルのいずれでも閉じる。 -->
<div class="cd-stage">
  <div class="cd-card">
    <h2 class="cd-card__title">プロジェクト「2026年度キャンペーン」</h2>
    <p class="cd-card__desc">最終更新: 3日前</p>
    <button type="button" class="cd-trigger" id="cdOpen">プロジェクトを削除</button>
  </div>
</div>

<div class="cd-overlay" id="cdOverlay" hidden>
  <div
    class="cd-dialog"
    id="cdDialog"
    role="alertdialog"
    aria-modal="true"
    aria-labelledby="cdTitle"
    aria-describedby="cdDesc"
    tabindex="-1"
  >
    <span class="cd-dialog__icon" aria-hidden="true">!</span>
    <h2 class="cd-dialog__title" id="cdTitle">本当に削除しますか?</h2>
    <p class="cd-dialog__desc" id="cdDesc">
      「2026年度キャンペーン」を削除すると元に戻せません。関連するファイルもすべて完全に削除されます。
    </p>
    <div class="cd-dialog__actions">
      <button type="button" class="cd-btn cd-btn--ghost" id="cdCancel">キャンセル</button>
      <button type="button" class="cd-btn cd-btn--danger" id="cdConfirm">削除する</button>
    </div>
  </div>
</div>

【CSS】
/* 確認ダイアログ: エラー色(赤)の破壊的操作確認。ui-modal(ダーク×indigo)とは異なる
   ライト基調のカードで統一し、独自のクラス名/構造にしている */
* { box-sizing: border-box; }
:root{
  --cd-danger: #dc2626;
  --cd-danger-dark: #b91c1c;
  --cd-ink: #1f2430;
  --cd-muted: #6b7280;
}
body{
  margin: 0;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: "Segoe UI", system-ui, -apple-system, "Hiragino Sans", sans-serif;
  background:
    radial-gradient(900px 460px at 50% -10%, #eef1fb, transparent 60%),
    #f5f6fa;
  color: var(--cd-ink);
}

.cd-stage{ width: min(360px, 90vw); }
.cd-card{
  background: #fff;
  border: 1px solid #e6e8f0;
  border-radius: 16px;
  padding: 24px;
  text-align: center;
  box-shadow: 0 20px 40px -24px rgba(20, 24, 40, .18);
}
.cd-card__title{ margin: 0 0 4px; font-size: 1.02rem; }
.cd-card__desc{ margin: 0 0 18px; font-size: .82rem; color: var(--cd-muted); }
.cd-trigger{
  font: inherit;
  font-weight: 600;
  font-size: .88rem;
  padding: 11px 22px;
  border-radius: 10px;
  border: 1px solid rgba(220, 38, 38, .35);
  background: #fff5f5;
  color: var(--cd-danger-dark);
  cursor: pointer;
  transition: background .2s ease;
  /* 待機中も控えめに危険性を示す呼吸アニメ */
  animation: cdHint 3.4s ease-in-out infinite;
}
.cd-trigger:hover{ background: #ffe8e8; }
.cd-trigger:focus-visible{ outline: 2px solid var(--cd-danger); outline-offset: 2px; }
@keyframes cdHint{
  0%, 100%{ box-shadow: 0 0 0 0 rgba(220, 38, 38, .22); }
  50%{ box-shadow: 0 0 0 6px rgba(220, 38, 38, 0); }
}

.cd-overlay{
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(17, 20, 30, .5);
  backdrop-filter: blur(3px);
  padding: 20px;
  z-index: 80;
  animation: cdFadeIn .2s ease;
}
.cd-overlay[hidden]{ display: none; }
@keyframes cdFadeIn{ from{ opacity: 0; } to{ opacity: 1; } }

.cd-dialog{
  width: min(380px, 100%);
  background: #fff;
  border-radius: 18px;
  padding: 30px 26px 26px;
  text-align: center;
  box-shadow: 0 40px 80px -24px rgba(0, 0, 0, .35);
  animation: cdPop .28s cubic-bezier(.2,.9,.3,1.2);
}
.cd-dialog:focus-visible,
.cd-dialog:focus{ outline: none; }
@keyframes cdPop{
  from{ opacity: 0; transform: translateY(10px) scale(.96); }
  to{ opacity: 1; transform: none; }
}

.cd-dialog__icon{
  display: inline-grid;
  place-items: center;
  width: 52px; height: 52px;
  margin: 0 auto 14px;
  border-radius: 50%;
  background: #fde8e8;
  color: var(--cd-danger);
  font-size: 1.5rem;
  font-weight: 800;
  font-style: normal;
}
.cd-dialog__title{ margin: 0 0 8px; font-size: 1.12rem; }
.cd-dialog__desc{ margin: 0 0 22px; font-size: .88rem; line-height: 1.65; color: var(--cd-muted); }
.cd-dialog__actions{ display: flex; gap: 10px; }
.cd-btn{
  flex: 1;
  font: inherit;
  font-weight: 600;
  font-size: .88rem;
  padding: 11px;
  border-radius: 10px;
  border: none;
  cursor: pointer;
  transition: background .2s ease, transform .15s ease;
}
.cd-btn:active{ transform: translateY(1px); }
.cd-btn:focus-visible{ outline: 2px solid var(--cd-danger); outline-offset: 2px; }
.cd-btn--ghost{ background: #f0f1f6; color: var(--cd-ink); }
.cd-btn--ghost:hover{ background: #e5e7f0; }
.cd-btn--danger{ background: var(--cd-danger); color: #fff; }
.cd-btn--danger:hover{ background: var(--cd-danger-dark); }

@media (prefers-reduced-motion: reduce){
  .cd-trigger{ animation: none; }
  .cd-overlay, .cd-dialog{ animation: none; }
}

【JavaScript】
// 確認ダイアログ: オーバーレイ+キャンセル/削除。Escで閉じ、閉じたら元のボタンへフォーカスを戻す
(() => {
  const openBtn = document.getElementById('cdOpen');
  const overlay = document.getElementById('cdOverlay');
  const dialog = document.getElementById('cdDialog');
  const cancelBtn = document.getElementById('cdCancel');
  const confirmBtn = document.getElementById('cdConfirm');
  if (!openBtn || !overlay || !dialog) return; // null安全

  let lastFocused = null;

  const onKeydown = (e) => {
    if (e.key === 'Escape') {
      e.preventDefault();
      close();
    }
  };

  const open = () => {
    lastFocused = document.activeElement;
    overlay.hidden = false;
    dialog.focus();
    document.addEventListener('keydown', onKeydown);
  };

  function close() {
    overlay.hidden = true;
    document.removeEventListener('keydown', onKeydown);
    if (lastFocused && typeof lastFocused.focus === 'function') lastFocused.focus();
  }

  openBtn.addEventListener('click', open);
  if (cancelBtn) cancelBtn.addEventListener('click', close);
  if (confirmBtn) {
    confirmBtn.addEventListener('click', () => {
      // デモのため実際の削除は行わず、ダイアログを閉じるのみ
      close();
    });
  }

  overlay.addEventListener('click', (e) => {
    if (e.target === overlay) close();
  });
})();

# 外部ライブラリ
なし(追加ライブラリ不要)

# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。