Cut duplicated and speculative code across the package

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>
This commit is contained in:
Andreas Reinhold / reini
2026-09-17 19:29:21 +02:00
co-authored by Claude Opus 5
parent 471d927e64
commit 247c596c3a
233 changed files with 16635 additions and 10579 deletions
+65 -32
View File
@@ -24,7 +24,7 @@ it('gives the collapsible, modal and adaptive rails a menu button and the store'
->toContain('data-md-navigation-rail-menu')
->toContain('aria-label="Collapse navigation"')
->toContain('aria-expanded="true"')
// The 40px menu button reaches M3's 48px target through the shared classes (N-01).
// The 40px menu button reaches M3's 48px target through the shared classes.
->toContain('class="md-state-layer md-focus-ring md-touch-target"')
->not->toContain('data-md-navigation-rail-scrim')
->not->toContain('x-trap')
@@ -63,13 +63,13 @@ it('takes M3\'s optional divider and turns the container fill off', function ()
$css = ComponentStylesheet::read('navigation-rail');
// Neither reaches a rail open over a scrim, which is a surface over the page (N-22), nor one
// Neither reaches a rail open over a scrim, which is a surface over the page, nor one
// sliding off the window as it closes.
expect($css->declarations('[data-md-navigation-rail][data-md-divider]:not([data-md-open], [data-md-closing=\'sheet\']) > [data-md-navigation-rail-panel]'))
->toBe(['border-inline-end' => '1px solid var(--md-sys-color-outline-variant)'])
->and($css->declarations('[data-md-navigation-rail][data-md-fill=\'false\']:not([data-md-open], [data-md-closing=\'sheet\']) > [data-md-navigation-rail-panel]'))
->toBe(['background-color' => 'transparent'])
// A collapsible rail is held to its collapsed width where M3 asks for a bar instead (N-24).
// A collapsible rail is held to its collapsed width where M3 asks for a bar instead.
->and($css->declarations('[data-md-navigation-rail=\'collapsible\']', ['@media (width < 600px)']))
->toBe(['width' => 'var(--navigation-rail-collapsed-width)']);
});
@@ -97,7 +97,7 @@ it('takes M3\'s narrow collapsed width, icons alone but still named', function (
->and($css->declarations('[data-md-navigation-rail]'))
->toHaveKey('--navigation-rail-collapsed-width', '96px')
// Out of the drawing, not out of the page: clipped only while collapsed.
->and($css->declarations("[data-md-navigation-rail][data-md-width='narrow'] [data-md-navigation-rail-item] [data-md-navigation-label]:where( [data-md-navigation-rail='collapsed'], [data-md-navigation-rail='collapsed'] *, [data-rail='collapsed'] [data-md-navigation-rail='collapsible']:not([data-md-open]), [data-rail='collapsed'] [data-md-navigation-rail='collapsible']:not([data-md-open]) *, [data-md-navigation-rail='modal']:not([data-md-open]), [data-md-navigation-rail='modal']:not([data-md-open]) * )"))
->and($css->declarations("[data-md-navigation-rail][data-md-width='narrow'] [data-md-navigation-rail-item] [data-md-navigation-label]", ['@container style(--md-navigation-rail-value: collapsed)']))
->toHaveKey('clip-path', 'inset(50%)');
});
@@ -206,18 +206,18 @@ it('reproduces every branch of the old rail-collapsed variant for the rail\'s ow
it('flattens a FAB nested in the rail header and morphs its label', function () {
$css = ComponentStylesheet::read('navigation-rail');
// A nested FAB rests at elevation 0, not the 3 a standalone one has (N-03). Now layered, like
// A nested FAB rests at elevation 0, not the 3 a standalone one has. Now layered, like
// toolbar.css's own docked-FAB override: fab.css's `[data-md-fab]` is one attribute, so the
// doubled selector here (two) always wins without needing to sit outside the layer.
expect($css->declarations('[data-md-navigation-rail-header] [data-md-fab][data-md-fab], [data-md-navigation-rail-header] [data-md-fab][data-md-fab]:hover'))
->toBe(['box-shadow' => 'none'])
// One FAB whose label springs shut, not two swapped by display (N-23).
// One FAB whose label springs shut, not two swapped by display.
->and($css->declarations('[data-md-navigation-rail-header] [data-md-fab] > span'))
->toHaveKey('max-width', '256px');
expect($css->declarations("[data-md-navigation-rail-header] [data-md-fab] > span:where( [data-md-navigation-rail='collapsed'], [data-md-navigation-rail='collapsed'] *, [data-rail='collapsed'] [data-md-navigation-rail='collapsible']:not([data-md-open]), [data-rail='collapsed'] [data-md-navigation-rail='collapsible']:not([data-md-open]) *, [data-md-navigation-rail='modal']:not([data-md-open]), [data-md-navigation-rail='modal']:not([data-md-open]) * )"))
expect($css->declarations('[data-md-navigation-rail-header] [data-md-fab] > span', ['@container style(--md-navigation-rail-value: collapsed)']))
->toBe(['max-width' => '0', 'opacity' => '0'])
->and($css->declarations("[data-md-navigation-rail-header] [data-md-fab][data-md-extended][data-md-extended]:where( [data-md-navigation-rail='collapsed'], [data-md-navigation-rail='collapsed'] *, [data-rail='collapsed'] [data-md-navigation-rail='collapsible']:not([data-md-open]), [data-rail='collapsed'] [data-md-navigation-rail='collapsible']:not([data-md-open]) *, [data-md-navigation-rail='modal']:not([data-md-open]), [data-md-navigation-rail='modal']:not([data-md-open]) * )"))
->and($css->declarations('[data-md-navigation-rail-header] [data-md-fab][data-md-extended][data-md-extended]', ['@container style(--md-navigation-rail-value: collapsed)']))
->toBe(['min-inline-size' => '0', 'aspect-ratio' => '1', 'gap' => '0']);
});
@@ -260,21 +260,13 @@ it('draws the item\'s own state layer split between the item and its indicator',
});
/**
* Plain CSS has no variant to name "drawn collapsed" once, so every rule with a collapsed shape
* writes the conditions out: navigation-rail.css, navigation-rail-item.css,
* navigation-rail-section.css, and layout/scaffold.css for the actions row inside the rail's footer.
* This walks every copy in those files — not one pinned rule — and fails if any drifts from the
* seven conditions the Tailwind-era `rail-collapsed:` variant had, or if a rule has its collapsed
* shape in some window bands but not in all five.
* navigation-rail.css alone still writes "drawn collapsed" branch for branch: the two rules that
* set the rail's own width and publish `--md-navigation-rail-value`, which the rail cannot query
* on itself (a `@container style()` reads an ancestor, never the element that publishes the
* property). This walks every copy of that seven-condition set and fails if any drifts from it, or
* has its collapsed shape in some window bands but not in all five.
*/
dataset('stylesheets that draw a collapsed rail', [
'navigation-rail',
'navigation-rail-item',
'navigation-rail-section',
'../layout/scaffold',
]);
it('writes "collapsed" with the same seven conditions in every copy, in every band', function (string $name) {
it('writes the rail\'s own "collapsed" condition with the same seven conditions, in every band', function () {
$closed = ':not([data-md-open])';
$bands = [
'always' => ["[data-md-navigation-rail='collapsed']", "[data-rail='collapsed'] [data-md-navigation-rail='collapsible']{$closed}", "[data-md-navigation-rail='modal']{$closed}"],
@@ -284,14 +276,12 @@ it('writes "collapsed" with the same seven conditions in every copy, in every ba
'@media (width >= 1200px)' => ["[data-rail='collapsed'] [data-md-navigation-rail='adaptive']{$closed}"],
];
// A condition matches the rail itself and everything in it; the scaffold's actions row can only
// ever be inside a rail, so its copy keeps the descendant half alone.
// A condition matches the rail itself and everything in it.
$both = fn (array $conditions): array => array_merge(...array_map(fn (string $condition): array => [$condition, "{$condition} *"], $conditions));
$inside = fn (array $conditions): array => array_map(fn (string $condition): string => "{$condition} *", $conditions);
$groups = [];
foreach (ComponentStylesheet::read($name)->rules() as $rule) {
foreach (ComponentStylesheet::read('navigation-rail')->rules() as $rule) {
preg_match_all('/:where\(((?:[^()]++|\((?1)\))*)\)/', $rule['selector'], $wheres, PREG_SET_ORDER);
$wheres = array_filter($wheres, fn (array $where): bool => str_contains($where[1], 'data-md-navigation-rail='));
@@ -302,17 +292,15 @@ it('writes "collapsed" with the same seven conditions in every copy, in every ba
$media = array_values(array_filter($rule['at'], fn (string $at): bool => str_starts_with($at, '@media')));
$band = match ($media) {
[] => 'always',
['@media (width >= 840px)', '@media (width < 1200px)'] => '@media (840px <= width < 1200px)',
default => implode(' ', $media),
};
expect(array_key_exists($band, $bands))->toBeTrue("{$name}.css: `{$rule['selector']}` sits in {$band}, which is no band of the rail's");
expect(array_key_exists($band, $bands))->toBeTrue("navigation-rail.css: `{$rule['selector']}` sits in {$band}, which is no band of the rail's");
foreach ($wheres as [$where, $conditions]) {
$written = array_map(trim(...), preg_split('/,(?![^()]*\))/', $conditions));
expect(in_array($written, [$both($bands[$band]), $inside($bands[$band])], true))
->toBeTrue("{$name}.css: `{$where}` in {$band} is not the rail's collapsed conditions for that band");
expect($written)->toBe($both($bands[$band]), "navigation-rail.css: `{$where}` in {$band} is not the rail's collapsed conditions for that band");
}
$key = str_replace(array_column($wheres, 0), '', $rule['selector']).' '.json_encode($rule['declarations']);
@@ -322,6 +310,51 @@ it('writes "collapsed" with the same seven conditions in every copy, in every ba
expect($groups)->not->toBeEmpty();
foreach ($groups as $key => $found) {
expect(array_values(array_unique($found)))->toEqualCanonicalizing(array_keys($bands), "{$name}.css: {$key} is drawn collapsed in some bands only");
expect(array_values(array_unique($found)))->toEqualCanonicalizing(array_keys($bands), "navigation-rail.css: {$key} is drawn collapsed in some bands only");
}
})->with('stylesheets that draw a collapsed rail');
});
/**
* Everything that takes a collapsed shape and is not the rail element itself reads the value the
* rules above publish, one `@container style()` per rule, instead of a copy of the rail's own
* seven conditions — the reason `--md-navigation-rail-value` is published at all (UPGRADE.md,
* "From 2.0.0 to 2.1.0"). This walks every copy across navigation-rail.css,
* navigation-rail-item.css, navigation-rail-section.css and layout/scaffold.css, fails if any of
* them spells the query differently or wraps no declarations, and — outside navigation-rail.css,
* where the two rules above are the only rules allowed to — fails if the old `:where()` copy of
* the rail's conditions comes back.
*/
dataset('stylesheets with a rail descendant that draws a collapsed shape', [
'navigation-rail',
'navigation-rail-item',
'navigation-rail-section',
'../layout/scaffold',
]);
it('reads the rail\'s published value with the same style container query in every copy', function (string $name) {
$css = ComponentStylesheet::read($name);
$rules = $css->rules();
$matches = array_values(array_filter(
$rules,
fn (array $rule): bool => array_filter($rule['at'], fn (string $at): bool => str_starts_with($at, '@container')) !== [],
));
expect($matches)->not->toBeEmpty();
foreach ($matches as $rule) {
$containers = array_values(array_filter($rule['at'], fn (string $at): bool => str_starts_with($at, '@container')));
expect($containers)->toBe(['@container style(--md-navigation-rail-value: collapsed)'], "{$name}.css: `{$rule['selector']}` wraps a different container query")
->and($rule['declarations'])->not->toBeEmpty("{$name}.css: `{$rule['selector']}` wraps an empty container query");
}
if ($name === 'navigation-rail') {
return;
}
foreach ($rules as $rule) {
expect(str_contains($rule['selector'], "data-md-navigation-rail='collapsed'"))
->toBeFalse("{$name}.css: `{$rule['selector']}` copies the rail's conditions instead of querying its value");
}
})->with('stylesheets with a rail descendant that draws a collapsed shape');