diff --git a/cli/engine/engines/regex/detect-text.mjs b/cli/engine/engines/regex/detect-text.mjs index ff4db4228..02d63eac3 100644 --- a/cli/engine/engines/regex/detect-text.mjs +++ b/cli/engine/engines/regex/detect-text.mjs @@ -425,11 +425,15 @@ function scanInsetStripeCss(rawContent, filePath, lineOffset = 0) { // an unchanged layer had no inset keyword and is not our shape. const body = layer.replace(/(^|\s)inset(?=\s|$)/i, '$1').trim(); if (body === layer) continue; - const shadow = body.match(/^(-?\d*\.?\d+)(px)?\s+(-?\d*\.?\d+)(px)?\s+(-?\d*\.?\d+)(px)?(?:\s+(-?\d*\.?\d+)(px)?)?\s+(.+)$/i); + // box-shadow takes {2,4}: only the two offsets are required, so + // `inset 4px 0 red` is valid and paints the same stripe as + // `inset 4px 0 0 red`. Demanding a third length missed the short form. + const shadow = body.match(/^(-?\d*\.?\d+)(px)?\s+(-?\d*\.?\d+)(px)?(?:\s+(-?\d*\.?\d+)(px)?)?(?:\s+(-?\d*\.?\d+)(px)?)?\s+(.+)$/i); if (!shadow) continue; const x = Number(shadow[1]); const y = Number(shadow[3]); - const blur = Number(shadow[5]); + // Omitted blur and spread default to 0, which is exactly the stripe shape. + const blur = shadow[5] == null ? 0 : Number(shadow[5]); const spread = shadow[7] == null ? 0 : Number(shadow[7]); if ((x !== 0 && !shadow[2]) || (y !== 0 && !shadow[4]) || blur !== 0 || spread !== 0) continue; const ax = Math.abs(x); diff --git a/tests/detect-antipatterns-fixtures.test.mjs b/tests/detect-antipatterns-fixtures.test.mjs index a6d9f9892..8b268d5df 100644 --- a/tests/detect-antipatterns-fixtures.test.mjs +++ b/tests/detect-antipatterns-fixtures.test.mjs @@ -33,6 +33,9 @@ describe('detectText - Astro structural CSS fixtures', () => { 'Trailing Inset Edge', 'Trailing Inset Token Edge', 'Inset Named Token Edge', + // Only the two offsets are required; blur/spread default to 0. + 'Two Length Edge', + 'Two Length Trailing Inset Edge', ]; const SHOULD_PASS = [ 'Neutral Shadow Token', @@ -56,6 +59,9 @@ describe('detectText - Astro structural CSS fixtures', () => { 'Commented Out Edge', // Trailing `inset` still respects the neutral-color exemption. 'Trailing Inset Neutral Edge', + // The short form still respects the neutral and blur exclusions. + 'Two Length Neutral Edge', + 'Two Length Blurred Edge', ]; it('Astro style blocks flag unresolved chromatic inset stripes only', () => { diff --git a/tests/fixtures/antipatterns/astro-inset-shadow-stripe.astro b/tests/fixtures/antipatterns/astro-inset-shadow-stripe.astro index a93c6e396..d836e66d4 100644 --- a/tests/fixtures/antipatterns/astro-inset-shadow-stripe.astro +++ b/tests/fixtures/antipatterns/astro-inset-shadow-stripe.astro @@ -17,6 +17,8 @@ const title = 'Astro inset shadow stripe regression';

Trailing Inset Edge

Trailing Inset Token Edge

Inset Named Token Edge

+

Two Length Edge

+

Two Length Trailing Inset Edge

Should pass

@@ -35,6 +37,8 @@ const title = 'Astro inset shadow stripe regression';

Shorthand Neutral Hex Edge

Commented Out Edge

Trailing Inset Neutral Edge

+

Two Length Neutral Edge

+

Two Length Blurred Edge

@@ -71,6 +75,13 @@ const title = 'Astro inset shadow stripe regression'; [data-case="Inset Named Token Edge"] { box-shadow: inset 4px 0 0 var(--inset-accent); } [data-case="Trailing Inset Neutral Edge"] { box-shadow: 4px 0 0 #000 inset; } + /* box-shadow takes {2,4}: blur and spread are optional and default to + 0, so these paint the same stripe as the four-length forms above. */ + [data-case="Two Length Edge"] { box-shadow: inset 4px 0 var(--brand-accent); } + [data-case="Two Length Trailing Inset Edge"] { box-shadow: 0 5px #6366f1 inset; } + [data-case="Two Length Neutral Edge"] { box-shadow: inset 4px 0 #000; } + [data-case="Two Length Blurred Edge"] { box-shadow: inset 4px 0 5px var(--brand-accent); } + /* Commented-out rules are not live CSS. [data-case="Commented Out Edge"] { box-shadow: inset 4px 0 0 var(--brand-accent); } */