Behavior
docs/BEHAVIOR.md をそのまま render。下に挙動を試せる live demo
AdXpand — Behavior Rules
確認・通知・フィードバックの判断ルール。 「いつモーダルを出すか」「いつ Toast にするか」「いつ Undo を提供するか」を統一します。
0. 判断の2軸
影響の大きさ(横軸) × 取り消し可能性(縦軸)
- 影響の大きさ: 自分のみ/チームに波及/顧客や課金に影響
- 取り消し可能性: 即時 Undo 可能/後から復元可能(ゴミ箱・履歴)/不可逆
この2軸で「確認の重さ」を決めます。
1. 確認パターン早見表
| 状況 | 影響 | 復元 | 推奨 UI |
|---|---|---|---|
| タブ切替・ソート・フィルタ | 自分のみ | 即時 | 確認なし |
| お気に入り・ピン留め・テーマ切替 | 自分のみ | 即時 | 確認なし |
| 設定保存・トグル変更 | 自分のみ | 即時 | 確認なし(自動保存)+ 微 Toast |
| 下書き削除(Trash 行き) | 自分のみ | 後から | Toast + Undo(5–10秒) |
| アーカイブ・ステータス変更 | 自分/チーム | 後から | Toast + Undo |
| メンバー招待・共有リンク発行 | チーム | 後から | インライン確認 + 完了 Toast |
| 公開状態の切替 | チーム/顧客 | 即時/後から | インライン確認(2段階ボタン) |
| メンバー解除・権限剥奪 | チーム | 不可逆寄り | AlertDialog |
| 課金プラン変更・購入 | 顧客/課金 | 不可 | AlertDialog + 金額表示 |
| 本番環境への破壊操作 | 顧客 | 不可 | AlertDialog destructive + 名前入力確認 |
| プロジェクト/組織削除 | 全員 | 不可 | AlertDialog destructive + 名前入力確認 |
| 大量一括操作(100件以上) | 状況依存 | 状況依存 | AlertDialog + 件数明示 |
2. AlertDialog(確認モーダル)を出すルール
2.1 出すべきとき
以下のどれか1つでも当てはまったら出す:
- 復元不可能(DB drop、本番環境への破壊)
- 課金が発生(プラン変更、有料機能購入、リソース増設)
- 他人に影響(メンバー解除、共有解除、公開停止)
- 大量操作の最終確定(100件以上の一括処理)
2.2 出してはいけないとき
- 単なる保存・更新
- 自分しか使わない設定変更
- すぐ Undo できる操作(→ Toast + Undo を使う)
- ログアウト(→ 即実行 + Toast)
2.3 AlertDialog の書き方ルール
import { AlertDialog } from "~/components";
<AlertDialog
open={open}
onOpenChange={setOpen}
variant="destructive" // destructive / warn / default
title="プロジェクトを削除" // 動詞 + 対象(疑問形にしない)
desc="この操作は取り消せません。プロジェクト「acme-prod」とすべてのデータが削除されます。"
confirmLabel="削除する" // 操作名を明示。「OK」は禁止
cancelLabel="キャンセル"
requireText="acme-prod" // destructive で不可逆ならテキスト入力必須
onConfirm={async () => { await api.delete(); }}
/>
ルール:
- ✅
confirmLabelは操作名(「削除する」「アップグレードする」「招待する」) - ❌
confirmLabelを「OK」「はい」にしない - ✅
descで何が起きるか具体的に書く(×「本当に?」○「acme-prod とすべてのデータが削除されます」) - ✅ destructive の不可逆操作は
requireText(プロジェクト名入力)を要求 - ✅ 取り消せるなら
descに「あとで復元できます」と明記 - ❌
confirmLabelを赤(danger)にするのは destructive のときだけ
3. Toast を使うルール
3.1 種別と用途
| variant | 使う場面 | 表示時間 |
|---|---|---|
success | 操作完了(保存・送信・公開) | 2–3秒 |
info | 状態通知(同期中・接続中) | 3–5秒 |
warn | 注意喚起(容量警告・期限近づく) | 5秒 or 手動閉じ |
error | エラー(通信失敗・バリデーション) | 手動閉じ |
progress | 長時間処理の進捗 | 完了まで |
3.2 Toast + Undo パターン
削除・アーカイブなど「やっちゃったけど戻せる」操作で必須。
import { toast } from "sonner";
toast.success("下書きを削除しました", {
action: { label: "元に戻す", onClick: restore },
duration: 8000 // Undo は長め
});
ルール:
- ✅ Undo 可能な操作は必ず
actionを提供 - ✅ Undo の表示時間は 5–10 秒(短すぎ・長すぎ NG)
- ❌ Undo できないのに「元に戻す」ボタンを出さない(嘘 UI)
3.3 Toast にしてはいけないこと
- ❌ 致命的エラー(→ Banner + Modal)
- ❌ ユーザーアクションが必要なもの(→ Modal)
- ❌ 重要な情報の唯一の表示場所(消えるので)
4. インライン確認パターン
「2段階ボタン」で確認をボタン自身に組み込む。 モーダルほど大げさじゃないが、明確な確定が欲しい時。
// React state で 2段階表示を自前実装
const [armed, setArmed] = useState(false);
useEffect(() => {
if (!armed) return;
const t = setTimeout(() => setArmed(false), 5000);
return () => clearTimeout(t);
}, [armed]);
<Btn variant="danger" onClick={() => (armed ? doDelete() : setArmed(true))}>
{armed ? "もう一度クリックで削除" : "削除"}
</Btn>
使う場面
- 復元可能な削除
- 公開トグル
- 重要だがモーダルほどではない確定操作
5. フィードバック早見表
| 状況 | UI |
|---|---|
| 入力中の検証エラー | フィールド下に <Field error="...">(赤テキスト) |
| フォーム送信成功 | Toast success(2–3秒) |
| フォーム送信エラー(再試行可) | Toast error + フィールドにエラーメッセージ |
| 自動保存完了 | サブトル Toast or インラインインジケータ「保存済み ✓」 |
| 長時間処理(>2秒) | Toast progress + 進捗% or インラインスピナー |
| 接続断 | Banner(ページ上部、再接続まで表示) |
| サーバーエラー(500) | Banner + Toast error + サポート導線 |
| 致命的(決済失敗等) | Modal + 再試行ボタン + サポート連絡導線 |
6. 進捗とローディング
| 待ち時間 | UI |
|---|---|
| < 200ms | 何もしない(ちらつき防止) |
| 200ms – 1s | ボタン内スピナー(押下後そのまま) |
| 1s – 2s | Skeleton(カードや表のプレースホルダー) |
| 2s – 10s | Skeleton + 「読み込み中...」テキスト |
| > 10s | プログレスバー or 「バックグラウンドで実行中、完了したら通知」 |
| バックグラウンド | Toast progress + 完了時に Toast success |
7. アンチパターン(禁止)
- ❌ 「保存しました」モーダル → Toast
successで十分 - ❌ ログアウト確認モーダル → 即実行 + Toast「ログアウトしました」
- ❌ 何度も同じ確認 → 一度確認したらセッション中は省略 or 「次回から表示しない」を提供
- ❌ 「OK / キャンセル」だけのボタン → 必ず操作名を書く
- ❌ 取り消せる操作にモーダル → Toast + Undo を使う
- ❌ モーダル on モーダル → 1階層まで(重ねない)
- ❌ 5秒消えるエラー Toast → エラーは手動閉じ or Banner
- ❌ 無音で破壊操作実行 → 必ず確認 or 通知
- ❌ 「確認画面」だけのページ → モーダルで済ませる
8. 操作名のコピーライティングルール
| 場面 | NG | OK |
|---|---|---|
| 確定ボタン | OK / はい | 削除する / 送信する / 公開する |
| キャンセル | いいえ | キャンセル |
| 削除確認 | 本当に削除しますか? | 「acme-prod」を削除 |
| 完了 Toast | 成功しました | プロジェクトを削除しました |
| エラー Toast | エラーが発生しました | 接続できませんでした。再試行してください |
| ローディング | Loading... | 読み込み中... |
原則:
- 動詞は終止形(「削除する」)または体言止め(「削除」)
- 過去形は完了 Toast のみ(「削除しました」)
- 主語は省略(「プロジェクトを削除しました」、×「あなたのプロジェクトが削除されました」)
- 敬語は使わない(「保存します」→「保存」)
9. デフォルト挙動の判断フロー
ユーザー操作
↓
取り消せる?
├─ いいえ(不可逆)
│ ↓
│ 影響範囲は?
│ ├─ 顧客/課金 → AlertDialog destructive + 名前入力
│ ├─ チーム → AlertDialog
│ └─ 自分のみ → AlertDialog
│
└─ はい
↓
他人に影響?
├─ はい → インライン確認 → 完了 Toast
└─ いいえ → 即実行 → Toast + Undo(破壊系のみ)
Live Demo
docs では伝わらない振る舞いを実体ボタンで
AlertDialog復元不可 / 課金 / 他人に影響 / 大量操作
ボタンラベルの命名
confirmLabel は必ず操作名を書く。「OK」「はい」は禁止。
Toast非ブロッキングなフィードバック
インライン確認モーダルほど大げさじゃない確定操作
進捗とローディング待ち時間ごとの UI
< 200ms
何もしない(ちらつき防止)
200ms – 1s
ボタン内スピナー
1s – 2s
Skeleton