mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-16 08:06:24 +03:00
A new icon-tile-stack detection (the canonical AI feature-card with a small rounded-square icon container above a heading), backed by a two-column TDD fixture, plus a single-source-of-truth design that ties the engine to the impeccable skill so they can no longer drift silently. Detection - New icon-tile-stack rule (slop): heading's previousElementSibling is a 32–128px rounded-square element with a non-transparent background or border, contains an svg/icon-i child, and sits above (not next to) the heading. Excludes round avatars, wide thumbnails, side-by-side layouts, tiny icons, and hero images. - Two-column fixture convention: a single icon-tile-stack.html with a flag column (4 cases) and pass column (6 cases), with snippet-text matching used by the fixture test. Single source of truth - Each ANTIPATTERNS entry can now declare skillSection + skillGuideline. 18 of 25 rules carry these fields; the build's new validateAntipatternRules() in scripts/build.js fails if any declared skillGuideline isn't found verbatim in the right SKILL.md section. - scripts/build-extension.js now includes the description field in extension/detector/antipatterns.json (it was previously dropped). - The existing count validator was promoted from warn to error so command count drift fails the build the same way detection drift does. Impeccable skill DON'Ts - Added 4 new top-level DON'Ts that target real default AI behavior: single-font, flat-type-hierarchy, all-caps-body, line-length. - Cut 7 new DON'Ts I had drafted (tight-leading, tiny-text, wide-tracking, justified-text, low-contrast, cramped-padding, skipped-heading) because they teach things every model already knows from CSS/a11y basics. The detector still catches all of them. Stale count cleanup - 22 commands → 21 across 17 references in HTML, README, NOTICE, AGENTS, plugin.json, marketplace.json (left over from the validate skill removal). - Dropped the hand-coded "212 design guidelines" marketing copy on the homepage, which never mapped to any real count. Sub-agent - New private .claude/agents/anti-patterns.md captures the full TDD recipe, schema, plug-in points, jsdom constraints, and pre-commit checklist so future sessions can add rules end-to-end without re-investigating the wiring. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
230 lines
8.6 KiB
HTML
230 lines
8.6 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>Icon-Tile-Stack — Should Flag vs Should Pass</title>
|
||
<style>
|
||
/* ─── Layout convention: two-column fixture ─────────────────────────
|
||
Left column = patterns the rule SHOULD flag.
|
||
Right column = legitimate patterns the rule should NOT flag.
|
||
Each test case is annotated with a unique <h3> text so the test
|
||
can match snippets back to expectations.
|
||
──────────────────────────────────────────────────────────────── */
|
||
body { font-family: system-ui, sans-serif; margin: 0; padding: 24px; }
|
||
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 960px; margin: 0 auto; }
|
||
.col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }
|
||
.case { margin-bottom: 24px; padding: 16px; border: 1px solid #e2e8f0; border-radius: 12px; background: #fff; }
|
||
.case h3 { font-size: 18px; margin: 0 0 4px; color: #0f172a; }
|
||
.case p { font-size: 14px; margin: 0; color: #64748b; }
|
||
|
||
/* ── FLAG cases ── */
|
||
|
||
/* 1: pastel-gradient icon-tile (the canonical massive-icons.html shape) */
|
||
.icon-pastel {
|
||
width: 80px; height: 80px;
|
||
border-radius: 20px;
|
||
background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);
|
||
display: flex; align-items: center; justify-content: center;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
/* 2: solid-color icon-tile, white svg inside (no gradient) */
|
||
.icon-solid {
|
||
width: 64px; height: 64px;
|
||
border-radius: 16px;
|
||
background: #3b82f6;
|
||
display: flex; align-items: center; justify-content: center;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
/* 3: white-bg outlined icon-tile (no chromatic background at all) */
|
||
.icon-white {
|
||
width: 72px; height: 72px;
|
||
border-radius: 18px;
|
||
background: #ffffff;
|
||
border: 1px solid #e2e8f0;
|
||
display: flex; align-items: center; justify-content: center;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
/* 4: larger square (still inside the 32–128 range) */
|
||
.icon-large {
|
||
width: 96px; height: 96px;
|
||
border-radius: 24px;
|
||
background: #f1f5f9;
|
||
display: flex; align-items: center; justify-content: center;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
/* ── PASS cases ── */
|
||
|
||
/* a: round avatar above name (border-radius: 50% — circle, not rounded square) */
|
||
.avatar-round {
|
||
width: 64px; height: 64px;
|
||
border-radius: 50%;
|
||
background: #f59e0b;
|
||
display: flex; align-items: center; justify-content: center;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
/* b: wide thumbnail above article title (16:9, much wider than tall) */
|
||
.thumb-wide {
|
||
width: 200px; height: 112px;
|
||
border-radius: 8px;
|
||
background: #cbd5e1;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
/* c: inline icon next to heading (icon and h3 on the same row, NOT stacked) */
|
||
.inline-row { display: flex; align-items: center; gap: 12px; }
|
||
.inline-row .icon-inline {
|
||
width: 80px; height: 80px;
|
||
border-radius: 20px;
|
||
background: #ddd6fe;
|
||
display: flex; align-items: center; justify-content: center;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* d: nothing above the heading at all */
|
||
.no-icon h3 { margin-top: 0; }
|
||
|
||
/* e: icon-tile that's too small (under 32px) — out of size range */
|
||
.icon-tiny {
|
||
width: 24px; height: 24px;
|
||
border-radius: 6px;
|
||
background: #93c5fd;
|
||
display: flex; align-items: center; justify-content: center;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
/* f: oversized image card / hero image (over 128px) — out of size range */
|
||
.img-huge {
|
||
width: 200px; height: 200px;
|
||
border-radius: 16px;
|
||
background: #cbd5e1;
|
||
margin-bottom: 16px;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="grid">
|
||
|
||
<!-- ════════════════════════════════════════════════════════════════
|
||
LEFT COLUMN: should flag
|
||
═══════════════════════════════════════════════════════════════ -->
|
||
<div class="col" data-col="flag">
|
||
<h2>Should flag</h2>
|
||
|
||
<div class="case">
|
||
<div class="icon-pastel">
|
||
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="#3b82f6" stroke-width="1.5">
|
||
<polyline points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>
|
||
</svg>
|
||
</div>
|
||
<h3>Lightning Fast</h3>
|
||
<p>Built for speed and performance from the ground up.</p>
|
||
</div>
|
||
|
||
<div class="case">
|
||
<div class="icon-solid">
|
||
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="1.5">
|
||
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
|
||
</svg>
|
||
</div>
|
||
<h3>Secure Storage</h3>
|
||
<p>Enterprise-grade security for your peace of mind.</p>
|
||
</div>
|
||
|
||
<div class="case">
|
||
<div class="icon-white">
|
||
<svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="#0f172a" stroke-width="1.5">
|
||
<circle cx="12" cy="12" r="10"/>
|
||
<path d="M12 6v6l4 2"/>
|
||
</svg>
|
||
</div>
|
||
<h3>Easy Setup</h3>
|
||
<p>Get started in minutes with zero configuration.</p>
|
||
</div>
|
||
|
||
<div class="case">
|
||
<div class="icon-large">
|
||
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="#0f172a" stroke-width="1.5">
|
||
<line x1="18" y1="20" x2="18" y2="10"/>
|
||
<line x1="12" y1="20" x2="12" y2="4"/>
|
||
<line x1="6" y1="20" x2="6" y2="14"/>
|
||
</svg>
|
||
</div>
|
||
<h3>Powerful Analytics</h3>
|
||
<p>Insights that drive smarter business decisions.</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ════════════════════════════════════════════════════════════════
|
||
RIGHT COLUMN: should pass
|
||
═══════════════════════════════════════════════════════════════ -->
|
||
<div class="col" data-col="pass">
|
||
<h2>Should pass</h2>
|
||
|
||
<!-- a: round avatar (circle, not rounded square) -->
|
||
<div class="case">
|
||
<div class="avatar-round">
|
||
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2">
|
||
<circle cx="12" cy="8" r="4"/>
|
||
<path d="M4 22v-2a8 8 0 0116 0v2"/>
|
||
</svg>
|
||
</div>
|
||
<h3>Sarah Chen</h3>
|
||
<p>Product designer based in Berlin.</p>
|
||
</div>
|
||
|
||
<!-- b: wide 16:9 thumbnail (aspect ratio out of range) -->
|
||
<div class="case">
|
||
<div class="thumb-wide"></div>
|
||
<h3>Article Headline</h3>
|
||
<p>A summary of an interesting article.</p>
|
||
</div>
|
||
|
||
<!-- c: side-by-side icon and heading (not stacked vertically) -->
|
||
<div class="case">
|
||
<div class="inline-row">
|
||
<div class="icon-inline">
|
||
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="#7c3aed" stroke-width="1.5">
|
||
<circle cx="12" cy="12" r="10"/>
|
||
</svg>
|
||
</div>
|
||
<div>
|
||
<h3>Inline Side By Side</h3>
|
||
<p>Icon sits next to the heading, not above it.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- d: no icon above the heading at all -->
|
||
<div class="case no-icon">
|
||
<h3>Plain Heading No Icon</h3>
|
||
<p>Just a heading with body text, nothing above.</p>
|
||
</div>
|
||
|
||
<!-- e: icon-tile too small (24px — under 32px floor) -->
|
||
<div class="case">
|
||
<div class="icon-tiny">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#1e40af" stroke-width="2">
|
||
<circle cx="12" cy="12" r="10"/>
|
||
</svg>
|
||
</div>
|
||
<h3>Tiny Icon Above Me</h3>
|
||
<p>The icon container is smaller than 32px.</p>
|
||
</div>
|
||
|
||
<!-- f: huge image / hero (200px — over 128px ceiling) -->
|
||
<div class="case">
|
||
<div class="img-huge"></div>
|
||
<h3>Huge Hero Image</h3>
|
||
<p>The container is larger than the size cap.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<script src="/js/detect-antipatterns-browser.js"></script>
|
||
</body>
|
||
</html>
|