コンテンツにスキップ

UI コピーの書き方

ハイパーメディアアプリでは言葉を描画するのはサーバです。アラートの タイトルも、トーストのメッセージも、エラーの一行も、すべて HTML として ワイヤ上を流れます。つまり UI コピーは契約の一部 — 周囲のマークアップと 同じだけの注意に値します。このページは、あなたのアプリが出力する 文字列のためのキットのボイス & トーンガイドです。キット自身の組み込み 文字列は末尾で扱います。

原則実践では
次のアクションのために書くどのメッセージも、読んだ人が今なにをすべきか — フィールドを直す、リトライする、取り消す、やり直す — が分かる状態で終わること。
プロトコル語彙より平易な言葉HTTP ステータス名、ステータスコード、例外クラス名はログと機械向け属性のもの。見えるテキストには決して出さない。
用語は UI 自身のラベルと一致させるUI が呼ぶとおりに呼ぶ。ラベルが「表示名」なら、エラーも「表示名」— 「ユーザー名」でも display_name でもなく。

エラーメッセージが答える問いは 2 つ — 何が起きたかどう直すか。 1 メッセージにつき 1 つの内容だけを伝えます。

  • HTTP ジャーゴンや内部識別子をエンドユーザーに見せないこと。 ステータステキスト、エラーコード、制約名は機械可読属性 (field-errors フラグメントdata-codedata-field)に乗せます。見えるテキストは人間のため のものです。
  • フィールドエラーはフィールドをそのラベル<label> の テキスト — で名指しします。読んだ人が見つけられるように。
  • 失敗だけでなく直し方を述べる:「無効なメールアドレス」より 「有効なメールアドレスを入力してください」。
Don’tDo
Unprocessable Entity以下のエラーを修正してください。
Error 422変更は保存されていません。ハイライトされたフィールドを直して再送信してください。
email: duplicateこのメールアドレスは既に登録されています。
Constraint violation: members_email_keyこのメールアドレスは既に登録されています。
Invalid input2026-07-09 の形式で日付を入力してください。

左の列はステータスライン上では問題ありません — 422 Unprocessable Entity は HTTP ステータスとしては完全に正しい。人が読む .hc-alert__title になった瞬間に、正しくなくなります。

トーストは読まれるのではなく、ちらりと見られるものです。配線は トーストレシピを参照。

  • 短く、行動を先に — 目安は 6 語(全角 15 文字)以内。
  • 「正常に」は不要 — トーストが出たこと自体が成功の合図です。 「メンバーを保存しました」であって「メンバーは正常に保存されま した!」ではなく。
  • エラートーストは次にすべきことを言う — 「エラーが発生しました」ではなく「保存できませんでした。接続を確認 して再試行してください。」。
  • スティッキーはユーザーの行動が必須のときだけ。 duration: 0 は 自動消滅を外します。取り消しや再認証など、判断を運ぶトーストのために 取っておき、強調のためには使わないこと。
  • アクションラベルは動詞 — 「元に戻す」「再試行」。 「こちらをクリック」は決して使わない。
Don’tDo
項目は正常に削除されました!項目を削除しました
保存中にエラーが発生しました保存できませんでした。しばらくして再試行してください。
こちらをクリックで元に戻す元に戻す

confirm-action レシピは ダイアログの各部分を属性として公開しています — 漠然とした質問では なく、アクションを名指しするために使ってください:

  • タイトルはアクションを名指しする:「本当によろしいですか?」 ではなく「請求書の削除」。
  • 本文は結果を述べる: 何が失われるのか、取り消せるのか。
  • 確定ボタンは動詞を繰り返す:「OK」「はい」ではなく「削除」— ボタンのラベルだけで、押すと何が起きるかが分かること。
<button class="hc-button" data-variant="error"
data-hx-delete="/invoices/1024"
data-hc-confirm="請求書 #1024 を完全に削除します。"
data-hc-confirm-title="請求書の削除"
data-hc-confirm-label="削除">
削除
</button>

hc-empty ブロックは ここに何が並ぶはずかを述べ、次の一歩を差し出します — 「データがありません」としか言わない空画面は行き止まりです:

<div class="hc-empty">
<p class="hc-empty__title">メンバーはまだいません</p>
<p class="hc-empty__description">
招待した人がここに表示されます。
</p>
<div class="hc-empty__actions">
<button class="hc-button" data-variant="primary">メンバーを招待</button>
</div>
</div>

役割が違えば、スロットも違います:

  • ヒント(.hc-field__hint)は常に真のガイダンス — 「共有されることはありません。」。フィールドが無効な間もミュート カラーのままです。
  • エラー(バリデーション結果としての .hc-field__message、または ビヘイビアが管理する .hc-field__error スロット)は今直すべき こと — 「有効なメールアドレスを入力してください。」。エラー色で 表示され、問題が消えれば一緒に消えます。

ユーザーがフィールドを直したあとも役に立つ文なら、それはヒントです。 マークアップと再着色のルールは フィールド → ヒントとエラーの区別を 参照してください。

ビヘイビアが自力で注入するもの — 「Dismiss」「Confirm」「Cancel」、 アップロードやページネーションのステータス行 — はすべて 1 つの メッセージカタログで解決されるため、アプリは setMessages() 一発で 自分のボイスと言語に言い換えられます。 国際化(i18n)を参照。