Plan step 36 review of 287340b0. The scrim had no closed opacity, so
allow-discrete held it at full strength for the length of the fade and
then dropped it, where x-transition.opacity had faded it out. The open
state now matches the root's own scrim and panel: a sheet nested in an
open one (a menu's sheet at compact inside a sheet) matched the outer
open rule, and closed in place after half a second instead of sliding.
The handle's ring stands 4px off its bar again, as the old handle's
outline-offset-4 drew it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
168 lines
7.3 KiB
CSS
168 lines
7.3 KiB
CSS
/*
|
||
* `<x-bottom-sheet>`: M3's modal and standard bottom sheets (SheetBottomTokens.kt, androidx
|
||
* Compose Material 3, Apache-2.0; docs/reference/m3/
|
||
* components-actions-communication-containment.md § Bottom sheets → Specs).
|
||
*
|
||
* [data-md-bottom-sheet] the wrapper; data-md-open, data-md-standard
|
||
* [data-md-bottom-sheet-scrim] modal only
|
||
* [data-md-bottom-sheet-probe] hidden, measures a stop's px height for a drag to settle on
|
||
* [data-md-bottom-sheet-panel] surface-container-low, extra-large top corners, elevation 1;
|
||
* data-md-preset while `heights`/`snap` gives it stops
|
||
* [data-md-bottom-sheet-handle] the drag target and M3's 48dp hit area (C-01)
|
||
* [data-md-bottom-sheet-grip] the 32×4px bar, the shared classes' touch target
|
||
* [data-md-bottom-sheet-announce] a live region, only while stops exist
|
||
* [data-md-bottom-sheet-body], [data-md-bottom-sheet-title], [data-md-bottom-sheet-actions]
|
||
*
|
||
* 640px max width, centred; 28px top corners only (`DockedContainerShape` = CornerExtraLargeTop);
|
||
* 50dvh default open height under a ceiling of the screen less M3's 72dp top margin (C-19). It
|
||
* rises on emphasized decelerate and only falls on emphasized accelerate — the asymmetric timing a
|
||
* more specific selector for the open state supplies, since a CSS transition always takes its
|
||
* duration and easing from the *after-change* style (the technique resources/css/components/
|
||
* drawer.css also uses). `data-md-preset` adds the height-between-stops transition on the spatial
|
||
* spring, independent of the open/close slide.
|
||
*
|
||
* The handle's own 22px top/bottom padding (`SheetDefaults.kt`'s `DragHandleVerticalPadding`) is a
|
||
* literal — 22 is not on the measurement scale — matching alert.css's 12px for the same reason.
|
||
* The grip button draws the shared `md-focus-ring` and `md-touch-target` classes rather than a
|
||
* hand-rolled ring: its own visible bar is not a smaller indicator drawn inside a bigger box those
|
||
* classes cannot reach (unlike the datepicker's day) — the box they draw *is* the button's whole
|
||
* hit area. The one refinement is the ring's offset, 4px rather than 2px, as the old handle drew
|
||
* it: 2px from a 4px-tall bar leaves the ring hard to tell from the bar.
|
||
*
|
||
* The scrim fades out as it fades in, and the state rules match the root's own scrim and panel,
|
||
* so a sheet nested in an open one (a menu's sheet at compact inside a sheet) keeps its own state.
|
||
*/
|
||
|
||
@layer material.reset, material.tokens, material.base, material.layout, material.components, material.text, material.visibility;
|
||
|
||
@layer material.components {
|
||
[data-md-bottom-sheet-scrim] {
|
||
position: fixed;
|
||
inset: 0;
|
||
z-index: 40;
|
||
background-color: color-mix(in srgb, var(--md-sys-color-scrim) 32%, transparent);
|
||
opacity: 0;
|
||
transition-property: opacity, display;
|
||
transition-duration: var(--md-sys-motion-effects-default-duration);
|
||
transition-timing-function: var(--md-sys-motion-effects-default);
|
||
transition-behavior: allow-discrete;
|
||
}
|
||
|
||
[data-md-bottom-sheet][data-md-open] > [data-md-bottom-sheet-scrim] {
|
||
opacity: 1;
|
||
}
|
||
|
||
@starting-style {
|
||
[data-md-bottom-sheet][data-md-open] > [data-md-bottom-sheet-scrim] {
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
[data-md-bottom-sheet-probe] {
|
||
position: fixed;
|
||
inset-inline-start: 0;
|
||
top: 0;
|
||
width: 0;
|
||
visibility: hidden;
|
||
pointer-events: none;
|
||
}
|
||
|
||
[data-md-bottom-sheet-panel] {
|
||
position: fixed;
|
||
inset-inline: 0;
|
||
bottom: 0;
|
||
z-index: 50;
|
||
display: flex;
|
||
width: 100%;
|
||
max-width: 640px;
|
||
margin-inline: auto;
|
||
flex-direction: column;
|
||
max-height: var(--sheet-max-height);
|
||
touch-action: pan-y;
|
||
border-radius: var(--md-sys-shape-corner-xl) var(--md-sys-shape-corner-xl) var(--md-sys-shape-corner-none) var(--md-sys-shape-corner-none);
|
||
background-color: var(--md-sys-color-surface-container-low);
|
||
color: var(--md-sys-color-on-surface);
|
||
box-shadow: var(--md-sys-elevation-1);
|
||
padding-bottom: var(--material-safe-bottom, env(safe-area-inset-bottom));
|
||
translate: 0 100%;
|
||
transition-property: translate, display;
|
||
transition-duration: var(--md-sys-motion-effects-default-duration);
|
||
transition-timing-function: var(--md-sys-motion-easing-emphasized-accelerate);
|
||
transition-behavior: allow-discrete;
|
||
}
|
||
|
||
[data-md-bottom-sheet][data-md-open] > [data-md-bottom-sheet-panel] {
|
||
translate: 0 0;
|
||
transition-duration: var(--md-sys-motion-spatial-default-duration);
|
||
transition-timing-function: var(--md-sys-motion-easing-emphasized-decelerate);
|
||
}
|
||
|
||
@starting-style {
|
||
[data-md-bottom-sheet][data-md-open] > [data-md-bottom-sheet-panel] {
|
||
translate: 0 100%;
|
||
}
|
||
}
|
||
|
||
/* With stops the panel is the height of its current one, moving between them on the spatial
|
||
spring, independent of the translate that opens and closes the sheet. */
|
||
[data-md-bottom-sheet-panel][data-md-preset] {
|
||
height: var(--sheet-max-height);
|
||
transition-property: translate, height, display;
|
||
transition-duration: var(--md-sys-motion-effects-default-duration), var(--md-sys-motion-spatial-default-duration), var(--md-sys-motion-effects-default-duration);
|
||
transition-timing-function: var(--md-sys-motion-easing-emphasized-accelerate), var(--md-sys-motion-spatial-default), var(--md-sys-motion-easing-emphasized-accelerate);
|
||
}
|
||
|
||
[data-md-bottom-sheet][data-md-open] > [data-md-bottom-sheet-panel][data-md-preset] {
|
||
transition-duration: var(--md-sys-motion-spatial-default-duration), var(--md-sys-motion-spatial-default-duration), var(--md-sys-motion-spatial-default-duration);
|
||
transition-timing-function: var(--md-sys-motion-easing-emphasized-decelerate), var(--md-sys-motion-spatial-default), var(--md-sys-motion-easing-emphasized-decelerate);
|
||
}
|
||
|
||
[data-md-bottom-sheet-handle] {
|
||
display: flex;
|
||
flex-shrink: 0;
|
||
justify-content: center;
|
||
padding-block: 22px;
|
||
cursor: grab;
|
||
}
|
||
|
||
[data-md-bottom-sheet-handle]:active {
|
||
cursor: grabbing;
|
||
}
|
||
|
||
[data-md-bottom-sheet-grip] {
|
||
width: 32px;
|
||
height: 4px;
|
||
border-radius: var(--md-sys-shape-corner-full);
|
||
background-color: var(--md-sys-color-on-surface-variant);
|
||
}
|
||
|
||
[data-md-bottom-sheet-grip]:focus-visible {
|
||
outline-offset: 4px;
|
||
}
|
||
|
||
[data-md-bottom-sheet-body] {
|
||
min-height: 0;
|
||
flex: 1 1 0%;
|
||
overflow-y: auto;
|
||
padding-inline: var(--md-sys-measurement-space300);
|
||
padding-bottom: var(--md-sys-measurement-space300);
|
||
}
|
||
|
||
[data-md-bottom-sheet-title] {
|
||
margin-bottom: var(--md-sys-measurement-space200);
|
||
font: var(--md-sys-typescale-title-lg);
|
||
letter-spacing: var(--md-sys-typescale-title-lg-tracking);
|
||
}
|
||
|
||
[data-md-bottom-sheet-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-bottom: var(--md-sys-measurement-space300);
|
||
}
|
||
}
|