Files
pbakaus_impeccable/tests/fixtures/antipatterns/layout-should-pass.html
T
Paul BakausandClaude Opus 4.6 72b9ca2941 Add layout anti-pattern detection: nested cards, identical grids, spacing, centering
Four new layout detections:
- nested-cards: jsdom DOM walk finds card-like elements (shadow + rounded + bg)
  nested inside other card-like elements. Excludes dropdowns (absolute/fixed),
  form inputs, code blocks, badges (<20 chars), and known component classes.
- identical-card-grid: detects grid/flex parents with 3+ children sharing the
  same structural fingerprint (icon + heading + paragraph template pattern).
- monotonous-spacing: regex on raw HTML collects padding/margin/gap values
  (px, rem, Tailwind classes), rounds to nearest 4px, flags when >60% use
  the same value with <=3 distinct values.
- everything-centered: regex counts text-align:center and Tailwind text-center
  on text elements, flags when >70% of 5+ text elements are centered.

Also narrowed pure-black-white to only flag #000 as background color —
text-black, text-white, bg-white, and #fff are no longer flagged (too
common, per user feedback).

Extensive should-pass fixture covers: shadcn card sub-components, cards
with form inputs/dropdowns/code blocks/badges/accordions/tabs/images,
pricing cards, varied spacing, mixed centered/left-aligned layouts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 15:15:40 -07:00

243 lines
12 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Layout — Clean Patterns (Should NOT Flag)</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
body { font-family: system-ui, sans-serif; background: #f9fafb; padding: 2rem; color: #111827; }
h1 { font-size: 2rem; margin-bottom: 0.5rem; }
h2 { font-size: 1.25rem; margin: 2.5rem 0 0.75rem; color: #6b7280; border-bottom: 1px solid #e5e7eb; padding-bottom: 0.5rem; }
p.intro { color: #6b7280; margin-bottom: 2rem; }
</style>
</head>
<body>
<h1>Layout — Should Pass</h1>
<p class="intro">All patterns here are legitimate. None should be flagged.</p>
<!-- ============================================================ -->
<!-- shadcn-style Card sub-components (NOT nested cards) -->
<!-- ============================================================ -->
<h2>shadcn Card Sub-Components</h2>
<!-- Card with CardHeader / CardContent / CardFooter structure -->
<div class="rounded-lg border bg-white shadow-sm max-w-md mb-4">
<div class="flex flex-col space-y-1.5 p-6">
<h3 class="text-lg font-semibold">Card Title</h3>
<p class="text-sm text-gray-500">Card description goes here.</p>
</div>
<div class="p-6 pt-0">
<p class="text-sm text-gray-600">This is CardContent — a sub-section, not a nested card. No shadow, no border-radius of its own.</p>
</div>
<div class="flex items-center p-6 pt-0">
<button class="px-4 py-2 bg-gray-900 text-white text-sm rounded-md">Action</button>
</div>
</div>
<!-- ============================================================ -->
<!-- Card with form inputs (inputs have shadow/rounded) -->
<!-- ============================================================ -->
<h2>Card with Form Inputs</h2>
<div class="bg-white rounded-lg shadow-sm p-6 max-w-md mb-4 border">
<h3 class="font-semibold mb-4">Settings</h3>
<div class="space-y-3">
<input type="text" placeholder="Name" class="w-full px-3 py-2 border rounded-md shadow-sm text-sm focus:outline-none focus:ring-2">
<input type="email" placeholder="Email" class="w-full px-3 py-2 border rounded-md shadow-sm text-sm focus:outline-none focus:ring-2">
<select class="w-full px-3 py-2 border rounded-md shadow-sm text-sm bg-white">
<option>Select option</option>
</select>
<textarea placeholder="Bio" class="w-full px-3 py-2 border rounded-md shadow-sm text-sm h-20 resize-none"></textarea>
</div>
</div>
<!-- ============================================================ -->
<!-- Card with dropdown/popover (has shadow + rounded) -->
<!-- ============================================================ -->
<h2>Card with Dropdown</h2>
<div class="bg-white rounded-lg shadow-sm p-6 max-w-md mb-4 border relative">
<h3 class="font-semibold mb-2">Select Plan</h3>
<p class="text-sm text-gray-600 mb-3">Choose your subscription tier.</p>
<!-- Simulated dropdown menu -->
<div class="absolute top-full left-6 mt-1 w-48 bg-white rounded-md shadow-lg border py-1 z-10">
<div class="px-3 py-2 text-sm hover:bg-gray-50 cursor-pointer">Free</div>
<div class="px-3 py-2 text-sm hover:bg-gray-50 cursor-pointer">Pro</div>
<div class="px-3 py-2 text-sm hover:bg-gray-50 cursor-pointer">Enterprise</div>
</div>
</div>
<div style="height: 80px;"></div> <!-- spacer for dropdown -->
<!-- ============================================================ -->
<!-- Card with code block inside -->
<!-- ============================================================ -->
<h2>Card with Code Block</h2>
<div class="bg-white rounded-lg shadow-sm p-6 max-w-md mb-4 border">
<h3 class="font-semibold mb-3">Installation</h3>
<pre class="bg-gray-900 text-gray-100 rounded-md p-4 text-sm overflow-x-auto"><code>npm install @acme/sdk</code></pre>
</div>
<!-- ============================================================ -->
<!-- Card with badge/chip inside -->
<!-- ============================================================ -->
<h2>Card with Badges</h2>
<div class="bg-white rounded-lg shadow-sm p-6 max-w-md mb-4 border">
<h3 class="font-semibold mb-3">Tags</h3>
<div class="flex flex-wrap gap-2">
<span class="px-2 py-1 bg-blue-100 text-blue-700 rounded-full text-xs font-medium">React</span>
<span class="px-2 py-1 bg-green-100 text-green-700 rounded-full text-xs font-medium">TypeScript</span>
<span class="px-2 py-1 bg-purple-100 text-purple-700 rounded-full text-xs font-medium">Tailwind</span>
</div>
</div>
<!-- ============================================================ -->
<!-- Card with accordion/collapsible inside -->
<!-- ============================================================ -->
<h2>Card with Accordion</h2>
<div class="bg-white rounded-lg shadow-sm max-w-md mb-4 border">
<div class="p-4 border-b cursor-pointer flex justify-between items-center">
<span class="font-medium text-sm">How does billing work?</span>
<span class="text-gray-400">+</span>
</div>
<div class="p-4 border-b cursor-pointer flex justify-between items-center">
<span class="font-medium text-sm">Can I cancel anytime?</span>
<span class="text-gray-400">+</span>
</div>
<div class="p-4 cursor-pointer flex justify-between items-center">
<span class="font-medium text-sm">Do you offer refunds?</span>
<span class="text-gray-400">+</span>
</div>
</div>
<!-- ============================================================ -->
<!-- Card with image (rounded + shadow on image) -->
<!-- ============================================================ -->
<h2>Card with Styled Image</h2>
<div class="bg-white rounded-lg shadow-sm p-6 max-w-md mb-4 border">
<div class="w-full h-40 bg-gradient-to-br from-blue-400 to-purple-500 rounded-lg shadow-inner mb-4"></div>
<h3 class="font-semibold">Project Preview</h3>
<p class="text-sm text-gray-600 mt-1">Image placeholder with rounded corners and shadow.</p>
</div>
<!-- ============================================================ -->
<!-- Card with tab panels inside -->
<!-- ============================================================ -->
<h2>Card with Tabs</h2>
<div class="bg-white rounded-lg shadow-sm max-w-md mb-4 border">
<div class="flex border-b">
<button class="px-4 py-3 text-sm font-medium text-blue-600 border-b-2 border-blue-600">Overview</button>
<button class="px-4 py-3 text-sm text-gray-500">Analytics</button>
<button class="px-4 py-3 text-sm text-gray-500">Settings</button>
</div>
<div class="p-6">
<p class="text-sm text-gray-600">Tab content area — structured content inside a card, not a nested card.</p>
</div>
</div>
<!-- ============================================================ -->
<!-- Pricing cards (similar but intentionally comparative) -->
<!-- ============================================================ -->
<h2>Pricing Cards (Intentionally Similar)</h2>
<div class="grid grid-cols-3 gap-4 max-w-3xl mb-4">
<div class="bg-white rounded-lg shadow-sm p-6 border">
<h3 class="font-semibold mb-1">Free</h3>
<div class="text-2xl font-bold mb-4">$0</div>
<ul class="space-y-2 text-sm text-gray-600">
<li>5 projects</li>
<li>1 GB storage</li>
<li>Community support</li>
</ul>
</div>
<div class="bg-white rounded-lg shadow-sm p-6 border-2 border-blue-500">
<h3 class="font-semibold mb-1">Pro</h3>
<div class="text-2xl font-bold mb-4">$29</div>
<ul class="space-y-2 text-sm text-gray-600">
<li>Unlimited projects</li>
<li>50 GB storage</li>
<li>Priority support</li>
</ul>
</div>
<div class="bg-white rounded-lg shadow-sm p-6 border">
<h3 class="font-semibold mb-1">Enterprise</h3>
<div class="text-2xl font-bold mb-4">Custom</div>
<ul class="space-y-2 text-sm text-gray-600">
<li>Everything in Pro</li>
<li>Unlimited storage</li>
<li>Dedicated support</li>
</ul>
</div>
</div>
<!-- ============================================================ -->
<!-- Good spacing variety -->
<!-- ============================================================ -->
<h2>Varied Spacing (Good Rhythm)</h2>
<div style="max-width: 28rem; margin-bottom: 1rem;">
<div style="background: white; padding: 2rem; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); margin-bottom: 2rem;">
<h3 style="font-weight: 600; margin-bottom: 0.5rem; font-size: 1.25rem;">Heading with tight spacing</h3>
<p style="font-size: 0.875rem; color: #6b7280; margin-bottom: 1.5rem;">Body text with medium spacing below.</p>
<div style="padding: 1rem; background: #f3f4f6; border-radius: 6px;">
<p style="font-size: 0.8125rem; color: #6b7280;">Nested content with different padding.</p>
</div>
</div>
<div style="background: white; padding: 1.5rem; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
<h3 style="font-weight: 600; margin-bottom: 0.75rem;">Another section</h3>
<p style="font-size: 0.875rem; color: #6b7280;">Different padding than above — intentional rhythm.</p>
</div>
</div>
<!-- ============================================================ -->
<!-- Centered hero section (legitimately centered) -->
<!-- ============================================================ -->
<h2>Centered Hero (Legitimate)</h2>
<div style="text-align: center; max-width: 32rem; margin: 0 auto 1rem; padding: 2rem 0;">
<h3 style="font-size: 1.5rem; font-weight: 700; margin-bottom: 0.5rem;">Hero Heading</h3>
<p style="color: #6b7280; margin-bottom: 1.5rem;">A centered hero section is fine — it's the rest of the page that shouldn't all be centered too.</p>
<button style="padding: 0.75rem 1.5rem; background: #111827; color: white; border: none; border-radius: 6px; font-weight: 500;">Get Started</button>
</div>
<!-- Left-aligned content following the hero -->
<div style="max-width: 32rem; margin-bottom: 1rem;">
<h3 style="font-size: 1.25rem; font-weight: 600; margin-bottom: 0.5rem;">Features</h3>
<p style="color: #6b7280; margin-bottom: 1rem;">This content is left-aligned, creating contrast with the centered hero above.</p>
<ul style="color: #4b5563; font-size: 0.875rem; padding-left: 1.25rem; list-style: disc;">
<li style="margin-bottom: 0.5rem;">Left-aligned list items</li>
<li style="margin-bottom: 0.5rem;">Natural reading direction</li>
<li>Intentional layout variety</li>
</ul>
</div>
<!-- ============================================================ -->
<!-- Non-card grid (varied structure) -->
<!-- ============================================================ -->
<h2>Grid with Varied Card Structures</h2>
<div class="grid grid-cols-2 gap-4 max-w-2xl mb-4">
<div class="bg-white rounded-lg shadow-sm p-6 border col-span-2">
<h3 class="font-semibold text-lg mb-2">Featured Item</h3>
<p class="text-sm text-gray-600">This card spans the full width — different from the others.</p>
</div>
<div class="bg-white rounded-lg shadow-sm p-4 border">
<h3 class="font-semibold text-sm">Compact Card</h3>
<p class="text-xs text-gray-500 mt-1">Smaller, less padding.</p>
</div>
<div class="bg-blue-50 rounded-lg p-4 border border-blue-200">
<h3 class="font-semibold text-sm text-blue-900">Highlighted</h3>
<p class="text-xs text-blue-700 mt-1">Different bg, no shadow — visual variety.</p>
</div>
</div>
<script src="/js/detect-antipatterns-browser.js"></script>
</body>
</html>