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:
Andreas Reinhold / reini
2026-09-15 09:31:47 +02:00
co-authored by Claude Sonnet 5
parent b243222e26
commit 3d9e494e22
3 changed files with 57 additions and 2 deletions
+25 -2
View File
@@ -923,7 +923,9 @@ class DesignGuard
$value = trim($value);
$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];
$found[] = [$line, "literal {$config['kind']} `{$property}: {$value}` — {$config['hint']}"];
}
@@ -932,7 +934,11 @@ class DesignGuard
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`"];
}
@@ -958,6 +964,23 @@ class DesignGuard
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
* px: `(width >= 840px)`, `(min-width: 840px)` and `(600px <= width < 840px)` pass, `(width >