コンテンツにスキップ

トースト

トーストは、画面の隅に現れる短い時間制限つきの通知です。 installToast ビヘイビアが document.body 上の hc:toast イベントを 待ち受け、ページのトースト領域へトーストを描画し、数秒後に自動で 解散させます。

各ボタンはデモエンドポイントに POST し、サーバは 204 No ContentHX-Trigger: {"hc:toast":…} ヘッダーを返します — htmx が <body> 上で イベントを発火し、installToast がトーストを描画します。下のクライアント 発火のプレビューと違い、こちらは実際のエンドポイントを往復します — サーバ応答契約をそのまま実装したデモで、api/recipes/toast/ 配下に 置かれています。Sticky errorduration: 0 なので、閉じるまで残ります — すべてのトーストは閉じるボタンを描画し、フォーカス中のトーストは Escape でも閉じられます。

トースト領域は単一のホスト要素です。<body> の終わり近くに一度だけ 置いてください。領域がなければ、ビヘイビアが自動で作成します。

<div
class="hc-toast-region"
data-hc-toast-region
role="region"
aria-label="Notifications">
</div>

detail オブジェクトつきのバブリングする hc:toast イベントを発火 します:

document.body.dispatchEvent(
new CustomEvent('hc:toast', {
bubbles: true,
detail: { message: 'Saved', variant: 'success' },
})
);

試してみてください — 各ボタンが上のイベントを発火します。トーストは ページの隅に現れ(そして自動で消えます):

HX-Trigger レスポンスヘッダーを返します。htmx が body 上で hc:toast を発火してくれます。

HTTP/1.1 200 OK
HX-Trigger: {"hc:toast":{"message":"Saved","variant":"success"}}

1 つのレスポンスに複数イベントを載せる場合:

HX-Trigger: {"hc:toast":{"message":"Saved","variant":"success"}, "items:refresh":true}
フィールドデフォルト備考
messagestring(必須)本文テキスト。
titlestring(省略)メッセージの上の太字 1 行。
variantstring'info'info / success / warning / error
durationnumber4500ミリ秒。0 は手動で消されるまで保持。
idstring(省略)安定ハンドル: 同じ id の後続 hc:toast は新規に積まず、既存トーストをその場で更新します。
actionobject(省略){ label, event } — アクションボタンを描画。押下で detail: { id, action, toast } を持つバブリング CustomEvent(event) を発火し、トーストを閉じます。

variant="error" はトーストを role="alert" / aria-live="assertive" にも切り替えるため、スクリーンリーダーは割り込ん で読み上げます。他のバリアントは role="status" / aria-live="polite" を使います。

アクションボタンと id による更新

Section titled “アクションボタンと id による更新”

idaction を組み合わせると、他のレシピが依拠する 2 つの サーバー駆動パターンになります:

  • id による更新(loading → success / error): まず { id: 'job-42', message: 'Exporting…', duration: 0 } を送り、次に 同じ idhc:toast を送ると、表示中のトーストがその場で 変化します(トースト → id による更新の プロミストーストパターン)。
  • アクション(例: 元に戻す): { action: { label: 'Undo', event: 'hc:undo' } } でボタンが描画され、htmx は data-hx-trigger="hc:undo from:body" で 受け取れます — undo-delete レシピを 参照。すべてのトーストには閉じるボタン(.hc-toast__close)も 描画されるため、スティッキートースト(duration: 0)は action が なくてもポインタで解散できます。フォーカスを含むトーストは Escape で解散できます(アクションのイベントは発火しません)。
// Success
document.body.dispatchEvent(new CustomEvent('hc:toast', {
bubbles: true,
detail: { message: 'Invoice sent.' },
}));
// Error, sticky until dismissed
document.body.dispatchEvent(new CustomEvent('hc:toast', {
bubbles: true,
detail: {
title: 'Sync failed',
message: 'Could not reach the server. Retrying…',
variant: 'error',
duration: 0,
},
}));

サーバサイドのハンドラから:

# Django
from django.http import HttpResponse
import json
def delete_item(request, id):
item = get_object_or_404(Item, pk=id)
item.delete()
return HttpResponse('', headers={
'HX-Trigger': json.dumps({
'hc:toast': {'message': 'Deleted.', 'variant': 'success'},
}),
})

ワイヤ契約はボディのフラグメントではなくレスポンスヘッダーです: htmx が処理するどのレスポンスも HX-Trigger: {"hc:toast":{…}} を 運ぶことができ、htmx が <body> 上で hc:toast イベントを発火 します — ペイロードのスキーマは上の detail の形です。

ケースレスポンス
トーストのみ — ページ上は何も変わらない204 No Content + HX-Trigger: {"hc:toast":{"message":"Saved","variant":"success"}}(ライブデモの形)
スワップと一緒に任意の 2xx フラグメントレスポンス + 同じヘッダー — トーストはスワップに同乗します
1 レスポンスに複数イベント1 つの JSON オブジェクト: HX-Trigger: {"hc:toast":{…},"items:refresh":true}
失敗の通知非 2xx + HX-Trigger のエラートースト — htmx は何もスワップせず、トーストが失敗を報告します(live-searchdatagrid-pager の失敗時の作法)

ヘッダーの値は latin-1 です — 非 ASCII のテキストは JSON の \uXXXX に エスケープしてください(htmx はネイティブにパースします。 ワンライナーは undo-delete の契約に あります)。

  • info / success / warningrole="status"(polite)を使う ため、ユーザーの流れを遮りません。
  • errorrole="alert"(assertive)を使い、失敗を即座に読み上げ させます。本物のエラーのために取っておいてください。
  • 領域自体はラベルつきの region ランドマークです — 支援技術で移動 できます。
  • トーストは自動で消えます。ユーザーが対応しなければならない重要な 情報をここに置かないでください。完了したアクションの確認が正準の ユースケースです。

プログレッシブエンハンスメント

Section titled “プログレッシブエンハンスメント”

hc.behaviors.js なしでは、hc:toast イベントは発火されてもリスナーが 存在せず、何も描画されません。HTTP レスポンス自体は依然として妥当です — HX-Trigger は素の fetch() 呼び出しには no-op です。

JavaScript なしで確認を伝えなければならないアプリケーションでは、 フラッシュメッセージをページ内にサーバレンダリングし (hc-alert が正しい コンポーネントです)、次のレスポンスで取り除いてください。

  • アラート — ブロック レベルのインライン通知。ページの再読み込みまで残ります。
  • 確認アクション — 成功時のトーストとよく組み合わされます。