コンテンツにスキップ

トースト

hc-toast は一時的な通知です。トーストのマークアップは書きません — イベントを発火する(または HX-Trigger ヘッダーを返す)と、 installToast().hc-toast.hc-toast-region へ描画し、遅延の 後に自動で解散させます。

別名: スナックバー、通知ポップアップ。

トーストは installToast() ビヘイビアが描画します — ないと hc:toast イベントを発火しても何も起きません。 起動時に一度 インストールしてください:

import { installToast } from '@hypermedia-components/core';
installToast(); // idempotent; returns an uninstaller

ゼロ設定の @hypermedia-components/core/behaviors エントリは自動で インストールします。マークアップは不要です — 事前描画しなければ ビヘイビアがデフォルトの .hc-toast-region を作成します。

このマークアップはビヘイビアが描画します — 手で書くことはありません。

Saved
Your changes were saved.
Profile updated.

document.bodyhc:toastCustomEvent を発火します:

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

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

…あるいはサーバにレスポンスから発火させます — htmx が HX-Trigger ヘッダーを同じイベントに変えます:

HX-Trigger: {"hc:toast": {"message": "Saved.", "variant": "success"}}

detail のフィールド: message(必須)、title?variant? (info · success · warning · error)、duration? ms(0 は 解散されるまで保持)、id?(下記)、action?(下記)。error は assertive に読み上げられ(role="alert")、それ以外は role="status" を使います。

アクションボタン。 action: { label, event } を足すとトーストが ボタンを描画します。クリックすると event という名前のバブリングする CustomEvent を発火します(その後トーストを解散)。素のリスナーか、 任意の祖先の htmx data-hx-trigger="<event>" で捕まえます — Undo に 便利です:

document.body.dispatchEvent(new CustomEvent('hc:toast', {
bubbles: true,
detail: { message: 'Item deleted', duration: 0, action: { label: 'Undo', event: 'hc:undo' } },
}));
document.body.addEventListener('hc:undo', () => restoreItem());

id による更新。 トーストに id を与えると、同じ id の後続 hc:toast は、新しいトーストを積む代わりにそれをその場で更新します (メッセージ / バリアントを再描画し、自動解散タイマーをリセット)。 これがクライアント状態なしの loading → success / error の promise をモデル化します — ネットワークは htmx に残ります:

# request fires a sticky loading toast
HX-Trigger: {"hc:toast": {"id": "save-42", "message": "Saving…", "duration": 0}}
# the response updates the same toast
HX-Trigger: {"hc:toast": {"id": "save-42", "message": "Saved!", "variant": "success", "duration": 4500}}

トーストがモデル化するのは UI の状態だけです。実際のリクエストは htmx が実行します。

トーストは最初の [data-hc-toast-region] へ描画されます。なければ ビヘイビアが(右下に)作成します。位置とスタッキングを設定するには 事前描画してください:

<div class="hc-toast-region" data-hc-toast-region
data-position="top-center" data-limit="3"
role="region" aria-label="Notifications"></div>

data-position が領域をアンカーします — {top,bottom}-{left,center,right}(デフォルト bottom-right)。 top 系は下向きに積まれ(最新が端に最も近い)、bottom 系は上向きに 積まれます。

<div class="hc-toast-region" data-hc-toast-region data-position="top-right" ></div>

data-limit="N" が見えるトースト数の上限です。新しいものが届くと 最も古いものが追い出されます。なければスタックは無制限です。

すべてのトーストは上端コーナーに閉じるボタン (.hc-toast__close、ラベルはカタログメッセージ toast.dismiss)を 描画します — マウスでも見つけられる可視の解散手段で、特に スティッキートースト(duration: 0)で重要です。その上に 2 つの操作が重なります:

  • スワイプ — トーストを横にドラッグし、幅の約 40% を超えると 飛び出し、そうでなければスナップバックします。縦のドラッグは 引き続きページをスクロールします。動きは prefers-reduced-motion の下で取り除かれます。
  • Escape — フォーカスを含むトーストを解散します(閉じるボタン またはアクションボタンがあるため、どのトーストにも キーボードで到達できます)。
  • 領域はラベルつきの role="region" です。ラベルは i18n カタログ (toast.label)で翻訳できます。
  • 各トーストはライブ領域です — errorrole="alert" / aria-live="assertive"、それ以外は role="status" / aria-live="polite" — フォーカスを動かさずに読み上げられます。
  • 色だけに頼らないでください: title か明確な message を含めて ください。
  • duration は読み切れる長さに保ってください(重要なメッセージは 0)。
  • 閉じるボタンのアクセシブルネームはカタログメッセージ toast.dismiss(既定 “Dismiss”)です — setMessages() で 翻訳してください。
トークンパス用途
toast.region-inset / region-gap端からの領域オフセット / トースト間のギャップ。
toast.gapタイトルと本文の間のギャップ。
toast.min-width / max-widthトーストの幅の範囲。
toast.padding-y / padding-x / radiusボックスの寸法。
toast.{variant}.{bg,fg,border}バリアントごとの色。
生成される CSS 変数を表示
--hc-toast-region-inset | -region-gap
--hc-toast-gap | -min-width | -max-width | -padding-y | -padding-x | -radius | -duration
--hc-toast-info-bg | -fg | -border
--hc-toast-success-bg | -fg | -border
--hc-toast-warning-bg | -fg | -border
--hc-toast-error-bg | -fg | -border

レシピでの利用: トースト