表示例
請求書_9月.pdf
このファイルを削除しますか?
共有中の相手も開けなくなります。
アイコン: Font Awesome 7 Free(fa-solid / fa-regular)
別名・英語表記
- ポップコンファーム
- インライン確認
- 確認ポップオーバー
- 削除の確認ポップアップ
- Popconfirm
実装例
<div class="x-popconfirm">
<div class="x-popconfirm__row">
<span class="x-popconfirm__file"><i class="fa-regular fa-file-lines" aria-hidden="true"></i>請求書_9月.pdf</span>
<button class="x-popconfirm__b" type="button" aria-haspopup="dialog" aria-expanded="true" aria-controls="x-popconfirm-p">
<i class="fa-solid fa-trash-can" aria-hidden="true"></i>削除
</button>
</div>
<!-- 見本なので開いた状態で流れの中に置いている。実際は小窓に popover 属性、「削除」に popovertarget="x-popconfirm-p" を付けて開閉し
(hidden 属性では閉じない)、aria-expanded は false から始めて toggle イベントで書き換える。開いたらフォーカスを「キャンセル」へ移す。
Esc か hidePopover() で閉じると、ブラウザはフォーカスを開く前の要素へ戻す。「削除」の click で先に this.focus() を呼び、
戻る先を「削除」にする(Safari と macOS の Firefox は、押したボタンにフォーカスを移さない) -->
<div class="x-popconfirm__p" id="x-popconfirm-p" role="dialog" aria-labelledby="x-popconfirm-t" aria-describedby="x-popconfirm-d">
<p class="x-popconfirm__t" id="x-popconfirm-t">
<i class="fa-solid fa-triangle-exclamation" aria-hidden="true"></i>このファイルを削除しますか?
</p>
<p class="x-popconfirm__d" id="x-popconfirm-d">共有中の相手も開けなくなります。</p>
<div class="x-popconfirm__act">
<button class="x-popconfirm__cancel" type="button">キャンセル</button>
<button class="x-popconfirm__ok" type="button">削除する</button>
</div>
</div>
</div>
/* 貼った先のリセットに頼らないよう、この部品の中だけ border-box にする */
.x-popconfirm,
.x-popconfirm *:not([class*="fa-"]),
.x-popconfirm *:not([class*="fa-"])::before,
.x-popconfirm *:not([class*="fa-"])::after {
box-sizing: border-box;
/* 貼った先の既定の書体に引きずられないようにする。書体が変わると
文字の幅と高さが変わり、枠のサイズまでずれる */
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Hiragino Sans", "Noto Sans JP", "Yu Gothic UI", Meiryo, sans-serif;
}
.x-popconfirm {
position: relative;
width: 300px;
}
.x-popconfirm__row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
padding: 6px 6px 6px 12px;
border: 1px solid #e3e6ea;
border-radius: 8px;
background: #fff;
}
.x-popconfirm__file {
display: inline-flex;
align-items: center;
gap: 6px;
color: #1b2028;
font-size: 13px;
}
.x-popconfirm__file .fa-regular {
color: #5b6472;
}
.x-popconfirm__b {
display: inline-flex;
align-items: center;
gap: 5px;
min-height: 32px;
padding: 0 10px;
border: 1px solid #cfd4dc;
border-radius: 7px;
background: #fff;
color: #b91c1c;
font: inherit;
font-size: 12px;
cursor: pointer;
}
.x-popconfirm__b[aria-expanded="true"] {
border-color: #b91c1c;
background: #fef2f2;
}
/* 実際は popover 属性で重ね、位置は CSS のアンカー配置で押したボタンのそばに決める。見本は流れの中に置いている。
position: relative は流れの中で矢印を置くための見本用で、popover にしたら外す(最前面の層では absolute になる) */
.x-popconfirm__p {
position: relative;
width: 264px;
margin: 10px 0 0 auto;
padding: 12px 14px;
border: 1px solid #cfd4dc;
border-radius: 10px;
background: #fff;
box-shadow: 0 8px 20px rgba(27, 32, 40, 0.14);
}
/* 吹き出しの矢印。押したボタンを指す */
.x-popconfirm__p::before {
content: "";
position: absolute;
top: -7px;
right: 30px;
width: 12px;
height: 12px;
border-top: 1px solid #cfd4dc;
border-left: 1px solid #cfd4dc;
background: #fff;
transform: rotate(45deg);
}
.x-popconfirm__t {
display: flex;
align-items: flex-start;
gap: 6px;
margin: 0 0 4px;
color: #1b2028;
font-size: 13px;
font-weight: 700;
}
.x-popconfirm__t .fa-solid {
margin-top: 2px;
color: #b45309;
}
.x-popconfirm__d {
margin: 0 0 10px;
color: #5b6472;
font-size: 11.5px;
line-height: 1.6;
}
.x-popconfirm__act {
display: flex;
justify-content: flex-end;
gap: 6px;
}
.x-popconfirm__cancel,
.x-popconfirm__ok {
min-height: 30px;
padding: 0 12px;
border-radius: 6px;
font: inherit;
font-size: 12px;
cursor: pointer;
}
.x-popconfirm__cancel {
border: 1px solid #7f8a99;
background: #fff;
color: #1b2028;
}
.x-popconfirm__ok {
border: 1px solid #b91c1c;
background: #b91c1c;
color: #fff;
font-weight: 700;
}
.x-popconfirm__b:focus-visible,
.x-popconfirm__cancel:focus-visible,
.x-popconfirm__ok:focus-visible {
outline: 2px solid #1d4ed8;
outline-offset: 2px;
}
AIへの指示文
「ポップ確認(Popconfirm)」を実装してください。
## これは何か
削除ボタンを押すと、そのすぐそばに「削除しますか?」の小窓が出て、キャンセルと削除するの2つのボタンで答える部品。画面全体は暗くならない。
## 使いどころ
- 一覧の行ごとの削除など、取り返しのつかない軽い操作を、その場で1回だけ確かめたいとき
- 確認のたびに画面全体を覆うダイアログを出すと、作業の流れが途切れるとき
## 実装の要件
- button[aria-haspopup=dialog][aria-expanded][aria-controls] + 小窓(問いかけ・補足・キャンセルと実行の2ボタン)
- 小窓に popover 属性、開くボタンに popovertarget を付ける。外側クリックと Esc で閉じる動きと重ね順はブラウザが持つ(Chrome・Edge は 114 から、Safari は 17 から、Firefox は 125 から。iOS の Safari が外側のタップで閉じるのは 18.3 から)
- 開いたらキャンセルへフォーカスを移すので、「削除」の click で先に this.focus() を呼ぶ。Safari と macOS の Firefox はマウスで押したボタンにフォーカスを移さず、そのままだと Esc で閉じたときに「削除」へ戻らない
- 小窓に position: relative を付けない。見本の position: relative は流れの中で矢印を置くためのもので、popover にすると最前面の層で absolute として扱われ、@supports not の枝では文書の先頭を基準に置かれて画面の外に出る。矢印の基準は popover の既定の position: fixed が担う
- 位置は CSS のアンカー配置で決める。popovertarget で開いた小窓は、押したボタンが暗黙のアンカーになる(Chrome・Edge は 133 から、Safari は 26 から、Firefox は 147 から)
- @supports (position-area: block-end) の中で margin: 10px 0 0; inset: auto; position-area: block-end span-inline-start; position-try-fallbacks: flip-block; を指定し、ボタンの下に右端をそろえて出す
- 下にはみ出すときはブラウザが上へ反転させ、隙間の margin も上下を入れ替える
- position-area に対応しないブラウザ向けに、@supports not (position-area: block-end) の中で小窓に margin: auto; を書き、画面の中央に出す(popover の既定の margin: auto に頼ると、margin を 0 にするリセット CSS に消されて画面の端に張りつく)
- ボタンを指す矢印は @supports (container-type: anchored) の中でだけ小窓の ::before で描く(Chrome・Edge は 143 から。Safari と Firefox は未対応なので矢印なしで出す)
- popover の既定の overflow: auto では枠の外の矢印が切れるので、小窓に overflow: visible; container-type: anchored; を付ける。上へ反転したときは @container anchored(fallback: flip-block) で矢印を下辺の下向きに付け替える
- 重い判断にはダイアログを使う。アカウントの削除や支払いのように影響の大きい操作は、画面の中央で理由まで説明する
- 取り消せる操作なら確認を挟まず、実行してからスナックバーで「元に戻す」を出すほうが手間が少ない
- ボタンの文言を「はい/いいえ」「OK」にしない。「削除する/キャンセル」のように動作を書く
- 外側を押したら「キャンセル」と同じ扱いで閉じる。押したら実行、にはしない
## アクセシビリティの要件
- 開くボタンに aria-haspopup="dialog" と aria-expanded を付け、aria-controls で小窓を指す
- aria-expanded は小窓の toggle イベントの newState(open / closed)に合わせて true / false に書き換え、外側クリックや Esc で閉じたときも古い値を残さない
- 小窓は role="dialog" にし、問いかけと補足を aria-labelledby / aria-describedby で結ぶ
- 開いたらフォーカスを危険でないほうのボタン(キャンセル)へ移す。Esc で閉じ、開いたボタンへフォーカスを戻す
- 背後は操作できるままなので aria-modal は付けない
## 参考にする実装
この形を出発点にして、プロジェクトの命名規則・色・余白に合わせて調整してください。
```html
<div class="x-popconfirm">
<div class="x-popconfirm__row">
<span class="x-popconfirm__file"><i class="fa-regular fa-file-lines" aria-hidden="true"></i>請求書_9月.pdf</span>
<button class="x-popconfirm__b" type="button" aria-haspopup="dialog" aria-expanded="true" aria-controls="x-popconfirm-p">
<i class="fa-solid fa-trash-can" aria-hidden="true"></i>削除
</button>
</div>
<!-- 見本なので開いた状態で流れの中に置いている。実際は小窓に popover 属性、「削除」に popovertarget="x-popconfirm-p" を付けて開閉し
(hidden 属性では閉じない)、aria-expanded は false から始めて toggle イベントで書き換える。開いたらフォーカスを「キャンセル」へ移す。
Esc か hidePopover() で閉じると、ブラウザはフォーカスを開く前の要素へ戻す。「削除」の click で先に this.focus() を呼び、
戻る先を「削除」にする(Safari と macOS の Firefox は、押したボタンにフォーカスを移さない) -->
<div class="x-popconfirm__p" id="x-popconfirm-p" role="dialog" aria-labelledby="x-popconfirm-t" aria-describedby="x-popconfirm-d">
<p class="x-popconfirm__t" id="x-popconfirm-t">
<i class="fa-solid fa-triangle-exclamation" aria-hidden="true"></i>このファイルを削除しますか?
</p>
<p class="x-popconfirm__d" id="x-popconfirm-d">共有中の相手も開けなくなります。</p>
<div class="x-popconfirm__act">
<button class="x-popconfirm__cancel" type="button">キャンセル</button>
<button class="x-popconfirm__ok" type="button">削除する</button>
</div>
</div>
</div>
```
```css
/* 貼った先のリセットに頼らないよう、この部品の中だけ border-box にする */
.x-popconfirm,
.x-popconfirm *:not([class*="fa-"]),
.x-popconfirm *:not([class*="fa-"])::before,
.x-popconfirm *:not([class*="fa-"])::after {
box-sizing: border-box;
/* 貼った先の既定の書体に引きずられないようにする。書体が変わると
文字の幅と高さが変わり、枠のサイズまでずれる */
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Hiragino Sans", "Noto Sans JP", "Yu Gothic UI", Meiryo, sans-serif;
}
.x-popconfirm {
position: relative;
width: 300px;
}
.x-popconfirm__row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
padding: 6px 6px 6px 12px;
border: 1px solid #e3e6ea;
border-radius: 8px;
background: #fff;
}
.x-popconfirm__file {
display: inline-flex;
align-items: center;
gap: 6px;
color: #1b2028;
font-size: 13px;
}
.x-popconfirm__file .fa-regular {
color: #5b6472;
}
.x-popconfirm__b {
display: inline-flex;
align-items: center;
gap: 5px;
min-height: 32px;
padding: 0 10px;
border: 1px solid #cfd4dc;
border-radius: 7px;
background: #fff;
color: #b91c1c;
font: inherit;
font-size: 12px;
cursor: pointer;
}
.x-popconfirm__b[aria-expanded="true"] {
border-color: #b91c1c;
background: #fef2f2;
}
/* 実際は popover 属性で重ね、位置は CSS のアンカー配置で押したボタンのそばに決める。見本は流れの中に置いている。
position: relative は流れの中で矢印を置くための見本用で、popover にしたら外す(最前面の層では absolute になる) */
.x-popconfirm__p {
position: relative;
width: 264px;
margin: 10px 0 0 auto;
padding: 12px 14px;
border: 1px solid #cfd4dc;
border-radius: 10px;
background: #fff;
box-shadow: 0 8px 20px rgba(27, 32, 40, 0.14);
}
/* 吹き出しの矢印。押したボタンを指す */
.x-popconfirm__p::before {
content: "";
position: absolute;
top: -7px;
right: 30px;
width: 12px;
height: 12px;
border-top: 1px solid #cfd4dc;
border-left: 1px solid #cfd4dc;
background: #fff;
transform: rotate(45deg);
}
.x-popconfirm__t {
display: flex;
align-items: flex-start;
gap: 6px;
margin: 0 0 4px;
color: #1b2028;
font-size: 13px;
font-weight: 700;
}
.x-popconfirm__t .fa-solid {
margin-top: 2px;
color: #b45309;
}
.x-popconfirm__d {
margin: 0 0 10px;
color: #5b6472;
font-size: 11.5px;
line-height: 1.6;
}
.x-popconfirm__act {
display: flex;
justify-content: flex-end;
gap: 6px;
}
.x-popconfirm__cancel,
.x-popconfirm__ok {
min-height: 30px;
padding: 0 12px;
border-radius: 6px;
font: inherit;
font-size: 12px;
cursor: pointer;
}
.x-popconfirm__cancel {
border: 1px solid #7f8a99;
background: #fff;
color: #1b2028;
}
.x-popconfirm__ok {
border: 1px solid #b91c1c;
background: #b91c1c;
color: #fff;
font-weight: 700;
}
.x-popconfirm__b:focus-visible,
.x-popconfirm__cancel:focus-visible,
.x-popconfirm__ok:focus-visible {
outline: 2px solid #1d4ed8;
outline-offset: 2px;
}
```
## 出力の約束
- HTML と CSS を分けて出す。JavaScript が要るときは素の JavaScript で書き、UIフレームワークは使わない。
- 参考実装がアイコンに Font Awesome のクラスを使っている場合は、プロジェクトで使っているアイコンかインライン SVG に置き換えてよい。
- 上のアクセシビリティ要件を満たす。満たせない箇所があるときは、理由を添えて先に知らせる。
- 色・角丸・余白は既存のデザイントークンがあればそれを使う。無ければ CSS 変数として切り出す。
使いどころ
- 一覧の行ごとの削除など、取り返しのつかない軽い操作を、その場で1回だけ確かめたいとき
- 確認のたびに画面全体を覆うダイアログを出すと、作業の流れが途切れるとき
注意点
- 重い判断にはダイアログを使う。アカウントの削除や支払いのように影響の大きい操作は、画面の中央で理由まで説明する
- 取り消せる操作なら確認を挟まず、実行してからスナックバーで「元に戻す」を出すほうが手間が少ない
- ボタンの文言を「はい/いいえ」「OK」にしない。「削除する/キャンセル」のように動作を書く
- 外側を押したら「キャンセル」と同じ扱いで閉じる。押したら実行、にはしない
アクセシビリティの要点
- 開くボタンに aria-haspopup="dialog" と aria-expanded を付け、aria-controls で小窓を指す
- aria-expanded は小窓の toggle イベントの newState(open / closed)に合わせて true / false に書き換え、外側クリックや Esc で閉じたときも古い値を残さない
- 小窓は role="dialog" にし、問いかけと補足を aria-labelledby / aria-describedby で結ぶ
- 開いたらフォーカスを危険でないほうのボタン(キャンセル)へ移す。Esc で閉じ、開いたボタンへフォーカスを戻す
- 背後は操作できるままなので aria-modal は付けない