From 8ebcfccc647e48b20dd627b80aa96c13084e3bc0 Mon Sep 17 00:00:00 2001 From: Paul Bakaus Date: Tue, 28 Jul 2026 16:56:08 -0700 Subject: [PATCH] Scope the page-level pattern checks to style carriers, not raw source The static engine's checkHtmlPatterns ran its CSS-property regexes over the entire source string, so documentation ABOUT css flagged as css: impeccable.style's changelog line naming background-clip: text inside a tag tripped gradient-text, the purple hexes in a
 sample
read as the AI palette, and a commented-out stripe rule counted as a
live one. The browser path shared the exposure through outerHTML.

The fix is engine-level, not a per-rule patch. The pattern pass now
scans scoped corpora: styleText carries 
+        

Styled gradient heading

+

Body copy long enough to make this a real page for the scanners.

+ `, + }, async ({ file }) => { + const f = await detectHtml(file); + expect(f.some(r => r.antipattern === 'gradient-text' && /background-clip/.test(r.snippet))).toBe(true); + }); + }); + + test('gradient-text: prose and code samples about the pattern do not flag', async () => { + await withStaticFixture({ + 'index.html': `changelog +

Changelog

+

background-clip: text gradients stop tripping the contrast rules.

+
.hero { background: linear-gradient(135deg, #667eea, #764ba2); background-clip: text; }
+

Pair bg-clip-text with bg-gradient-to-r and both utilities together are the tell.

+ `, + }, async ({ file }) => { + const f = await detectHtml(file); + expect(f.filter(r => r.antipattern === 'gradient-text')).toHaveLength(0); + expect(f.filter(r => r.antipattern === 'ai-color-palette')).toHaveLength(0); + }); + }); + test('flattens @layer, resolves CSS variables and fallbacks, and skips unsupported selectors', async () => { await withStaticFixture({ 'index.html': ` diff --git a/tests/fixtures/antipatterns/css-in-prose-should-flag.html b/tests/fixtures/antipatterns/css-in-prose-should-flag.html new file mode 100644 index 000000000..af69dc8e3 --- /dev/null +++ b/tests/fixtures/antipatterns/css-in-prose-should-flag.html @@ -0,0 +1,35 @@ + + + + + +Real gradient-text styling + + + +

Ship Faster With Us

+ + +

Inline gradient heading

+ + +

Tailwind gradient heading

+ +

Body copy long enough to make this a real page for the scanners to read.

+ + diff --git a/tests/fixtures/antipatterns/css-in-prose-should-pass.html b/tests/fixtures/antipatterns/css-in-prose-should-pass.html new file mode 100644 index 000000000..96f5282a1 --- /dev/null +++ b/tests/fixtures/antipatterns/css-in-prose-should-pass.html @@ -0,0 +1,77 @@ + + + + + +Docs about CSS anti-patterns + + + +

Changelog

+ + +

Detector

+

background-clip: text gradients stop tripping the contrast rules when the glyph fill is transparent.

+ + +

What the rule catches

+
.hero-title {
+  background: linear-gradient(135deg, #667eea, #764ba2);
+  -webkit-background-clip: text;
+  background-clip: text;
+  color: #7c3aed;
+}
+ + +

The Tailwind form pairs bg-clip-text with bg-gradient-to-r on one element; the two utilities together are the tell.

+ + + + +

Pulsing dots

+
.status-dot {
+  width: 8px;
+  height: 8px;
+  border-radius: 50%;
+  animation: pulse 2s ease-in-out infinite;
+}
+ + +

Side stripes

+
.card::before {
+  content: "";
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  width: 4px;
+  background: #3b82f6;
+}
+ + +