確認画面
日本の業務アプリが必ず出荷する 3 ステップの確認フロー — 海外でも
review-before-commit と呼ばれるものです。このテンプレートは
マルチステップフォームの
機構をその最頻出形に特化させたものです: 入力ステップ(422 は
フィールドエラー)、
サーバが自分のパース結果から描画するレビューステップ、そして
確認描画時に発行した
冪等キーの
おかげで二重送信しても安全な完了ステップ。1 つの領域、領域全体の
スワップ、同じ真実の一部として再描画される
hc-stepper。
新規 JS はゼロです。
ライブテンプレート
Section titled “ライブテンプレート”フォームを送信すると Review に進みます — 入力した金額が
フォーマットされて返ってくるのは、レビューがエコーではなく
サーバのパース結果を見せているからです。Back は入力ステップに
値を保ったまま戻ります(名前付きサブミットであって
history.back() ではありません)。Place order で Done に
到達します。2 回押しても同じ受付番号が出ます — レスポンスが
冪等キーから導出される、それがリプレイ契約です。金額に数字以外を
入れると 422 経路が見られます。
Loading flow…
ページスケルトン
Section titled “ページスケルトン”実アプリ形の URL での完全なフロー。どのステップも同じ領域
(#flow)を outerHTML でスワップし、ステッパーは各レスポンスに
同乗するので状態と見た目が食い違うことはありません。
<!-- Step 1: input --><section id="flow"> <ol class="hc-stepper"> <li class="hc-stepper__step" aria-current="step"> <span class="hc-stepper__marker" aria-hidden="true">1</span> <span class="hc-stepper__label">Input</span> </li> <li class="hc-stepper__step">…Review…</li> <li class="hc-stepper__step">…Done…</li> </ol> <form method="post" action="/orders/confirm" data-hx-post="/orders/confirm" data-hx-target="#flow" data-hx-swap="outerHTML" data-hx-disabled-elt="find button[type=submit]"> <!-- …hc-field の入力… 422 はフィールドエラー付きでこのステップを再描画 --> <button class="hc-button" data-variant="primary" type="submit">Review</button> </form></section>
<!-- Step 2: review (/confirm のレスポンス) --><section id="flow"> <ol class="hc-stepper"> <li class="hc-stepper__step" data-state="complete"> <span class="hc-stepper__marker" aria-hidden="true">✓</span> <span class="hc-stepper__label">Input</span> </li> <li class="hc-stepper__step" aria-current="step">…Review…</li> <li class="hc-stepper__step">…Done…</li> </ol> <form method="post" action="/orders/place" data-hx-post="/orders/place" data-hx-target="#flow" data-hx-swap="outerHTML" data-hx-disabled-elt="find button[type=submit]"> <input type="hidden" name="idempotency_key" value="ik_7d1f9c2e"> <input type="hidden" name="item" value="Ergonomic chair"> <input type="hidden" name="amount" value="48000"> <dl class="hc-stack"> <div><dt>Item</dt><dd>Ergonomic chair</dd></div> <div><dt>Amount</dt><dd>¥48,000</dd></div> </dl> <div class="hc-toolbar" role="toolbar" aria-label="Review actions"> <button class="hc-button" type="submit" name="nav" value="back" formnovalidate>Back</button> <button class="hc-button" data-variant="primary" type="submit" name="nav" value="place">Place order</button> </div> </form></section>
<!-- Step 3: done (/place のレスポンス。フルページフローでは PRG) --><section id="flow"> <ol class="hc-stepper"> <li class="hc-stepper__step" data-state="complete">…Input ✓…</li> <li class="hc-stepper__step" data-state="complete">…Review ✓…</li> <li class="hc-stepper__step" aria-current="step">…Done…</li> </ol> <div class="hc-card" role="status"> <p>Order <strong>REQ-58214</strong> placed — ¥48,000.</p> </div></section>| 領域 | コンポーネント / ブロック | レシピ | 契約 |
|---|---|---|---|
| フロー全体 | 1 つの #flow 領域、outerHTML スワップ | multi-step-form | contract |
| ステッパー | hc-stepper、aria-current="step"、完了マーカーはサーバ描画 | — | 各ステップのスワップに同乗 |
| 入力ステップの 422 | hc-field + aria-invalid + エラーヒント | field-errors | contract |
| レビューステップ | <dl class="hc-stack"> の読み戻し + hidden フィールド | — | サーバはパース結果を描画する — 読み戻しこそが要点 |
| 戻る | 名前付きサブミット(name="nav" value="back"、formnovalidate) | multi-step-form | history.back() は使わない — 真実はサーバが所有 |
| 完了 / 二重送信 | キーはレビュー描画時に発行 | idempotency-key | contract |
設計を支える 3 つの規則:
- レビューはサーバの読み戻し。 ユーザーが確認するのはサーバが 理解した内容 — パース済み・フォーマット済み・正規化済み。 クライアントの文字列のエコーでは何も確認したことになりません。
- 値は hidden フィールドでレビューフォームに同乗: ステートレスで
検査可能、そして
/placeが再検証するものそのもの(必須です — hidden フィールドもクライアント供給値です)。 - キーはレビュー描画時に発行: 戻ってまた進めば新しいキー (新しい意図)、Place order のダブルクリックは同じキーのリプレイ (同じ意図)。
/orders/confirm//orders/placeを自分のリソース名に。 フルページ(no-JS)フローの/placeは受付ページへの303リダイレクト(PRG)で応答します — リプレイされたキーの保存済み レスポンスはその同じリダイレクトです。- レビューステップは利用規約チェックボックスと
hc.print.cssの 自然な置き場所です — 確認画面は印刷されます。 - 長いフォーム: 入力ステップに 自動保存と 未保存ガードを 合成。レビューと完了は読み取り専用なのでどちらも不要です。
- 入力が複数ページの場合: マルチステップフォームの ドキュメントどおりに入力ステップを連鎖させ、レビューを最後から 2 番目のステップに保ちます。