diff --git a/.agents/skills/impeccable/scripts/detector/detect-antipatterns-browser.js b/.agents/skills/impeccable/scripts/detector/detect-antipatterns-browser.js index 3d948ad39..5266535c3 100644 --- a/.agents/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +++ b/.agents/skills/impeccable/scripts/detector/detect-antipatterns-browser.js @@ -1863,9 +1863,11 @@ function collectMarqueeKeyframes(content) { // bound to a keyframe loop that travels a large horizontal distance. // Rotation/opacity animations never qualify (no X travel); JS-driven // carousels with user controls have no infinite CSS X-loop to match. -function scanCssTextForMarquee(content) { +// `content` is CSS-bearing text; `markup` (defaulting to the same string +// for single-corpus callers) is where the tag itself lives. +function scanCssTextForMarquee(content, markup = content) { const findings = []; - if (/ element' }); } const marqueeKeyframes = collectMarqueeKeyframes(content); @@ -2044,10 +2046,14 @@ function selectorHitsLandmark(content, selector, ranges) { // element sits inside a header/nav landmark is the hero liveness cliché // and is promoted to error severity; occurrences elsewhere keep the // registry default severity. -function scanCssTextForPulsingDot(content) { +// +// `content` is CSS-bearing text (rules and keyframes); `markup` — defaulting +// to the same string for single-corpus callers like the regex source +// engine — is where landmark ranges and Tailwind class attributes live. +function scanCssTextForPulsingDot(content, markup = content) { const customProps = collectCssCustomProps(content); const keyframes = collectPulseKeyframes(content); - const heroRanges = landmarkSourceRanges(content); + const heroRanges = landmarkSourceRanges(markup); const findings = []; const seen = new Set(); @@ -2096,7 +2102,7 @@ function scanCssTextForPulsingDot(content) { if (seen.has(selector)) continue; seen.add(selector); - const inLandmark = selectorHitsLandmark(content, selector, heroRanges); + const inLandmark = selectorHitsLandmark(markup, selector, heroRanges); findings.push({ id: 'pulsing-dot', snippet: `${selector} — ${w}x${h}px dot with infinite "${pulseName}" animation${inLandmark ? ' in header/nav' : ''}`, @@ -2106,10 +2112,11 @@ function scanCssTextForPulsingDot(content) { } // Tailwind utilities: animate-ping / animate-pulse on a tiny rounded-full - // element declared entirely in the class attribute. + // element declared entirely in the class attribute. Scanned in the markup + // corpus so the match index lines up with the landmark ranges. const classRe = /class\s*=\s*(?:"([^"]*)"|'([^']*)')/gi; let cm; - while ((cm = classRe.exec(content)) !== null) { + while ((cm = classRe.exec(markup)) !== null) { const cls = cm[1] || cm[2] || ''; const anim = cls.match(/\banimate-(ping|pulse)\b/); if (!anim) continue; @@ -2190,20 +2197,64 @@ function scanHtmlForShapeAssembledIllustration(html) { return findings; } +// Scoped scan corpora for the page-level pattern checks. CSS-property +// regexes run over the whole source string fire on documentation ABOUT +// css — `background-clip: text` prose,
 samples, HTML
+// comments — so the checks scan only the strings that actually style the
+// page:
+//   styleText —