Skip to content
fandhe-frontendcore v0.4.3
GitHub

Dialog

モーダルダイアログです。fandhe-frontend-headless-ui の dialog mod は Root / Trigger / Backdrop / Positioner / Content / Title / Description / CloseTrigger の 8 anatomy パーツと、DialogRole(Dialog/Alertdialog)で切り替えられる role・aria-modal・aria-haspopup="dialog" を提供します。本部品は SSR での属性出力と状態機械のみを担い、Escape キーでの閉鎖・外側クリックでの閉鎖・フォーカストラップ・閉鎖時の trigger へのフォーカス復帰・click → dispatch 配線は fandhe-frontend-wasm-full が実 DOM 配線を担います。content は tabindex="-1" を固定付与します(プログラム的フォーカスのみを許可する WAI-ARIA dialog パターンの前提)。

キーボード操作

キー対象効果
Enter / SpaceTriggerダイアログを開きます(fandhe-frontend-wasm-full の headless part → action 対応表が "toggle" を dispatch)。
Enter / SpaceCloseTriggerダイアログを閉じます(同対応表が "close" を dispatch)。
EscapeContentダイアログを閉じます。role="alertdialog" でも Escape は閉じます(外側クリックのみ alertdialog は既定で無効)。data-close-on-escape="false" で無効化できます。
Tab / Shift+TabContentaria-modal="true" のとき content 内でフォーカスを循環させます(フォーカストラップ)。data-autofocus で初期フォーカス先を指定でき、tabbable な子が無い場合は content 自身(tabindex="-1")へフォーカスします。
(閉鎖時)—フォーカスはフォーカストラップ開始時点でフォーカスされていた要素(取得不能なら trigger)へ復帰します。

参考サイトとの差分

ark-ui(zag dialog.connect.ts)・Radix Primitives・chakra-ui と突合し、content の tabindex="-1" 固定付与を追加しました。anatomy(8 パート)・data-state 語彙(open/closed)は一致しており、パート・data-* の増減はありません。一方、以下は意図的に合わせていません。

  • DOM 上の root パート: zag の Dialog.Root は context のみで DOM を持ちませんが、本リポジトリの全部品が root を DOM 要素として持つ規約(data-state の付与先)のため維持しています。
  • positioner の data-state + hidden: zag は pointer-events のインラインスタイルで代替しますが、headless-ui はスタイルを出力しないため JS なしの SSR での閉状態表現として維持しています。
  • trigger の data-ownedby / data-value / data-current(zag の複数トリガー識別): aria-controls による id 関連付けが同等の役割を担うため不採用です。
  • content の data-nested / data-has-nested(ark-ui のネストダイアログ実行時計測): レイアウト計測・実行時関心は headless へ持ち込まない方針(docs/policy/intentional-non-adoption.md §3.25 規則 2)のため不採用です。
  • Radix Portal: DOM 配置の関心のため不採用です。
  • Radix AlertDialog の Cancel / Action パート: DialogRole::Alertdialog + CloseTrigger + 素の button で構成でき、ark-ui にも該当パートは無いため不採用です。

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

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

自前 CSS の最小例

Themes 版を使わず本部品を直接使う場合、[data-scope="dialog"][data-part="..."] セレクタでスタイルを当てます。以下は backdrop・positioner(中央寄せ)・content(枠・余白)の最小例です。author スタイルの display が UA の [hidden] { display: none } を上書きして closed 状態が見えてしまわないよう、[hidden] ガードを必ず含めます。

CSS
[data-scope="dialog"][data-part="backdrop"] {
  position: fixed;
  inset: 0;
  background: rgb(0 0 0 / 0.4);
}

[data-scope="dialog"][data-part="backdrop"][hidden] {
  display: none;
}

[data-scope="dialog"][data-part="positioner"] {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

[data-scope="dialog"][data-part="positioner"][hidden] {
  display: none;
}

[data-scope="dialog"][data-part="content"] {
  background: #fff;
  border-radius: 8px;
  padding: 1.5rem;
  max-width: 28rem;
}

[data-scope="dialog"][data-part="content"][hidden] {
  display: none;
}

Demo

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

Features

  • Root / Trigger / Backdrop / Positioner / Content / Title / Description / CloseTrigger の 8 anatomy パーツと、Disclosure を埋め込んだ開閉状態機械 Dialog を提供する(dispatch: open/close/toggle)。
  • trigger は type="button" を固定付与し、aria-haspopup="dialog" を常に付与する。
  • content は DialogRole(Dialog/Alertdialog)で role を切り替えられ、modal 引数で aria-modal の値を制御できる。content は tabindex="-1" を固定付与する(zag dialog.connect.ts と同じく、プログラム的フォーカスのみを許可する前提。イシュー #1638)。
  • backdrop/positioner は closed のとき hidden 存在属性を付与し、JS なしの SSR でも閉状態を表現する。
  • Escape キー閉鎖・外側クリック閉鎖・フォーカストラップ・閉鎖時の trigger へのフォーカス復帰・click → dispatch 配線は本モジュールが属性を出力するのみで、実 DOM 配線は fandhe-frontend-wasm-full(overlay/focus_trap/headless の part → action 対応表)が担う。content の attrs 経由で data-close-on-escape="false" / data-close-on-interact-outside="false"("false" リテラルのときのみ無効化)を渡せる。初期フォーカス先を指定する data-autofocus は content ではなく、content 配下の tabbable な対象の子要素へ付与する(fandhe-frontend-wasm-full の focus_trap::collect_tabbable が content の子孫のみを候補として収集するため)。

Anatomy

trigger
root
  backdrop
  positioner
    content
      title
      description
      close-trigger

API Reference

Arguments

NameTypeDefaultDescription
root: stateOpenStateClosedダイアログ全体の開閉状態(data-state へ反映)。
trigger: stateOpenStateClosedaria-expanded/data-state へ反映される開閉状態。
trigger: controlsOption<&str>—Some のとき aria-controls で content と関連付ける。
backdrop: stateOpenStateClosedclosed のとき hidden 存在属性を付与する背面レイヤーの開閉状態。
positioner: stateOpenStateClosedclosed のとき hidden 存在属性を付与する配置ラッパーの開閉状態。
content: stateOpenStateClosed開閉状態(closed のとき hidden 存在属性を付与)。
content: role_kindDialogRole—role 属性の値(Dialog="dialog" / Alertdialog="alertdialog")。
content: modalbool—aria-modal へ反映する値。
content: idsContentIdsContentIds::default()content 自身の id・aria-labelledby・aria-describedby をまとめた構造体(各フィールドが Some のときのみ対応する属性を出力)。
title: idOption<&str>—content の labelledby と対で aria-labelledby 関連付けを成立させる。
description: idOption<&str>—content の describedby と対で aria-describedby 関連付けを成立させる。
close_trigger_with_variant: variantCloseTriggerVariantIcondata-variant(icon/text)へ反映する見た目バリアント(イシュー #2193)。text は可視テキストでアクセシブルネームを持つため aria-label は不要。既存 close_trigger(variant 引数なし)は data-variant を出力しない。

Data Attributes

PartAttributeObserved Values
backdropdata-stateopen
close-triggerdata-varianttext
contentdata-stateopen
positionerdata-stateopen
rootdata-stateopen
triggerdata-stateopen

Examples

Alert dialog (non-modal)

DialogRole::Alertdialog + modal=false の組み合わせで、role="alertdialog" かつ aria-modal="false" を出力する例です。

Delete this item?

Accessibility

Keyboard Interactions

KeyDescription
Enter / Space (trigger)ネイティブ button 要素の click 相当が成立し、fandhe-frontend-wasm-full の headless part → action 対応表が "toggle" を dispatch する。
Enter / Space (close-trigger)同対応表が "close" を dispatch する。
Escapeoverlay::close_on_escape_for が "close" を通知する(role="alertdialog" でも Escape は閉じる。外側クリックのみ alertdialog は既定で無効)。data-close-on-escape="false" で無効化できる。
Tab / Shift+Tabaria-modal="true" のとき focus_trap::should_trap が content 内でフォーカスを循環させる。data-autofocus で初期フォーカス先を指定でき、tabbable な子が無い場合は content 自身(tabindex="-1")へフォーカスする。
(閉鎖時)focus_trap::push_trap が push 時点でフォーカスされていた要素(取得不能なら trigger 引数)をスナップショットしており、pop_trap がその要素へフォーカスを復帰する。

WAI-ARIA

AttributeDescription
aria-haspopup="dialog"trigger に固定付与。
aria-expandedtrigger に付与。開閉状態を反映する。
aria-controlstrigger に付与。controls が Some のとき content と関連付ける。
aria-hidden="true"backdrop に固定付与(装飾層のため読み上げ対象外)。
role="dialog" / role="alertdialog"content に付与。role_kind の値を反映する。
aria-modalcontent に付与。modal 引数の値を反映する。
aria-labelledby / aria-describedbycontent に付与。ids.labelledby/ids.describedby が Some のときのみ出力される。
tabindex="-1"content に固定付与(zag dialog.connect.ts の getContentProps と同じく、プログラム的フォーカスのみを許可する)。