Allow legitimate linear easing and a role-coloured box-shadow ring
Check (iii) flagged two shapes the package's own stylesheets (and their stylesheet tests) already treat as fine: `linear`, M3's own easing for continuous motion (an indeterminate progress indicator's rotation), and a box-shadow that is only an inset or outline-style ring — `[inset] 0 0 0 <n>px` in a `var(--md-sys-color-*)` role, or one `color-mix()`-dimmed for a disabled ring — the shape a focused field's edge or a day's "current" outline draws. A blurred shadow or one with a literal colour is still reported. Plan step 42 (Phase F), Part B. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
b243222e26
commit
3d9e494e22
@@ -923,7 +923,9 @@ class DesignGuard
|
|||||||
$value = trim($value);
|
$value = trim($value);
|
||||||
$line = substr_count(substr($css, 0, $offset), "\n") + 1;
|
$line = substr_count(substr($css, 0, $offset), "\n") + 1;
|
||||||
|
|
||||||
if (isset(self::LITERAL_PROPERTIES[$property]) && ! $this->isSafeLiteralValue($value)) {
|
$isRing = $property === 'box-shadow' && $this->isSafeBoxShadowRing($value);
|
||||||
|
|
||||||
|
if (isset(self::LITERAL_PROPERTIES[$property]) && ! $this->isSafeLiteralValue($value) && ! $isRing) {
|
||||||
$config = self::LITERAL_PROPERTIES[$property];
|
$config = self::LITERAL_PROPERTIES[$property];
|
||||||
$found[] = [$line, "literal {$config['kind']} `{$property}: {$value}` — {$config['hint']}"];
|
$found[] = [$line, "literal {$config['kind']} `{$property}: {$value}` — {$config['hint']}"];
|
||||||
}
|
}
|
||||||
@@ -932,7 +934,11 @@ class DesignGuard
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (preg_match('/cubic-bezier\(|steps\(|(?<![\w-])(?:ease(?:-in-out|-in|-out)?|linear|step-start|step-end)(?![\w-])/i', $value) === 1) {
|
// `linear` is M3's own easing for continuous motion (an indeterminate progress
|
||||||
|
// indicator's rotation, foundations-supplement.md § Motion) — legitimate literal CSS,
|
||||||
|
// unlike `ease`/`ease-in`/`ease-out`/`ease-in-out`, a spring's own shape and never
|
||||||
|
// written by hand, or `cubic-bezier()`/`steps()`, always a hand-rolled curve.
|
||||||
|
if (preg_match('/cubic-bezier\(|steps\(|(?<![\w-])(?:ease(?:-in-out|-in|-out)?|step-start|step-end)(?![\w-])/i', $value) === 1) {
|
||||||
$found[] = [$line, "literal easing in `{$property}: {$value}` — use `var(--md-sys-motion-spatial-*)`/`var(--md-sys-motion-effects-*)`, paired with its `-duration`"];
|
$found[] = [$line, "literal easing in `{$property}: {$value}` — use `var(--md-sys-motion-spatial-*)`/`var(--md-sys-motion-effects-*)`, paired with its `-duration`"];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -958,6 +964,23 @@ class DesignGuard
|
|||||||
return $value === '' || in_array(strtolower($value), ['0', '0px', '0s', '0ms', 'none', 'inherit', 'initial', 'unset', 'revert', 'normal', 'auto'], true);
|
return $value === '' || in_array(strtolower($value), ['0', '0px', '0s', '0ms', 'none', 'inherit', 'initial', 'unset', 'revert', 'normal', 'auto'], true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A `box-shadow` shaped like an inset or outline-style ring in a colour role — `[inset] 0 0 0
|
||||||
|
* <n>px` in a `var(--md-sys-color-*)`, or one mixed toward transparent for a disabled ring
|
||||||
|
* (`color-mix(in srgb, var(--md-sys-color-*) <n>, transparent)`) — the shape the package's own
|
||||||
|
* stylesheets (and their stylesheet tests) use for a day's or a year's "current" outline, a
|
||||||
|
* focused field's edge, a selected chip's border: legitimate M3 CSS, not a hand-made shadow.
|
||||||
|
* `$value` has already had every `var()`/`calc()` call's own arguments blanked (see
|
||||||
|
* `withoutTokenFunctions()`), so the colour itself is read here only as an empty shell.
|
||||||
|
*/
|
||||||
|
protected function isSafeBoxShadowRing(string $value): bool
|
||||||
|
{
|
||||||
|
return preg_match(
|
||||||
|
'/^(?:inset\s+)?0\s+0\s+0\s+\d+(?:\.\d+)?px\s+(?:var\([ \t]*\)|color-mix\(in srgb,\s*var\([ \t]*\)[^,]*,\s*transparent\s*\))$/',
|
||||||
|
trim($value),
|
||||||
|
) === 1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Every width in an `@media` condition that is not one of M3's four breakpoints written in
|
* Every width in an `@media` condition that is not one of M3's four breakpoints written in
|
||||||
* px: `(width >= 840px)`, `(min-width: 840px)` and `(600px <= width < 840px)` pass, `(width >
|
* px: `(width >= 840px)`, `(min-width: 840px)` and `(600px <= width < 840px)` pass, `(width >
|
||||||
|
|||||||
@@ -540,6 +540,17 @@ it('finds every literal design value and off-scale media query in the applicatio
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('leaves a box-shadow ring in a colour role alone, but still reports a blurred or literal-coloured one', function () {
|
||||||
|
$violations = fixtureRelative(DesignGuard::scan(realpath(GUARD_FIXTURES.'/shadows/rings.css'))->violations());
|
||||||
|
|
||||||
|
expect($violations)->toBe([
|
||||||
|
'shadows/rings.css:16 literal colour `rgba(0, 0, 0, 0.2)` — use `var(--md-sys-color-*)`',
|
||||||
|
'shadows/rings.css:16 literal shadow `box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2)` — use `var(--md-sys-elevation-*)`',
|
||||||
|
'shadows/rings.css:20 literal colour `#000` — use `var(--md-sys-color-*)`',
|
||||||
|
'shadows/rings.css:20 literal shadow `box-shadow: inset 0 0 4px 1px #000` — use `var(--md-sys-elevation-*)`',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
it('ignores the generated material-scheme.css, by name and by its generated header, wherever it sits among the scanned paths', function () {
|
it('ignores the generated material-scheme.css, by name and by its generated header, wherever it sits among the scanned paths', function () {
|
||||||
$withScheme = fixtureRelative(DesignGuard::scan(realpath(GUARD_FIXTURES.'/app-css'))->violations());
|
$withScheme = fixtureRelative(DesignGuard::scan(realpath(GUARD_FIXTURES.'/app-css'))->violations());
|
||||||
$withoutScheme = fixtureRelative(DesignGuard::scan(realpath(GUARD_FIXTURES.'/app-css/app.css'))->violations());
|
$withoutScheme = fixtureRelative(DesignGuard::scan(realpath(GUARD_FIXTURES.'/app-css/app.css'))->violations());
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/* A ring in a colour role (a focused field's edge, a day's "current" outline), and the two shapes
|
||||||
|
check (iii) still reports: a blurred shadow, and one with a literal colour. */
|
||||||
|
.ring-outline {
|
||||||
|
box-shadow: inset 0 0 0 1px var(--md-sys-color-outline);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ring-secondary {
|
||||||
|
box-shadow: 0 0 0 3px var(--md-sys-color-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ring-disabled {
|
||||||
|
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--md-sys-color-on-surface) 38%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.literal-shadow {
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.blurred-ring {
|
||||||
|
box-shadow: inset 0 0 4px 1px #000;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user