Skip to content
fandhe-frontendcore v0.4.3
GitHub

Toast

一時的な通知の queue 表示です。fandhe-frontend-headless-ui の toast mod は group(live region)/ root(通知 1 件)/ title / description / action-trigger / close-trigger の 6 anatomy パーツと、複数通知を有界なキューとして管理する状態機械 Toaster を提供します。aria-live は ToastStatus から決定的に導出され(Error のみ assertive、他は polite)、group は role="region" + 必須の aria-label + tabindex="-1" を、root は role="status" + data-state="open"(固定値)+ tabindex="0" を固定付与します。タイマーによる自動 dismiss の実配線は本部品のスコープ外です。

キーボード操作

キー対象効果
Tab / Shift+Tabroot → action-trigger → close-triggerroot の tabindex="0" とネイティブ button により、ブラウザ標準でこの順にフォーカス到達します。Radix の F8 / Zag の Alt+T によるリージョンフォーカスは未配線です。
Enter / Spaceaction-trigger / close-triggerネイティブ button の click が発火します。click から "dismiss" dispatch への配線(fandhe-frontend-wasm-full の MAPPING_TABLE)は未実装で、Escape での閉鎖も同様に未配線です。

参考サイトとの差分

Zag.js(toast.connect.ts/toast-group.connect.ts、ark-ui の実体)・Radix Primitives Toast・chakra-ui v3 Toast と突合しました(イシュー #1643)。anatomy(6 パート)はいずれの参照軸とも増減なしで一致します。是正した差分は次の 4 点です。

  • root に data-state="open" を追加: Zag・Radix 双方が持ちます。固定値 "open" のみを発行し、"closed"(exit 遷移用の中間状態)は発行しません(Toaster に閉鎖・退出中間状態が存在せず、描画された root は構造上つねに open のため)。
  • root に tabindex="0" を追加: Zag・Radix 双方が持ちます。
  • group に tabindex="-1" を追加: Zag group・Radix viewport 双方が持ちます。
  • group ラベルの既定値: Toaster::view が aria-label="" を出していた不具合を DEFAULT_GROUP_LABEL("Notifications")で是正しました。

action-trigger に data-disabled を発行するかは Themes 側イシュー #1544/#1545 が保留していた判断で、本イシューで発行しないことを確定しました(Zag の getActionTriggerProps は type="button" のみを発行し、ネイティブ disabled 属性を呼び出し側 attrs 経由で渡す運用を維持します)。

意図的に合わせていない点:

  • Zag のスタック・遷移・一時停止系 data-*(data-mounted/data-paused/ data-first/data-sibling/data-stack/data-overlap)と Radix のスワイプ系(data-swipe/data-swipe-direction)は装飾・アニメーション・ジェスチャ計測の関心であり headless 層へ持ち込みません。
  • data-placement/data-side/data-align は group のみに付与し、Zag のように root へは重複付与しません。
  • aria-live は root に置く方針を維持します(Zag は group に置きますが、本クレートは Radix 準拠の既存方針を維持します)。
  • root の aria-labelledby/aria-describedby、title/description の自動 id は付与しません(role="status" + aria-atomic="true" で通知全体が読み上げられるため必須ではなく、Radix も付けません)。
  • close-trigger の既定 aria-label(Zag の "Dismiss notification")は付与しません(headless-ui の Anatomy::part は呼び出し側 attrs を後置追記するため、固定既定値は利用者指定と二重出力になります。アイコンのみの close-trigger には利用者が attrs で aria-label を渡してください)。
  • ToastStatus への loading variant 追加(Zag/chakra)は公開 enum の破壊的変更のため見送りました。
  • chakra の Indicator(アイコン)パートは装飾であり headless には置きません(Themes 候補として記録)。
  • 要素型は Radix の ol/li ではなく ark-ui と同じ div を維持します。

上記の data-state/tabindex の追加は Themes(fandhe-frontend-pre-styled-ui)側イシュー #1543/#1545 へコメントで共有しました。Themes 側の CSS 変更は不要ですが、exit 遷移(data-state="closed")は fandhe-frontend-wasm-full の dismiss 配線待ちのまま保留です。

スコープ外候補(是正対象ではなく、別途起票を検討する余地がある事項):

  • fandhe-frontend-wasm-full 側の toast 配線(close/action-trigger → "dismiss" dispatch、Escape での閉鎖、ホットキー F8/Alt+T によるリージョンフォーカス、タイマーによる自動 dismiss と hover/focus による一時停止、exit 遷移用 data-state="closed" の追加)
  • ToastStatus::Loading variant
  • Themes 側 Indicator パート

スタイル済みの表示例は Toast を参照してください。

関連 API: fandhe-frontend-headless-ui API

自前 CSS の最小例

[data-scope="toast"][data-part="..."] セレクタでスタイルを当てます。以下は root の枠・影、Error 状態の配色、open 状態のフェードイン、group の固定配置、close-trigger のフォーカスリングの最小例です。

CSS
[data-scope="toast"][data-part="root"] {
  border: 1px solid #888;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}

[data-scope="toast"][data-part="root"][data-type="error"] {
  border-color: #dc2626;
  background: #fef2f2;
}

[data-scope="toast"][data-part="root"][data-state="open"] {
  animation: fd-toast-fade-in 150ms ease-out;
}

@keyframes fd-toast-fade-in {
  from { opacity: 0; transform: translateY(4px); }
  to { opacity: 1; transform: translateY(0); }
}

[data-scope="toast"][data-part="group"][data-placement="bottom-end"] {
  position: fixed;
  bottom: 16px;
  right: 16px;
}

[data-scope="toast"][data-part="close-trigger"]:focus-visible {
  outline: 2px solid #2563eb;
  outline-offset: 2px;
}

Demo

枠線・余白は docs サイト側のデモ枠であり、fandhe-frontend-headless-ui 自体はスタイルを持ちません。

Saved
Your changes have been saved.
Upload failed
Network connection was lost.

Features

  • group(live region)/ root(通知 1 件)/ title/description/action-trigger/close-trigger の 6 anatomy パーツと、複数通知を有界なキューとして管理する状態機械 Toaster を提供する(dispatch: dismiss/clear)。イシュー #1643 で Zag.js toast.connect.ts(ark-ui の実体)・Radix Primitives Toast・chakra-ui v3 Toast と突合し、6 パートの増減なしで一致することを確認した。
  • aria-live は ToastStatus から決定的に導出する(呼び出し側文字列を直接流し込まない)。Error のみ "assertive"、他は "polite"。aria-atomic="true" を併用し、通知全体を単位として読み上げさせる(Zag は group 側に polite を出すが、本クレートは Radix 準拠で root に置く既存方針を #1643 で維持確定)。
  • group は role="region" + aria-label(label は必須引数)+ tabindex="-1" を固定付与する(tabindex は #1643 で追加。Zag/Radix ともプログラム的フォーカス先として持つが、本クレートはホットキー未配線のため Tab 順には入らない)。
  • root は role="status" + aria-atomic="true" + aria-live + data-type に加え、data-state="open"(固定値のみ)・tabindex="0" を発行する(#1643 で追加。Zag/Radix ともに持つ属性で、キーボード利用者が Tab で通知本体へ到達できる)。exit 遷移用の data-state="closed" は発行しない。
  • action-trigger は data-disabled を発行しない(#1643 で確定。ネイティブ disabled 属性を呼び出し側 attrs 経由で渡す運用)。
  • タイマーによる自動 dismiss・Escape での閉鎖・ホットキーによるリージョンフォーカス(Radix F8 / Zag Alt+T)の実配線は fandhe-frontend-wasm-full 側の後続イシューのスコープ。

Anatomy

group
  root
    title
    description
    action-trigger
    close-trigger

API Reference

Arguments

NameTypeDefaultDescription
group: placementToastPlacementBottomEnddata-placement へ反映するビューポート角配置(6 語彙)。
group: label&str—role="region" とセットの aria-label(呼び出し側が必ず指定する)。
root: statusToastStatusInfodata-type と aria-live の緊急度(Error のみ assertive、他は polite)を導出する状態。

Data Attributes

PartAttributeObserved Values
groupdata-placementbottom-end
rootdata-stateopen
rootdata-typeerror, success

Examples

Error status

ToastStatus::Error の Toast は aria-live="assertive" になり、他の状態より即座に割り込んで読み上げられます。

Upload failed
Network connection was lost.

Custom CSS

data-scope / data-part / data-type / data-state / data-placement セレクタで自前 CSS を当てる最小例です。

利用者が data-scope / data-part / data-type / data-state / data-placement セレクタで自前 CSS を当てる最小例です。headless-ui 自体はスタイルを持ちません。

Upload failed
Network connection was lost.
CSS
[data-scope="toast"][data-part="root"] {
  border: 1px solid #888;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
[data-scope="toast"][data-part="root"][data-type="error"] {
  border-color: #dc2626;
  background: #fef2f2;
}
[data-scope="toast"][data-part="root"][data-state="open"] {
  animation: fd-toast-fade-in 150ms ease-out;
}
@keyframes fd-toast-fade-in {
  from { opacity: 0; transform: translateY(4px); }
  to { opacity: 1; transform: translateY(0); }
}
[data-scope="toast"][data-part="group"][data-placement="bottom-end"] {
  position: fixed;
  bottom: 16px;
  right: 16px;
}
[data-scope="toast"][data-part="close-trigger"]:focus-visible {
  outline: 2px solid #2563eb;
  outline-offset: 2px;
}

Accessibility

Keyboard Interactions

KeyDescription
Tab / Shift+Tabroot(tabindex="0")→ action-trigger → close-trigger の順にブラウザ標準でフォーカス到達する。Radix の F8 / Zag の Alt+T によるリージョンフォーカスは未配線。
Enter / Space (action-trigger / close-trigger)ネイティブ button の click が発火する。click から "dismiss" dispatch への配線(fandhe-frontend-wasm-full の MAPPING_TABLE)は未実装で、Escape での閉鎖も同様。

WAI-ARIA

AttributeDescription
role="region" / aria-label / tabindex="-1"group に固定付与(tabindex は #1643 で追加)。
role="status" / tabindex="0"root に固定付与(tabindex は #1643 で追加)。
aria-atomic="true"root に固定付与。通知全体を単位として読み上げさせる。
aria-liveroot に付与。status から決定的に導出される(Error のみ assertive、他は polite)。Zag は group に置くが本クレートは Radix 準拠で root 側の既存方針を維持する(#1643)。
data-state="open"root に固定発行(#1643 で追加)。"closed"(exit 遷移用)は発行しない。
close-trigger の aria-label既定値は付与しない。アイコンのみの close-trigger には呼び出し側が attrs で aria-label を渡すこと(#1643)。