/* * ``: M3's basic and full-screen dialogs (DialogTokens.kt, FullScreenDialogTokens.kt, * androidx Compose Material 3, Apache-2.0; docs/reference/m3/ * components-actions-communication-containment.md § Dialogs → Specs). * * [data-md-modal] the ; data-md-fullscreen while `fullscreen` holds * [data-md-modal-box] surface-container-high, extra-large corner, elevation 3 * [data-md-modal-bar] the 56px phone-only header bar (fullscreen, below 600px) * [data-md-modal-head] title/subtitle/icon, pinned * [data-md-modal-body] the only part that scrolls; [data-md-modal-content] wraps the slot * [data-md-modal-actions] pinned, trailing-aligned * * 560/280px width (min/max), 48px scrim margin, 28px corner and 24dp padding all match the specs * page; a compact window's full-screen dialog swaps the box for the whole screen with a 56px bar * (C-17: this used to be 64px) instead of the headline block. It opens on the fast spatial spring * and closes at once — a native `` makes an exit transition awkward, since * `@starting-style` only ever supplies an entry. * * The dividers (§ Dialogs → Anatomy, C-06): a 1px outline-variant rule under the pinned head and * over the pinned actions, each only while the body has more content hidden on its side. * resources/js/dialog.js marks the `` with data-md-overflow-top/-bottom as the body * scrolls; `data-md-modal-divider` says an element takes part at all — the head and the actions * always do while a body exists (structural: whichever follows or precedes it), the phone bar * only when it is the row actually sitting above the body there (computed in the view, because a * subtitle or an icon can keep the head on screen too, in which case the bar must not draw a rule * the head already owns); `data-md-separator` (the `separator` prop) draws the rule whatever the * scroll. The rule is a pseudo-element over the row's own edge, so showing it moves nothing. * * `[data-md-modal-body]:focus-visible` is Chrome's own focusable scroll container catching the * dialog's first focus when nothing inside can take it — M3's 3px secondary indicator, drawn * inside the edge because the box's rounded, overflow-hidden corner would clip one drawn outside. */ @layer material.reset, material.tokens, material.base, material.layout, material.components, material.text, material.visibility; @import './icon.css'; @import './button.css'; @layer material.components { [data-md-modal] { margin: auto; max-width: 560px; min-width: 280px; width: calc(100vw - 48px); max-height: calc(100dvh - 48px); overflow: visible; padding: 0; background-color: transparent; color: var(--md-sys-color-on-surface); opacity: 1; scale: 1; transition-property: opacity, scale; transition-duration: var(--md-sys-motion-spatial-fast-duration); transition-timing-function: var(--md-sys-motion-spatial-fast); } @starting-style { [data-md-modal] { opacity: 0; scale: 0.95; } } [data-md-modal]::backdrop { background-color: color-mix(in srgb, var(--md-sys-color-scrim) 32%, transparent); } @media (width < 600px) { [data-md-modal][data-md-fullscreen] { margin: 0; width: 100%; max-width: none; min-width: 0; height: 100dvh; max-height: none; } } [data-md-modal-box] { display: flex; max-height: inherit; flex-direction: column; overflow: hidden; border-radius: var(--md-sys-shape-corner-xl); background-color: var(--md-sys-color-surface-container-high); box-shadow: var(--md-sys-elevation-3); } @media (width < 600px) { [data-md-modal][data-md-fullscreen] > [data-md-modal-box] { height: 100%; border-radius: var(--md-sys-shape-corner-none); padding-top: var(--material-safe-top, env(safe-area-inset-top)); } } /* The phone-only header bar: M3's full-screen dialog header, 56dp (C-17). */ [data-md-modal-bar] { display: flex; height: var(--md-sys-measurement-space700); flex-shrink: 0; align-items: center; gap: var(--md-sys-measurement-space50); padding-inline: var(--md-sys-measurement-space50); } @media (width >= 600px) { [data-md-modal-bar] { display: none; } } [data-md-modal-bar-title] { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font: var(--md-sys-typescale-title-lg); letter-spacing: var(--md-sys-typescale-title-lg-tracking); } [data-md-modal-head] { flex-shrink: 0; padding-inline: var(--md-sys-measurement-space300); padding-top: var(--md-sys-measurement-space300); } [data-md-modal-head]:has(+ [data-md-modal-body]) { padding-bottom: var(--md-sys-measurement-space100); } [data-md-modal-head]:has([data-md-icon]) { text-align: center; } /* On a phone the bar already names a plain dialog; the head hides unless an icon or a subtitle keeps it there (the bar cannot hold either). */ @media (width < 600px) { [data-md-modal][data-md-fullscreen] [data-md-modal-head]:not(:has([data-md-icon])):not(:has([data-md-modal-subtitle])) { display: none; } } [data-md-modal-head] > [data-md-icon] { margin-inline: auto; margin-bottom: var(--md-sys-measurement-space200); color: var(--md-sys-color-secondary); } [data-md-modal-title] { font: var(--md-sys-typescale-headline-sm); letter-spacing: var(--md-sys-typescale-headline-sm-tracking); } @media (width < 600px) { [data-md-modal][data-md-fullscreen]:not(:has([data-md-modal-head] [data-md-icon])) [data-md-modal-title] { display: none; } } [data-md-modal-subtitle] { color: var(--md-sys-color-on-surface-variant); font: var(--md-sys-typescale-body-md); letter-spacing: var(--md-sys-typescale-body-md-tracking); } [data-md-modal-title] ~ [data-md-modal-subtitle], [data-md-modal-head] > [data-md-icon] ~ [data-md-modal-subtitle] { margin-top: var(--md-sys-measurement-space200); } @media (width < 600px) { [data-md-modal][data-md-fullscreen]:not(:has([data-md-modal-head] [data-md-icon])) [data-md-modal-subtitle] { margin-top: 0; } } [data-md-modal-body] { min-height: 0; flex: 1 1 0%; overflow-y: auto; padding-inline: var(--md-sys-measurement-space300); padding-top: var(--md-sys-measurement-space300); padding-bottom: var(--md-sys-measurement-space300); color: var(--md-sys-color-on-surface-variant); font: var(--md-sys-typescale-body-md); letter-spacing: var(--md-sys-typescale-body-md-tracking); outline: none; } [data-md-modal-body]:focus-visible { outline: 3px solid var(--md-sys-color-secondary); outline-offset: -3px; } [data-md-modal-head] ~ [data-md-modal-body] { padding-top: var(--md-sys-measurement-space100); } @media (width < 600px) { [data-md-modal][data-md-fullscreen] [data-md-modal-head]:not(:has([data-md-icon])):not(:has([data-md-modal-subtitle])) ~ [data-md-modal-body] { padding-top: var(--md-sys-measurement-space200); } } [data-md-modal-body]:has(~ [data-md-modal-actions]) { padding-bottom: var(--md-sys-measurement-space100); } [data-md-modal-actions] { display: flex; flex-shrink: 0; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--md-sys-measurement-space100); padding-inline: var(--md-sys-measurement-space300); padding-top: var(--md-sys-measurement-space300); padding-bottom: var(--md-sys-measurement-space300); } [data-md-modal-body] ~ [data-md-modal-actions] { padding-top: var(--md-sys-measurement-space200); } @media (width < 600px) { [data-md-modal][data-md-fullscreen] [data-md-modal-actions] { min-height: var(--md-sys-measurement-space700); padding-top: var(--md-sys-measurement-space100); padding-bottom: var(--md-sys-measurement-space100); } } /* The pinned-edge dividers: a pseudo-element over the row's own padding, drawn only while something is scrolled past it (or `separator` says always). */ [data-md-modal-head], [data-md-modal-bar], [data-md-modal-actions] { position: relative; } [data-md-modal-head]::after, [data-md-modal-bar]::after, [data-md-modal-actions]::before { content: ''; position: absolute; inset-inline: 0; height: 1px; background-color: var(--md-sys-color-outline-variant); opacity: 0; pointer-events: none; transition: opacity var(--md-sys-motion-effects-fast-duration) var(--md-sys-motion-effects-fast); } [data-md-modal-head]::after, [data-md-modal-bar]::after { bottom: 0; } [data-md-modal-actions]::before { top: 0; } dialog[data-md-overflow-top] > [data-md-modal-box] > [data-md-modal-head][data-md-modal-divider]::after, dialog[data-md-overflow-top] > [data-md-modal-box] > [data-md-modal-bar][data-md-modal-divider]::after, dialog[data-md-overflow-bottom] > [data-md-modal-box] > [data-md-modal-actions][data-md-modal-divider]::before, [data-md-modal-head][data-md-modal-divider][data-md-separator]::after, [data-md-modal-bar][data-md-modal-divider][data-md-separator]::after, [data-md-modal-actions][data-md-modal-divider][data-md-separator]::before { opacity: 1; } }