Files
pbakaus_impeccable/tests/fixtures/antipatterns/icon-tile-stack.html
T
Paul BakausandClaude Opus 4.6 e1032b7285 Add icon-tile-stack rule and cross-validate engine against skill
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>
2026-04-06 22:58:13 -07:00

230 lines
8.6 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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 32128 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>