Dialog
Dialogs
A native <dialog> element with modal and non-modal semantics, icon/ title/content/actions layout, and adaptive width sizing.
Non-modal: the rest of the page stays interactive while this is open.
No title here: the icon and body carry the message on their own.
import { useState } from 'react'
import { Button, Dialog, Icon, Surface, Text } from '@language-lit/material3-expressive'
export function DialogExample() {
const [confirmOpen, setConfirmOpen] = useState(false)
const [customOpen, setCustomOpen] = useState(false)
const [nonModalOpen, setNonModalOpen] = useState(false)
const [titlelessOpen, setTitlelessOpen] = useState(false)
return (
<Surface
as="section"
aria-labelledby="dialog-example-title"
color="surface-container-low"
shape="extra-large"
className="dialog-example"
>
<Text as="h2" id="dialog-example-title" variant="titleLarge" emphasis="emphasized">
Dialogs
</Text>
<Text as="p" variant="bodyMedium">
A native <dialog> element with modal and non-modal semantics, icon/
title/content/actions layout, and adaptive width sizing.
</Text>
<div className="dialog-example__row">
<Button variant="filled" onClick={() => setConfirmOpen(true)}>
Open confirmation
</Button>
<Button variant="outlined" onClick={() => setCustomOpen(true)}>
Open custom content
</Button>
<Button variant="tonal" onClick={() => setNonModalOpen(true)}>
Open non-modal
</Button>
<Button variant="tonal" onClick={() => setTitlelessOpen(true)}>
Open title-less
</Button>
</div>
<Dialog
open={confirmOpen}
onOpenChange={setConfirmOpen}
role="alertdialog"
icon={<Icon source="delete" />}
title="Delete conversation?"
actions={
<>
<Button variant="text" onClick={() => setConfirmOpen(false)}>
Cancel
</Button>
<Button variant="text" onClick={() => setConfirmOpen(false)}>
Delete
</Button>
</>
}
>
This removes the conversation and its messages. This action cannot be
undone.
</Dialog>
<Dialog
open={customOpen}
onOpenChange={setCustomOpen}
title="Rename item"
actions={
<form method="dialog">
<Button variant="text" type="submit">
Save
</Button>
</form>
}
>
<label className="dialog-example__field">
Name
<input type="text" defaultValue="Untitled" />
</label>
</Dialog>
<Dialog
open={nonModalOpen}
onOpenChange={setNonModalOpen}
modal={false}
title="Playback controls"
>
<Text as="p" variant="bodyMedium">
Non-modal: the rest of the page stays interactive while this is open.
</Text>
<div className="dialog-example__row">
<Button variant="text" onClick={() => setNonModalOpen(false)}>
Close
</Button>
</div>
</Dialog>
<Dialog
open={titlelessOpen}
onOpenChange={setTitlelessOpen}
icon={<Icon source="check_circle" />}
aria-label="Changes saved"
actions={
<Button variant="text" onClick={() => setTitlelessOpen(false)}>
Done
</Button>
}
>
<Text as="p" variant="bodyMedium">
No title here: the icon and body carry the message on their own.
</Text>
</Dialog>
</Surface>
)
}Dialogはネイティブの<dialog>要素を描画し、その上にMaterialのアイコン、タイトル、コンテンツ、アクションのレイアウトを表示します。ほかの状態を持つコンポーネントと同様に開閉状態を管理します。モーダル/非モーダルの動作、バックドロップ、フォーカストラップ、フォーカスのライフサイクルはすべてブラウザーが担います。
import { Button, Dialog, Icon } from '@language-lit/material3-expressive'
import '@language-lit/material3-expressive/styles.css'
<Dialog
open={open}
onOpenChange={setOpen}
role="alertdialog"
icon={<Icon source="delete" />}
title="Delete conversation?"
actions={
<>
<Button variant="text" onClick={() => setOpen(false)}>Cancel</Button>
<Button variant="text" onClick={() => setOpen(false)}>Delete</Button>
</>
}
>
This removes the conversation and its messages. This action cannot be undone.
</Dialog>仕様
open/defaultOpen/onOpenChangeは、ほかの状態を持つコンポーネントと同じ制御/非制御の形式です。Escape、外側のクリック、ネイティブの<form method="dialog">送信でダイアログが閉じたときにonOpenChangeを呼び出します。利用側がプログラムからopenを変更した場合には呼び出しません。icon、title、本文/補足テキスト領域のchildren、actionsはすべて任意の名前付き領域で、この順に描画されます。省略した領域は余分な間隔を残しません。roleの既定値は"dialog"です。中断を伴う確認には"alertdialog"を指定できます。classNameとstyleはルートの<dialog>を対象とします。refも同じ要素を参照します。
制御されたダイアログがネイティブ操作で閉じた場合は、必ずonOpenChangeで通知します。ただしコールバックを無視しても強制的に開き直しません。ハンドラーが呼ばれた時点ですでにネイティブのclose処理が完了しているためです。ほかの制御/非制御コンポーネントと同様に、onOpenChangeで新しい値を確定してください。
モーダルと非モーダル
<Dialog open={open} onOpenChange={setOpen} modal={false} title="Playback controls">
…
</Dialog>modal | ネイティブ呼び出し | バックドロップ | フォーカストラップ | 背景 |
|---|---|---|---|---|
true(既定値) | showModal() | あり | あり | 操作不可 |
false | show() | なし | なし | 操作可能 |
dismissOnEscape(既定値true)とdismissOnOutsideClick(既定値true)が適用されるのはモーダルモードだけです。非モーダルダイアログにはクリック可能なバックドロップがなく、ネイティブの既定動作ではEscapeでも閉じません。
アクセシブルな名前と説明
titleがある場合は、明示的にaria-labelまたはaria-labelledbyを渡していない限り、生成したIDをaria-labelledbyに設定します。childrenがある場合も同様にaria-describedbyを設定します。title、aria-label、aria-labelledbyのいずれかを指定してください。アクセシブルな名前がない場合は開発時に警告します。
初期フォーカスの移動と、ダイアログを閉じたときに開く前のフォーカス位置へ戻す動作は、modalの値にかかわらずネイティブの<dialog>が担います。設定が必要なライブラリ独自のフォーカス管理コードはありません。
フォーム
<Dialog open={open} onOpenChange={setOpen} title="Rename item"
actions={
<form method="dialog">
<Button variant="text" type="submit">Save</Button>
</form>
}
>
<label>
Name
<input type="text" defaultValue="Untitled" />
</label>
</Dialog>ダイアログ内で<form method="dialog">を送信すると、追加の接続処理なしにダイアログが閉じ、onOpenChange(false)が呼び出されます。Escapeや外側のクリックと同じネイティブのcloseイベントを通じて利用側に通知されます。
サイズ
ダイアログ自体の幅は280pxから560pxの範囲で、左右の余白を保ちながらビューポートに合わせて調整されます。非常に長いコンテンツはビューポートからはみ出さず、ダイアログ内でスクロールします。全画面用やブレークポイントで切り替わる別バリアントはありません。このコンテンツに合わせた範囲内のサイズが適応動作です。
トークンと境界
色、形状、モーション値はすべて1つの--m3e-comp-dialog-*登録にまとめています。テーマの上書きはMaterial3Provider内に限定されます。Dialogは実行時スタイルを挿入しません。Next.js、Vite、ルーター、アニメーションライブラリ、アプリケーション独自のコードは読み込みません。