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>
212 lines
8.5 KiB
CSS
212 lines
8.5 KiB
CSS
/*
|
|
* <x-scaffold>: M3's scaffold — bars, then rails, then panes (docs/reference/m3/foundations.md
|
|
* § Layout → Scaffold) — as a column: an optional `banner` full width, a row below it holding the
|
|
* adaptive navigation rail and the content region, the bottom navigation bar on a compact window,
|
|
* and the snackbar host. Plan step 36 (navigation group) moves in everything the view drew with
|
|
* Tailwind; `data-md-scaffold-fab` and the content region's `--md-layout-margin` are step 35's,
|
|
* unchanged below.
|
|
*
|
|
* `--material-margin` is M3's window margin, which the content region pads itself with and an
|
|
* application's own rules can read: 16px below `medium`, 24px from it
|
|
* (docs/reference/m3/foundations-supplement.md § Breakpoints), the same two spacing tokens
|
|
* layout/pane.css draws its own margin from. `--md-layout-margin: 0px` on the region tells a pane
|
|
* or canonical layout inside that the margin is already drawn.
|
|
*
|
|
* `[data-md-skip-link]` hides the way `md-visually-hidden` does at rest (text.css) — duplicated
|
|
* here rather than shared, because a class cannot be un-applied on `:focus` — and reveals itself
|
|
* fixed at the window's leading corner, clear of a safe area an application sets, in M3's pill
|
|
* shape and label-large type, with the elevation-3 shadow every temporary surface above the page
|
|
* takes.
|
|
*
|
|
* `[data-md-scaffold-row]` is flex from `medium` only (the width the rail joins the layout at
|
|
* instead of sliding over it as a modal) but always takes the remaining height, whichever it is;
|
|
* `[data-md-scaffold-content]` — the rail's sibling — is always a flex column, so the page's own
|
|
* bar, the FAB wrapper and `<main>` stack regardless of the row's own display.
|
|
*
|
|
* `[data-md-scaffold-actions]`'s column layout is `rail-collapsed:flex-col`, the one Tailwind
|
|
* variant scaffold.blade.php still used (`resources/css/tailwind.css`, removed with this rule): the
|
|
* actions row is always inside a rail, never the rail element itself, so it reads
|
|
* `--md-navigation-rail-value` with `@container style()` like any other application content does
|
|
* (`resources/css/components/navigation-rail.css`), rather than copying the rail's own conditions.
|
|
*
|
|
* `[data-md-scaffold-bar]` pins the navigation bar to the window's bottom edge below `medium`, at
|
|
* z-index 30: over the page, under the rail's scrim (40) and the snackbar (50, toast.css). While a
|
|
* bar is there, the scaffold's root publishes `--material-bottom-bar` below `medium` — the bar's
|
|
* 64px or, tall, 80px (`NavigationBarTokens.ContainerHeight` and `TallContainerHeight`, the bar's
|
|
* own dimensions rather than the spacing scale), the bottom safe area and `--material-bottom-extra`
|
|
* — which everything the bar would otherwise cover reads: the FAB (below), the snackbar host
|
|
* (toast.css), a placed toolbar (toolbar.css) and the content region's bottom padding. Without a
|
|
* bar, or from `medium`, the scaffold publishes nothing and each reader falls back to 0px. A bar that has slid
|
|
* away on scroll drops its own height from the variable (navigation-bar.css); that rule wins
|
|
* because `material.components` comes after this file's `material.layout`, where before this
|
|
* rewrite the variable was a Tailwind utility and the override had to sit outside every layer.
|
|
*
|
|
* The content region's `<main>` is matched as the content wrapper's own child, so a `<main>` an
|
|
* application nests inside the page takes none of the region's padding or clipping.
|
|
*
|
|
* Imports the stylesheets of every component this view renders: the adaptive rail and its item and
|
|
* section, the navigation bar and its item, and the snackbar host.
|
|
*
|
|
* In `material.layout`.
|
|
*/
|
|
|
|
@layer material.reset, material.tokens, material.base, material.layout, material.components, material.text, material.visibility;
|
|
|
|
@import './visibility.css';
|
|
@import '../components/navigation-rail.css';
|
|
@import '../components/navigation-rail-item.css';
|
|
@import '../components/navigation-rail-section.css';
|
|
@import '../components/navigation-bar.css';
|
|
@import '../components/navigation-bar-item.css';
|
|
@import '../components/toast.css';
|
|
|
|
@layer material.layout {
|
|
[data-md-scaffold] {
|
|
--material-margin: var(--md-sys-measurement-space200);
|
|
|
|
display: flex;
|
|
min-block-size: 100dvh;
|
|
flex-direction: column;
|
|
background-color: var(--md-sys-color-surface);
|
|
color: var(--md-sys-color-on-surface);
|
|
|
|
@media (width >= 600px) {
|
|
--material-margin: var(--md-sys-measurement-space300);
|
|
}
|
|
}
|
|
|
|
[data-md-skip-link] {
|
|
position: absolute;
|
|
inline-size: 1px;
|
|
block-size: 1px;
|
|
padding: 0;
|
|
margin: -1px;
|
|
overflow: hidden;
|
|
clip-path: inset(50%);
|
|
white-space: nowrap;
|
|
border-width: 0;
|
|
|
|
&:focus {
|
|
position: fixed;
|
|
z-index: 60;
|
|
inset-inline-start: var(--md-sys-measurement-space200);
|
|
top: calc(var(--material-safe-top, env(safe-area-inset-top)) + var(--md-sys-measurement-space200));
|
|
inline-size: auto;
|
|
block-size: auto;
|
|
padding: var(--md-sys-measurement-space100) var(--md-sys-measurement-space200);
|
|
margin: 0;
|
|
overflow: visible;
|
|
clip-path: none;
|
|
white-space: normal;
|
|
border-radius: var(--md-sys-shape-corner-full);
|
|
background-color: var(--md-sys-color-inverse-surface);
|
|
color: var(--md-sys-color-inverse-on-surface);
|
|
font: var(--md-sys-typescale-label-lg);
|
|
letter-spacing: var(--md-sys-typescale-label-lg-tracking);
|
|
font-variation-settings: normal;
|
|
box-shadow: var(--md-sys-elevation-3);
|
|
outline: none;
|
|
}
|
|
}
|
|
|
|
[data-md-scaffold-banner] {
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
[data-md-scaffold-row] {
|
|
flex: 1 1 0%;
|
|
|
|
@media (width >= 600px) {
|
|
display: flex;
|
|
}
|
|
}
|
|
|
|
[data-md-scaffold-content] {
|
|
display: flex;
|
|
min-inline-size: 0;
|
|
flex: 1 1 0%;
|
|
flex-direction: column;
|
|
}
|
|
|
|
[data-md-scaffold-actions] {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--md-sys-measurement-space50);
|
|
padding-block-start: var(--md-sys-measurement-space100);
|
|
padding-inline: 20px;
|
|
|
|
@container style(--md-navigation-rail-value: collapsed) {
|
|
flex-direction: column;
|
|
}
|
|
}
|
|
|
|
[data-md-scaffold-content] > main {
|
|
--md-layout-margin: 0px;
|
|
|
|
min-inline-size: 0;
|
|
flex: 1 1 0%;
|
|
padding-inline: var(--material-margin);
|
|
outline: none;
|
|
|
|
@media (width < 840px) {
|
|
overflow-x: clip;
|
|
}
|
|
|
|
@media (width < 600px) {
|
|
padding-block-end: var(--material-bottom-bar);
|
|
}
|
|
}
|
|
|
|
[data-md-scaffold-bar] {
|
|
position: fixed;
|
|
z-index: 30;
|
|
inset-inline: 0;
|
|
inset-block-end: 0;
|
|
|
|
@media (width >= 600px) {
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
[data-md-scaffold]:has(> [data-md-scaffold-bar] [data-md-navigation-bar]:not([data-md-tall])) {
|
|
@media (width < 600px) {
|
|
--material-bottom-bar: calc(64px + var(--material-safe-bottom, env(safe-area-inset-bottom)) + var(--material-bottom-extra, 0px));
|
|
}
|
|
}
|
|
|
|
[data-md-scaffold]:has(> [data-md-scaffold-bar] [data-md-navigation-bar][data-md-tall]) {
|
|
@media (width < 600px) {
|
|
--material-bottom-bar: calc(80px + var(--material-safe-bottom, env(safe-area-inset-bottom)) + var(--material-bottom-extra, 0px));
|
|
}
|
|
}
|
|
|
|
[data-md-scaffold-fab] {
|
|
--md-scaffold-fab-margin: var(--md-sys-measurement-space200);
|
|
|
|
position: fixed;
|
|
z-index: 30;
|
|
inset-inline-end: max(
|
|
var(--md-scaffold-fab-margin),
|
|
var(--material-safe-right, env(safe-area-inset-right))
|
|
);
|
|
inset-block-end: calc(
|
|
max(var(--material-bottom-bar, 0px), var(--material-safe-bottom, env(safe-area-inset-bottom)))
|
|
+ var(--md-scaffold-fab-margin)
|
|
+ var(--material-snackbar-height, 0px)
|
|
+ min(var(--material-snackbar-height, 0px), var(--md-sys-measurement-space200))
|
|
);
|
|
transition: inset-block-end var(--md-sys-motion-spatial-default-duration) var(--md-sys-motion-spatial-default);
|
|
|
|
@media (width >= 600px) {
|
|
--md-scaffold-fab-margin: var(--md-sys-measurement-space300);
|
|
}
|
|
}
|
|
|
|
[data-md-scaffold-fab]:is([dir='rtl'], [dir='rtl'] *) {
|
|
inset-inline-end: max(
|
|
var(--md-scaffold-fab-margin),
|
|
var(--material-safe-left, env(safe-area-inset-left))
|
|
);
|
|
}
|
|
}
|