Merge gallery into /anti-patterns, hide rule ids

Two fixes from the review.

1. Rule id chip hidden. The internal slugs (e.g. 'border-accent-on-rounded')
   are not useful to readers, only to detector code. Drop the
   .rule-card-id element from the card head entirely. The DOM id on
   the article stays so rules can still be anchor-linked.

2. Merge /gallery into /anti-patterns and drop 'Gallery' from the nav.
   'Gallery' in the top nav reads as 'things built with impeccable'
   when it is actually a curated collection of AI-generated UI in the
   wild — the complement to the rule catalog above.

   - Add GALLERY_ITEMS to content/site/anti-patterns-catalog.js
     (11 entries, same ids and copy as the old gallery.html)
   - Render a new 'In the wild' section at the bottom of
     /anti-patterns with a card grid of the 11 specimens, each linking
     to its standalone live example under /antipattern-examples/{id}.html
   - New .gallery-card CSS: square thumbnail, italic display title,
     charcoal body, hover lifts the card and tints the title accent
   - Add an 'In the wild' entry to the anti-patterns TOC sidebar so
     readers can jump to it
   - Drop the 'Gallery' link from the top-level nav in the shared
     header partial and the 4 hand-authored HTML pages. The old
     /gallery route still serves its page directly (for bookmarked
     links), but the nav no longer advertises it and the gallery page
     itself now marks Anti-Patterns as the active nav item.
This commit is contained in:
Paul Bakaus
2026-04-08 12:48:24 -07:00
parent c384e6b568
commit ebe07cbae5
9 changed files with 205 additions and 36 deletions
+33 -7
View File
@@ -19,6 +19,7 @@ import {
CATEGORY_DESCRIPTIONS,
LAYER_LABELS,
LAYER_DESCRIPTIONS,
GALLERY_ITEMS,
} from './lib/sub-pages-data.js';
import { renderMarkdown, slugify } from './lib/render-markdown.js';
import { renderPage } from './lib/render-page.js';
@@ -356,11 +357,16 @@ function renderAntiPatternsSidebar(grouped) {
return `
<aside class="skills-sidebar anti-patterns-sidebar" aria-label="Anti-pattern sections">
<div class="skills-sidebar-inner">
<button class="skills-sidebar-toggle" type="button" aria-expanded="false" aria-controls="anti-patterns-sidebar-inner">
<span class="skills-sidebar-toggle-label">Sections</span>
<svg class="skills-sidebar-toggle-chevron" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg>
</button>
<div class="skills-sidebar-inner" id="anti-patterns-sidebar-inner">
<p class="skills-sidebar-label">Sections</p>
<div class="skills-sidebar-group">
<ul class="skills-sidebar-list anti-patterns-sidebar-list">
${entries}
<li><a href="#in-the-wild"><span>In the wild</span><span class="anti-patterns-sidebar-count">${GALLERY_ITEMS.length}</span></a></li>
</ul>
</div>
</div>
@@ -381,17 +387,13 @@ function renderRuleCard(rule) {
const visual = rule.visual
? `<div class="rule-card-visual" aria-hidden="true"><div class="rule-card-visual-inner">${rule.visual}</div></div>`
: '';
const ruleIdDisplay = rule.layer === 'llm' ? '' : `<code class="rule-card-id">${escapeHtml(rule.id)}</code>`;
return `
<article class="rule-card" id="rule-${rule.id}" data-layer="${layer}">
${visual}
<div class="rule-card-body">
<div class="rule-card-head">
${ruleIdDisplay}
<span class="rule-card-badges">
<span class="rule-card-category" data-category="${rule.category}">${categoryLabel}</span>
<span class="rule-card-layer" data-layer="${layer}" title="${escapeAttr(layerTitle)}">${escapeHtml(layerLabel)}</span>
</span>
<span class="rule-card-category" data-category="${rule.category}">${categoryLabel}</span>
<span class="rule-card-layer" data-layer="${layer}" title="${escapeAttr(layerTitle)}">${escapeHtml(layerLabel)}</span>
</div>
<h3 class="rule-card-name">${escapeHtml(rule.name)}</h3>
<p class="rule-card-desc">${escapeHtml(rule.description)}</p>
@@ -481,6 +483,19 @@ ${rules.map(renderRuleCard).join('\n')}
.filter((r) => r.layer !== 'llm').length;
const llmCount = totalRules - detectedCount;
const galleryHtml = GALLERY_ITEMS.map(
(item) => `
<a class="gallery-card" href="/antipattern-examples/${item.id}.html">
<div class="gallery-card-thumb">
<img src="/antipattern-images/${item.id}.png" alt="${escapeAttr(item.title)} example" loading="lazy" width="540" height="540">
</div>
<div class="gallery-card-body">
<h3 class="gallery-card-title">${escapeHtml(item.title)}</h3>
<p class="gallery-card-desc">${escapeHtml(item.desc)}</p>
</div>
</a>`,
).join('\n');
return `
<div class="anti-patterns-content">
<header class="anti-patterns-header">
@@ -507,6 +522,17 @@ ${rules.map(renderRuleCard).join('\n')}
<div class="anti-patterns-sections">
${sectionsHtml}
</div>
<section class="gallery-section" id="in-the-wild">
<header class="anti-patterns-section-header">
<h2 class="anti-patterns-section-title">In the wild</h2>
<p class="anti-patterns-section-count">${GALLERY_ITEMS.length} specimens</p>
</header>
<p class="gallery-section-lede">Real examples scraped from the web. Click any to see the live page with the overlay running.</p>
<div class="gallery-grid">
${galleryHtml}
</div>
</section>
</div>`;
}
+6 -1
View File
@@ -18,9 +18,14 @@ import {
DETECTION_LAYERS,
VISUAL_EXAMPLES,
LLM_ONLY_RULES,
GALLERY_ITEMS,
} from '../../content/site/anti-patterns-catalog.js';
export { LAYER_LABELS, LAYER_DESCRIPTIONS } from '../../content/site/anti-patterns-catalog.js';
export {
LAYER_LABELS,
LAYER_DESCRIPTIONS,
GALLERY_ITEMS,
} from '../../content/site/anti-patterns-catalog.js';
/**
* Skills that should be excluded from the index and not get a detail page.