Basic Modal
A titled dialog with a focus trap, Escape to close and focus returned to the trigger.
6 個のフレームワーク中級
A base dialog that raises a second dialog on top, peeled one layer at a time by Escape.
<!--
Two dialogs, stacked. The second sits at a higher z-index with its own backdrop,
and Escape only ever closes the TOPMOST layer - so the user peels the stack one
at a time rather than being dumped back to the page. Closing the top layer
returns focus to the control that raised it. This markup shows both layers open;
the React versions manage the second with state.
-->
<div class="fixed inset-0 z-40 flex items-center justify-center p-4">
<div class="absolute inset-0 bg-black/50"></div>
<div role="dialog" aria-modal="true" aria-labelledby="base-modal-title" class="relative max-h-[calc(100dvh-2rem)] w-full max-w-md overflow-y-auto rounded-2xl border border-gray-200 bg-white p-6 shadow-2xl dark:border-gray-800 dark:bg-gray-900">
<h2 id="base-modal-title" class="text-lg font-semibold text-gray-900 dark:text-gray-100">Account settings</h2>
<p class="mt-2 text-sm leading-relaxed text-gray-600 dark:text-gray-400">Manage your account. Some actions here are permanent and will ask you to confirm.</p>
<div class="mt-6 flex flex-col gap-2 sm:flex-row sm:justify-between">
<button type="button" data-modal-open-confirm class="w-full rounded-lg border border-red-300 bg-white px-4 py-2 text-sm font-semibold text-red-700 hover:bg-red-50 sm:w-auto dark:border-red-900 dark:bg-gray-900 dark:text-red-400 dark:hover:bg-red-950">Delete account</button>
<button type="button" data-modal-close class="w-full rounded-lg bg-blue-600 px-4 py-2 text-sm font-semibold text-white hover:bg-blue-700 sm:w-auto">Done</button>
</div>
</div>
</div>
<div class="fixed inset-0 z-50 flex items-center justify-center p-4">
<div class="absolute inset-0 bg-black/50" data-modal-close-confirm></div>
<div role="alertdialog" aria-modal="true" aria-labelledby="confirm2-modal-title" class="relative max-h-[calc(100dvh-2rem)] w-full max-w-sm overflow-y-auto rounded-2xl border border-gray-200 bg-white p-6 text-center shadow-2xl dark:border-gray-800 dark:bg-gray-900">
<h2 id="confirm2-modal-title" class="text-lg font-semibold text-gray-900 dark:text-gray-100">Delete account?</h2>
<p class="mt-2 text-sm leading-relaxed text-gray-600 dark:text-gray-400">This removes everything tied to your account and cannot be undone.</p>
<div class="mt-6 flex gap-2">
<button type="button" data-modal-close-confirm class="flex-1 rounded-lg border border-gray-300 bg-white px-4 py-2 text-sm font-semibold text-gray-700 hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 dark:hover:bg-gray-800">Cancel</button>
<button type="button" class="flex-1 rounded-lg bg-red-600 px-4 py-2 text-sm font-semibold text-white hover:bg-red-700">Delete</button>
</div>
</div>
</div>| Prop | 型 | デフォルト | 説明 |
|---|---|---|---|
open必須 | boolean | - | コンポーネントを表示するかどうか。 |
onDismiss必須 | () => void | - | ユーザーが通知を閉じたときに呼ばれます。 |
Two rules keep a stack honest: the second overlay sits at a higher z-index with its own backdrop, and Escape only ever closes the topmost layer - so the user peels the stack one at a time instead of being dumped straight back to the page. Closing the top layer returns focus to the control that raised it, captured on open, not to the base dialog's first field. The base trap is suppressed while the confirm is up so the two do not fight over Tab.