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 / Space | Trigger | ダイアログを開きます(fandhe-frontend-wasm-full の headless part → action 対応表が "toggle" を dispatch)。 |
| Enter / Space | CloseTrigger | ダイアログを閉じます(同対応表が "close" を dispatch)。 |
| Escape | Content | ダイアログを閉じます。role="alertdialog" でも Escape は閉じます(外側クリックのみ alertdialog は既定で無効)。data-close-on-escape="false" で無効化できます。 |
| Tab / Shift+Tab | Content | aria-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] ガードを必ず含めます。
[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 自体はスタイルを持ちません。
Confirm action
This cannot be undone.
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-triggerAPI Reference
Arguments
| Name | Type | Default | Description |
|---|---|---|---|
root: state | OpenState | Closed | ダイアログ全体の開閉状態(data-state へ反映)。 |
trigger: state | OpenState | Closed | aria-expanded/data-state へ反映される開閉状態。 |
trigger: controls | Option<&str> | — | Some のとき aria-controls で content と関連付ける。 |
backdrop: state | OpenState | Closed | closed のとき hidden 存在属性を付与する背面レイヤーの開閉状態。 |
positioner: state | OpenState | Closed | closed のとき hidden 存在属性を付与する配置ラッパーの開閉状態。 |
content: state | OpenState | Closed | 開閉状態(closed のとき hidden 存在属性を付与)。 |
content: role_kind | DialogRole | — | role 属性の値(Dialog="dialog" / Alertdialog="alertdialog")。 |
content: modal | bool | — | aria-modal へ反映する値。 |
content: ids | ContentIds | ContentIds::default() | content 自身の id・aria-labelledby・aria-describedby をまとめた構造体(各フィールドが Some のときのみ対応する属性を出力)。 |
title: id | Option<&str> | — | content の labelledby と対で aria-labelledby 関連付けを成立させる。 |
description: id | Option<&str> | — | content の describedby と対で aria-describedby 関連付けを成立させる。 |
close_trigger_with_variant: variant | CloseTriggerVariant | Icon | data-variant(icon/text)へ反映する見た目バリアント(イシュー #2193)。text は可視テキストでアクセシブルネームを持つため aria-label は不要。既存 close_trigger(variant 引数なし)は data-variant を出力しない。 |
Data Attributes
| Part | Attribute | Observed Values |
|---|---|---|
backdrop | data-state | open |
close-trigger | data-variant | text |
content | data-state | open |
positioner | data-state | open |
root | data-state | open |
trigger | data-state | open |
Examples
Alert dialog (non-modal)
DialogRole::Alertdialog + modal=false の組み合わせで、role="alertdialog" かつ aria-modal="false" を出力する例です。
Delete this item?
Accessibility
Keyboard Interactions
| Key | Description |
|---|---|
| Enter / Space (trigger) | ネイティブ button 要素の click 相当が成立し、fandhe-frontend-wasm-full の headless part → action 対応表が "toggle" を dispatch する。 |
| Enter / Space (close-trigger) | 同対応表が "close" を dispatch する。 |
| Escape | overlay::close_on_escape_for が "close" を通知する(role="alertdialog" でも Escape は閉じる。外側クリックのみ alertdialog は既定で無効)。data-close-on-escape="false" で無効化できる。 |
| Tab / Shift+Tab | aria-modal="true" のとき focus_trap::should_trap が content 内でフォーカスを循環させる。data-autofocus で初期フォーカス先を指定でき、tabbable な子が無い場合は content 自身(tabindex="-1")へフォーカスする。 |
| (閉鎖時) | focus_trap::push_trap が push 時点でフォーカスされていた要素(取得不能なら trigger 引数)をスナップショットしており、pop_trap がその要素へフォーカスを復帰する。 |
WAI-ARIA
| Attribute | Description |
|---|---|
| aria-haspopup="dialog" | trigger に固定付与。 |
| aria-expanded | trigger に付与。開閉状態を反映する。 |
| aria-controls | trigger に付与。controls が Some のとき content と関連付ける。 |
| aria-hidden="true" | backdrop に固定付与(装飾層のため読み上げ対象外)。 |
| role="dialog" / role="alertdialog" | content に付与。role_kind の値を反映する。 |
| aria-modal | content に付与。modal 引数の値を反映する。 |
| aria-labelledby / aria-describedby | content に付与。ids.labelledby/ids.describedby が Some のときのみ出力される。 |
| tabindex="-1" | content に固定付与(zag dialog.connect.ts の getContentProps と同じく、プログラム的フォーカスのみを許可する)。 |