確認ダイアログ橋渡し
stimeo--confirm
ブラウザ標準の確認ダイアログを、読み上げにも対応した警告ダイアログに置き換えます。
Turbo が使うブラウザ標準の確認ダイアログを、キーボードと読み上げに対応した警告ダイアログに置き換えます。フォーカスの閉じ込めや Esc での取消は、ほかのモーダルと同じ仕組みを使い回します。置き換えはこの部品がページにある間だけで、ページを離れると元へ戻すので、二重に掛かることも残ることもありません。リンクやボタンのクリックを横取りして、承認したときだけ元の遷移や送信を続ける使い方もできます。最初にフォーカスが当たるのは安全な側、つまり取消です。ダイアログを置いていないページでは、標準の確認へ自然に戻ります。ダイアログのマークアップは利用側で用意します。
実行中
アカウントを削除しますか?
キーボード操作
| キー | 動作 |
|---|---|
| Esc | 取消(false で解決)してダイアログを閉じる。 |
| Tab / Shift+Tab | ダイアログ内でフォーカスを循環させる(フォーカストラップ)。 |
| Enter | フォーカス中の承認/取消ボタンを実行する。 |
<%# Markup for the confirm-bridge demo.
The controller intercepts the danger button (request action), shows the accessible
alert dialog with the message, and only continues the form submit when confirmed.
The dialog reuses the shared FocusTrap (trap, restore, Escape=cancel). This
Playground has no backend, so demo.js cancels the real submit and reports the
outcome from the resolve event. %>
<div class="confirm-demo" data-controller="stimeo--confirm"
data-stimeo--confirm-confirm-label-value="<%= t("components.confirm.demo.confirm_label") %>"
data-stimeo--confirm-cancel-label-value="<%= t("components.confirm.demo.cancel_label") %>">
<form class="confirm-demo__form" action="#" method="post" data-confirm-demo="form">
<button type="submit" class="demo-trigger demo-trigger--danger"
data-action="click->stimeo--confirm#request"
data-stimeo--confirm-message-param="<%= t("components.confirm.demo.message") %>">
<%= t("components.confirm.demo.trigger") %>
</button>
</form>
<p class="confirm-demo__result" data-confirm-demo="result" role="status" aria-live="polite"
data-confirmed="<%= t("components.confirm.demo.confirmed") %>"
data-cancelled="<%= t("components.confirm.demo.cancelled") %>"></p>
<div class="confirm-demo__backdrop" data-stimeo--confirm-target="dialog"
role="alertdialog" aria-modal="true" aria-labelledby="confirm-title"
aria-describedby="confirm-message" hidden>
<div class="confirm-demo__dialog">
<h2 id="confirm-title" data-stimeo--confirm-target="title">
<%= t("components.confirm.demo.title") %>
</h2>
<p id="confirm-message" data-stimeo--confirm-target="message"></p>
<div class="confirm-demo__actions">
<button type="button" class="demo-trigger" data-stimeo--confirm-target="cancel"
data-action="click->stimeo--confirm#cancel"></button>
<button type="button" class="demo-trigger demo-trigger--danger"
data-stimeo--confirm-target="confirm"
data-action="click->stimeo--confirm#confirm"></button>
</div>
</div>
</div>
</div>
/*
* Presentation-only styles for the confirm-bridge demo.
* The library only toggles the dialog's hidden attribute and manages focus; this
* CSS owns the modal overlay, the dialog box, and the danger-button affordance.
*/
.confirm-demo {
display: flex;
flex-direction: column;
gap: 0.75rem;
max-width: 32rem;
}
.demo-trigger--danger {
border-color: var(--danger-500);
/* The label is danger TEXT, so it needs the 700 step: danger-500 measures
3.68:1 on the dark canvas. The border keeps 500 (non-text, 3:1 floor). */
color: var(--danger-700);
}
.confirm-demo__result {
margin: 0;
min-height: 1.25rem;
font-size: 0.95rem;
color: var(--color-text-muted);
}
.confirm-demo__backdrop {
position: fixed;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
background: rgb(15 23 42 / 0.5);
z-index: 50;
}
.confirm-demo__backdrop[hidden] {
display: none;
}
.confirm-demo__dialog {
width: min(28rem, calc(100vw - 2rem));
padding: 1.25rem;
border-radius: 0.75rem;
background: var(--surface, var(--surface-card));
box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.2);
}
.confirm-demo__dialog h2 {
margin: 0 0 0.5rem;
font-size: 1.1rem;
}
.confirm-demo__actions {
display: flex;
justify-content: flex-end;
gap: 0.5rem;
margin-top: 1rem;
}
// Consumer-side JS for the confirm-bridge demo (demo-only).
// On confirm, the controller continues the original action by submitting the form.
// This Playground has no backend, so we cancel the real submit and instead report
// the outcome (confirmed / cancelled) from the controller's resolve event.
const form = document.querySelector("[data-confirm-demo='form']");
const result = document.querySelector("[data-confirm-demo='result']");
if (form) {
form.addEventListener("submit", (event) => event.preventDefault());
}
document.addEventListener("stimeo--confirm:resolve", (event) => {
if (!result) return;
result.textContent = event.detail.confirmed
? result.dataset.confirmed
: result.dataset.cancelled;
});
これらのスタイルは共通のデザイントークン(ライト/ダーク両対応)を使います。共通スタイルも一緒にコピーし、ルート要素の data-theme を切り替えればダークになります。
このコンポーネントを動かすために HTML へ記述する data-* 属性です。ルート要素に下の data-controller を付け、その内側に各 target / value / action を配置します。
ルート要素に付与
data-controller="stimeo--confirm"
ターゲット
| 名前 | 説明 | 属性 |
|---|---|---|
dialog
必須
|
トラップ・表示の対象となる警告ダイアログ要素(role="alertdialog")。 |
data-stimeo--confirm-target="dialog" |
title
|
ダイアログ名となる任意の見出し要素。 | data-stimeo--confirm-target="title" |
message
|
確認メッセージを書き込む要素。 | data-stimeo--confirm-target="message" |
confirm
|
承認ボタン。文言は confirmLabel から設定する。 |
data-stimeo--confirm-target="confirm" |
cancel
|
取消ボタン。文言は cancelLabel から設定する。 |
data-stimeo--confirm-target="cancel" |
値(Values)
| 名前 | 説明 | 属性 |
|---|---|---|
confirmLabel
|
承認ボタンの文言(既定 OK)。 |
data-stimeo--confirm-confirm-label-value |
cancelLabel
|
取消ボタンの文言(既定 Cancel)。 |
data-stimeo--confirm-cancel-label-value |
initialFocus
|
初期フォーカスのボタン: cancel か confirm(既定 cancel)。 |
data-stimeo--confirm-initial-focus-value |
アクション
| 名前 | 説明 | アクション |
|---|---|---|
confirm
|
保留中の確認を true で解決して閉じる。 |
stimeo--confirm#confirm |
cancel
|
保留中の確認を false で解決して閉じる。 |
stimeo--confirm#cancel |
request
|
クリックを横取りし、承認後に元のアクションを続行する。 | stimeo--confirm#request |
イベント
| 名前 | 説明 | イベント |
|---|---|---|
open
|
ダイアログを開いたとき発火。detail に message を伴う。 |
stimeo--confirm:open |
resolve
|
確定したとき発火。detail に confirmed(真偽値)を伴う。 |
stimeo--confirm:resolve |
状態フック
ライブラリが操作するのはこれらの ARIA / data 属性、カスタムプロパティだけです。見た目は利用側 CSS がこれらに反応して作ります([aria-selected] / [aria-expanded] / var(--stimeo--…) などのセレクタでフックします)。
| フック | 対象 | 意味 |
|---|---|---|
hidden |
ダイアログ | 確認ダイアログの表示/非表示を切り替える。 |