An over-engineering audit of the whole tree, applied in five reviewed batches. Behaviour stays the same except where UPGRADE.md says otherwise. PHP: the showcase and error-page stylesheets are prebuilt into resources/dist by bin/stylesheets.mjs, through Vite's own postcss-import (first occurrence kept, the order an application's build gives), instead of Stylesheets::bundle() inlining imports on every request; only the import walk DesignGuard needs stays. SchemeStylesheet::withProfiles() replaces three copies of the scheme-plus-profiles loop, material:scheme leaves spec and contrast checks to the node script that already made them, and the error page's scheme cache, the hashed view namespace, the translations path with no lang/ folder and DesignGuard's 1.x-name hints are gone. JS: the androidx shape port progress.js and both bin scripts each carried lives once in resources/js/shapes.js (the generated SVGs are unchanged); util.js holds ringIndex(), ms(), reopenGuard() and remember(), which were written out several times; listeners are released through AbortController; tooltip.js's hoverPopover() serves the rich tooltip too. CSS: every rule for an element inside the navigation rail queries `--md-navigation-rail-value` instead of repeating the seven collapsed conditions under five media branches; badge, alert, progress, slider and button read one non-inheriting colour-role table (components/color.css); the dialog chrome, the submenu's popover chrome, the chip's state layer and touch target, and the visually-hidden inputs use the shared rules they copied; foundation/tokens.css is folded into foundation.css. Views: Support\Field and Support\Link replace the error-key, bound-value and link-attribute blocks copied into the fields and link components; the timepicker period group, the menu filter and the showcase head are partials; the datepicker's steppers and entry fields are loops; component docblocks no longer restate SKILL.md. Tests and tooling: one dataset-driven ComponentStylesheetsTest replaces four per-group files, DesignGuardTest and the layout-component tests use datasets, browser tests share one ready() helper, CSS parsing lives in ComponentStylesheet alone. docs/audits and the finding IDs citing it are removed, as are pestphp/pest-plugin-laravel, the unused composer scripts and check:font; the lint job runs in the feature job, which now installs node packages so the prebuilt-stylesheet staleness test runs in CI. Feature suite 1177 passed, Chrome browser suite 299 passed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
170 lines
7.3 KiB
CSS
170 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
|
||
* [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. 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.
|
||
* Both keep `x-show`, and the view's `x-transition` holds their `display` through the exit rather
|
||
* than `allow-discrete`, which Firefox does not honour for `display` (see drawer.css); Alpine holds
|
||
* for the first `transition-duration` listed, so the closing slide is listed first.
|
||
*/
|
||
|
||
@layer material.reset, material.tokens, material.base, material.layout, material.components, material.text, material.visibility;
|
||
|
||
@layer material.components {
|
||
/* The scrim fades out while the view's `x-transition` holds its `display` (see drawer.css). */
|
||
[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;
|
||
transition-duration: var(--md-sys-motion-effects-default-duration);
|
||
transition-timing-function: var(--md-sys-motion-effects-default);
|
||
}
|
||
|
||
[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;
|
||
transition-duration: var(--md-sys-motion-effects-default-duration);
|
||
transition-timing-function: var(--md-sys-motion-easing-emphasized-accelerate);
|
||
}
|
||
|
||
[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;
|
||
transition-duration: var(--md-sys-motion-effects-default-duration), var(--md-sys-motion-spatial-default-duration);
|
||
transition-timing-function: var(--md-sys-motion-easing-emphasized-accelerate), var(--md-sys-motion-spatial-default);
|
||
}
|
||
|
||
[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);
|
||
transition-timing-function: var(--md-sys-motion-easing-emphasized-decelerate), var(--md-sys-motion-spatial-default);
|
||
}
|
||
|
||
[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);
|
||
}
|
||
}
|