Fix: stop flagging 1D dashed rules as grid backgrounds (#615)

codex-grid-background treated any 2D px background-size as a grid, so a single hairline tiled as a dash or rail false-positived. A finding now requires two hairline gradients plus a px tile.

Prepared with AI assistance (Cursor agent), directed by @abdulwahabone.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Abdul Wahab
2026-08-22 06:51:25 +05:00
co-authored by Cursor
parent 56f44523f7
commit a236137bc6
3 changed files with 44 additions and 24 deletions
+6 -10
View File
@@ -1955,20 +1955,19 @@ function scanCssTextForGlow(content) {
return results; return results;
} }
// Decorative grid or line-field backgrounds drawn with hairline // Decorative two-axis grid backgrounds drawn with hairline
// linear-gradient layers tiled by a fixed pixel cell. Shared by the HTML // linear-gradient layers tiled by a fixed pixel cell. Shared by the HTML
// pattern pass and the regex source engine so standalone CSS, component // pattern pass and the regex source engine so standalone CSS, component
// styles, and inline styles receive the same coverage. Both signals must // styles, and inline styles receive the same coverage. Both signals must
// co-occur in one declaration block; unrelated rules must not add up across // co-occur in one declaration block; unrelated rules must not add up across
// the file. Returns [{ index, snippet }], capped at one finding per source to // the file. A single hairline is a line, divider, or rail, not a grid, even
// match the page-level HTML check's existing behavior. // when tiled by a 2D px cell. Returns [{ index, snippet }], capped at one
// finding per source to match the page-level HTML check's existing behavior.
function scanCssTextForGridBackground(content) { function scanCssTextForGridBackground(content) {
const hairlineRe = /\b\d{1,3}px\s*,\s*transparent\s+\d{1,3}px/gi; const hairlineRe = /\b\d{1,3}px\s*,\s*transparent\s+\d{1,3}px/gi;
const invertedHairlineRe = /transparent\s+calc\(100%\s*-\s*\d{1,3}px\)/gi; const invertedHairlineRe = /transparent\s+calc\(100%\s*-\s*\d{1,3}px\)/gi;
const sizeDeclPxRe = /background-size\s*:[^;{}"']*\b\d{1,3}px\b/i; const sizeDeclPxRe = /background-size\s*:[^;{}"']*\b\d{1,3}px\b/i;
const sizeDeclPxPairRe = /background-size\s*:[^;{}"']*\b\d{1,3}px\s+\d{1,3}px/i;
const shorthandPxAnyRe = /\/\s*\d{1,3}px\b/; const shorthandPxAnyRe = /\/\s*\d{1,3}px\b/;
const shorthandPxPairRe = /\/\s*\d{1,3}px\s+\d{1,3}px/;
const bgDeclRe = /\bbackground(?:-image)?\s*:\s*([^;{}"']*)/gi; const bgDeclRe = /\bbackground(?:-image)?\s*:\s*([^;{}"']*)/gi;
const blockRe = /\{([^{}]*)\}|style\s*=\s*"([^"]*)"|style\s*=\s*'([^']*)'/gi; const blockRe = /\{([^{}]*)\}|style\s*=\s*"([^"]*)"|style\s*=\s*'([^']*)'/gi;
let blk; let blk;
@@ -1985,13 +1984,10 @@ function scanCssTextForGridBackground(content) {
} }
if (hairlineCount === 0) continue; if (hairlineCount === 0) continue;
const hasPxCell = sizeDeclPxRe.test(block) || shorthandPxAnyRe.test(bgJoined); const hasPxCell = sizeDeclPxRe.test(block) || shorthandPxAnyRe.test(bgJoined);
const hasPxPairCell = sizeDeclPxPairRe.test(block) || shorthandPxPairRe.test(bgJoined); if (hairlineCount >= 2 && hasPxCell) {
if ((hairlineCount >= 2 && hasPxCell) || hasPxPairCell) {
return [{ return [{
index: blk.index, index: blk.index,
snippet: hairlineCount >= 2 snippet: 'two-axis grid-line gradient background',
? 'two-axis grid-line gradient background'
: 'px-tiled hairline line-field background',
}]; }];
} }
} }
+6 -10
View File
@@ -721,20 +721,19 @@ function scanCssTextForGlow(content) {
return results; return results;
} }
// Decorative grid or line-field backgrounds drawn with hairline // Decorative two-axis grid backgrounds drawn with hairline
// linear-gradient layers tiled by a fixed pixel cell. Shared by the HTML // linear-gradient layers tiled by a fixed pixel cell. Shared by the HTML
// pattern pass and the regex source engine so standalone CSS, component // pattern pass and the regex source engine so standalone CSS, component
// styles, and inline styles receive the same coverage. Both signals must // styles, and inline styles receive the same coverage. Both signals must
// co-occur in one declaration block; unrelated rules must not add up across // co-occur in one declaration block; unrelated rules must not add up across
// the file. Returns [{ index, snippet }], capped at one finding per source to // the file. A single hairline is a line, divider, or rail, not a grid, even
// match the page-level HTML check's existing behavior. // when tiled by a 2D px cell. Returns [{ index, snippet }], capped at one
// finding per source to match the page-level HTML check's existing behavior.
function scanCssTextForGridBackground(content) { function scanCssTextForGridBackground(content) {
const hairlineRe = /\b\d{1,3}px\s*,\s*transparent\s+\d{1,3}px/gi; const hairlineRe = /\b\d{1,3}px\s*,\s*transparent\s+\d{1,3}px/gi;
const invertedHairlineRe = /transparent\s+calc\(100%\s*-\s*\d{1,3}px\)/gi; const invertedHairlineRe = /transparent\s+calc\(100%\s*-\s*\d{1,3}px\)/gi;
const sizeDeclPxRe = /background-size\s*:[^;{}"']*\b\d{1,3}px\b/i; const sizeDeclPxRe = /background-size\s*:[^;{}"']*\b\d{1,3}px\b/i;
const sizeDeclPxPairRe = /background-size\s*:[^;{}"']*\b\d{1,3}px\s+\d{1,3}px/i;
const shorthandPxAnyRe = /\/\s*\d{1,3}px\b/; const shorthandPxAnyRe = /\/\s*\d{1,3}px\b/;
const shorthandPxPairRe = /\/\s*\d{1,3}px\s+\d{1,3}px/;
const bgDeclRe = /\bbackground(?:-image)?\s*:\s*([^;{}"']*)/gi; const bgDeclRe = /\bbackground(?:-image)?\s*:\s*([^;{}"']*)/gi;
const blockRe = /\{([^{}]*)\}|style\s*=\s*"([^"]*)"|style\s*=\s*'([^']*)'/gi; const blockRe = /\{([^{}]*)\}|style\s*=\s*"([^"]*)"|style\s*=\s*'([^']*)'/gi;
let blk; let blk;
@@ -751,13 +750,10 @@ function scanCssTextForGridBackground(content) {
} }
if (hairlineCount === 0) continue; if (hairlineCount === 0) continue;
const hasPxCell = sizeDeclPxRe.test(block) || shorthandPxAnyRe.test(bgJoined); const hasPxCell = sizeDeclPxRe.test(block) || shorthandPxAnyRe.test(bgJoined);
const hasPxPairCell = sizeDeclPxPairRe.test(block) || shorthandPxPairRe.test(bgJoined); if (hairlineCount >= 2 && hasPxCell) {
if ((hairlineCount >= 2 && hasPxCell) || hasPxPairCell) {
return [{ return [{
index: blk.index, index: blk.index,
snippet: hairlineCount >= 2 snippet: 'two-axis grid-line gradient background',
? 'two-axis grid-line gradient background'
: 'px-tiled hairline line-field background',
}]; }];
} }
} }
+32 -4
View File
@@ -1806,11 +1806,9 @@ describe('codex-grid-background variants', () => {
expect(grids(css)).toHaveLength(1); expect(grids(css)).toHaveLength(1);
}); });
test('flags single-axis hairline tiled by a px pair cell', () => { test('keeps single-axis hairline tiled by a px pair cell legal', () => {
const css = `body { background: linear-gradient(90deg, rgba(23,25,24,.035) 1px, transparent 1px) 0 0 / 40px 40px, #f4f1ea; }`; const css = `body { background: linear-gradient(90deg, rgba(23,25,24,.035) 1px, transparent 1px) 0 0 / 40px 40px, #f4f1ea; }`;
const f = grids(css); expect(grids(css)).toHaveLength(0);
expect(f).toHaveLength(1);
expect(f[0].snippet).toContain('line-field');
}); });
test('keeps percent-tiled single hairlines (data-viz track rules) legal', () => { test('keeps percent-tiled single hairlines (data-viz track rules) legal', () => {
@@ -1818,6 +1816,36 @@ describe('codex-grid-background variants', () => {
expect(grids(css)).toHaveLength(0); expect(grids(css)).toHaveLength(0);
}); });
test('keeps 1D dashed dot rules legal', () => {
const css = `.dot-rule {
height: 5px;
background-image: linear-gradient(90deg, rgba(255,255,255,.75) 5px, transparent 5px);
background-size: 10px 5px;
background-repeat: repeat-x;
}`;
expect(grids(css)).toHaveLength(0);
});
test('keeps 1D progress rails with dash-period px pair tiles legal', () => {
const css = `.progress-rail {
background-image: linear-gradient(90deg, #eee 1px, transparent 1px);
background-size: 8px 4px;
background-repeat: repeat-x;
}`;
expect(grids(css)).toHaveLength(0);
});
test('regex source engine keeps 1D dot rules legal', () => {
const css = `.dot-rule {
height: 5px;
background-image: linear-gradient(90deg, rgba(255,255,255,.75) 5px, transparent 5px);
background-size: 10px 5px;
background-repeat: repeat-x;
}`;
const findings = detectText(css, 'dot-rule.css');
expect(findings.filter(f => f.antipattern === 'codex-grid-background')).toHaveLength(0);
});
test('classic two-axis background-size form still flags', () => { test('classic two-axis background-size form still flags', () => {
const css = `.hero { background-image: const css = `.hero { background-image:
linear-gradient(#eee 1px, transparent 1px), linear-gradient(#eee 1px, transparent 1px),