/** * Manual metadata for the /anti-patterns page. * * The detection rules themselves live in cli/engine/registry/antipatterns.mjs. * This file adds three pieces of content that * can't be automated: * * 1. DETECTION_LAYERS: which layer (cli, browser, or llm) catches the * rule. Manually classified by reading the detector source and the * browser-only test file. * * 2. VISUAL_EXAMPLES: a tiny inline HTML snippet showing what the * bad pattern actually looks like. Rendered inside each rule card. * Snippets should be self-contained with inline styles, use the * cream/paper/ink palette when possible, and sit naturally at * ~100% width by ~120px height. * * 3. LLM_ONLY_RULES: DON'T lines from skill/SKILL.src.md * that do not map to any detection rule. These can only be caught by * the /critique skill's LLM pass. They appear on the /anti-patterns * page alongside detected rules with an 'llm' layer badge. */ // ─── Detection layers ──────────────────────────────────────────────── /** * Which layer catches each rule. * * 'cli': static analysis or jsdom (works with `npx impeccable detect` * on files, no browser required) * 'browser': requires real browser layout (getBoundingClientRect with * actual dimensions). Works via Puppeteer or the browser * extension, NOT via the CLI on raw HTML. * 'llm': no deterministic detector; only caught by /critique's LLM * assessment pass. * * Per tests/detect-antipatterns-browser.test.mjs: only two rules genuinely * need real browser layout. Everything else is 'cli'. */ export const DETECTION_LAYERS = { 'side-tab': 'cli', 'border-accent-on-rounded': 'cli', 'overused-font': 'cli', 'single-font': 'cli', 'flat-type-hierarchy': 'cli', 'icon-tile-stack': 'cli', 'gradient-text': 'cli', 'ai-color-palette': 'cli', 'dark-glow': 'cli', 'nested-cards': 'cli', 'monotonous-spacing': 'cli', 'everything-centered': 'cli', 'bounce-easing': 'cli', 'all-caps-body': 'cli', 'pure-black-white': 'cli', 'gray-on-color': 'cli', 'low-contrast': 'cli', 'layout-transition': 'cli', 'tight-leading': 'cli', 'skipped-heading': 'cli', 'justified-text': 'cli', 'tiny-text': 'cli', 'wide-tracking': 'cli', // Browser-only: need real layout measurements. 'cramped-padding': 'browser', 'line-length': 'browser', }; export const LAYER_LABELS = { cli: 'CLI', browser: 'Browser', llm: 'LLM only', }; export const LAYER_DESCRIPTIONS = { cli: 'Deterministic. Runs from `npx impeccable detect` on files, no browser required.', browser: 'Deterministic, but needs real browser layout. Runs via the browser extension or Puppeteer, not the plain CLI.', llm: 'Not caught by any deterministic detector. Flagged by /impeccable critique during its LLM design review.', }; // ─── Visual examples ───────────────────────────────────────────────── /** * One tiny inline HTML snippet per rule showing what the bad pattern * looks like. Snippets use inline styles only and are sized to fit the * rule card preview area (~100% wide, ~120px tall). */ export const VISUAL_EXAMPLES = { 'side-tab': `
Alert title
Thick colored stripe on one side.
`, 'border-accent-on-rounded': `
Rounded card
Thick colored border clashes with the radius.
`, 'overused-font': `
Just another Inter headline
Every SaaS homepage looks like this.
`, 'single-font': `
Heading in the body font
Body in the same font. No contrast. Flat.
`, 'flat-type-hierarchy': `
Heading
Subheading
Body text at almost the same size.
`, 'icon-tile-stack': `
Feature name
Rounded icon tile above heading.
`, 'gradient-text': `
Build the Future
Gradient text kills scannability.
`, 'ai-color-palette': `
`, 'dark-glow': `
Neon on dark
Cyberpunk-by-default slop.
`, 'nested-cards': `
Card inside card inside card.
`, 'monotonous-spacing': `
`, 'everything-centered': `
Centered headline
Everything centered by default.
Call to action
`, 'bounce-easing': `
Bounce + elastic easing feels dated.
`, 'all-caps-body': `
Long passages in uppercase are hard to read. We recognize words by their shape, which all-caps removes.
`, 'pure-black-white': `
Pure black on pure white
Neither exists in nature. Always tint.
`, 'gray-on-color': `
Gray text on a colored background. Washed out and hard to read.
`, 'low-contrast': `
Light gray text on a white background. 1.6:1 contrast, fails WCAG.
`, 'layout-transition': `
Animating width/height causes layout jank.
`, 'cramped-padding': `
2px vertical padding.
`, 'tight-leading': `
Tight leading makes multi-line body text feel crammed and hard for the eye to track between lines.
`, 'skipped-heading': `

Page title (h1)

Subsection (h3), skipped h2

`, 'justified-text': `
Justified text on screens creates rivers of whitespace because browsers can't hyphenate well. Leave this for print.
`, 'tiny-text': `
Regular body text
And then fine print at 9 pixels that no one will ever read.
`, 'wide-tracking': `
Wide tracking on body text slows reading by breaking up natural character groupings.
`, 'line-length': `
Paragraphs wider than roughly 75 characters per line become fatiguing because the eye has to track an excessive distance back to the start of the next line, losing its place.
`, // ── LLM-only rule visuals ───────────────────────────────────────── 'monospace-as-technical': `
TECHNICAL_TOOL
Mono for "developer" vibes. Lazy.
`, 'dark-mode-default': `
Dark by default
Defaulting to dark is a retreat from a decision.
`, 'everything-in-cards': `
Title
Card around every single thing.
`, 'identical-card-grids': `
${'
Feature
Short copy.
'.repeat(6)}
`, 'hero-metric-layout': `
10M+
Active users
99.9% uptime200ms p50
`, 'glassmorphism': `
Frosted glass card
`, 'sparkline-decoration': `
Revenue
$42.1k
Tiny chart, no real information.
`, 'generic-drop-shadows': `
`, 'modal-reflex': `
Are you sure?
Really, truly sure about this?
Cancel
OK
`, 'every-button-primary': `
Every action shouts equally.
`, 'redundant-headers': `
Overview
This is the overview section, which provides an overview of the overview.
`, 'mobile-amputation': `
Export to CSV
"Not available on mobile."
`, }; /** * Anti-patterns that live in the /impeccable skill's DON'T list but * don't have a deterministic detector. These can only be caught by * /critique running an LLM assessment pass. * * Each entry looks like a detection rule: id, category, name, * description, skillSection. The generator merges these into the * grouped sections alongside detected rules with an 'llm' layer badge. */ // ─── Gallery: real examples in the wild ────────────────────────────── /** * Curated real-world examples of anti-patterns caught in the wild. * Each entry maps to: * - public/antipattern-images/{id}.png (preview thumbnail) * - public/antipattern-examples/{id}.html (standalone live example) * Rendered as a dedicated section on the /anti-patterns page, replacing * the old /gallery route which was confusingly labeled in the top nav. */ export const GALLERY_ITEMS = [ { id: 'purple-gradients', title: 'Purple Gradients Everywhere', desc: 'The AI color palette: purple-to-blue gradients on everything. Buttons, text, backgrounds, orbs. The new "make it pop."', }, { id: 'lazy-cool', title: 'Lazy "Cool"', desc: 'Glassmorphism, neon glows, blurred orbs, monospace everything. Looks like a hackathon project, not a product.', }, { id: 'lazy-impact', title: 'Lazy "Impact"', desc: 'When in doubt, animate everything. Bouncing buttons, wiggling icons, gradient text, floating badges. Motion without meaning.', }, { id: 'thick-border-cards', title: 'Side-Tab Cards', desc: 'A thick colored border on one side of a rounded card. The single most recognizable tell of AI-generated UI.', }, { id: 'cardocalypse', title: 'Cardocalypse', desc: 'Cards inside cards inside cards. Five levels of nesting, each with its own padding and shadow.', }, { id: 'layout-templates', title: 'Copy-Paste Layouts', desc: 'The same hero-metric-features template repeated with different colors. When every section looks the same, nothing stands out.', }, { id: 'inter-everywhere', title: 'Inter Everywhere', desc: 'One font for everything. Headings, body, labels, buttons. No typographic hierarchy, no personality, no design.', }, { id: 'massive-icons', title: 'Massive Icons', desc: 'Icon containers larger than the content they introduce. When the decoration is bigger than the message, priorities are backwards.', }, { id: 'bad-contrast', title: 'Bad Contrast Choices', desc: 'Gray text on colored backgrounds, low-contrast labels, unreadable combinations. Looking good and being readable should not conflict.', }, { id: 'redundant-ux-writing', title: 'Redundant UX Writing', desc: 'Label, sublabel, helper text, and hint text all saying the same thing in slightly different words. Say it once, say it well.', }, { id: 'modal-abuse', title: 'Modal Abuse', desc: 'Complex settings crammed into a modal. If it needs a scroll bar and three columns, it deserves its own page.', }, ]; // ─── LLM-only rules ────────────────────────────────────────────────── export const LLM_ONLY_RULES = [ { id: 'monospace-as-technical', category: 'slop', name: 'Monospace as "technical" shorthand', description: 'Using a monospace typeface to signal "developer / technical" vibes. Reach for real type choices instead of a lazy stereotype.', skillSection: 'Typography', }, { id: 'dark-mode-default', category: 'slop', name: 'Defaulting to dark mode for "safety"', description: 'Defaulting to light mode to be safe is the inverse of defaulting to dark mode to look cool. Either way you are retreating from a decision.', skillSection: 'Color & Contrast', }, { id: 'everything-in-cards', category: 'slop', name: 'Wrapping everything in cards', description: 'Not every piece of content needs a bordered container. Spacing and alignment create visual grouping without the overhead of a card.', skillSection: 'Layout & Space', }, { id: 'identical-card-grids', category: 'slop', name: 'Identical card grids', description: 'Same-sized cards with icon + heading + text repeated endlessly. The default AI homepage layout.', skillSection: 'Layout & Space', }, { id: 'hero-metric-layout', category: 'slop', name: 'Hero metric layout', description: 'Big number, small label, three supporting stats, gradient accent. Used everywhere, trusted nowhere.', skillSection: 'Layout & Space', }, { id: 'glassmorphism', category: 'slop', name: 'Glassmorphism everywhere', description: 'Blur effects, glass cards, and glow borders used as decoration rather than to solve a real layering problem.', skillSection: 'Visual Details', }, { id: 'sparkline-decoration', category: 'slop', name: 'Sparklines as decoration', description: 'Tiny charts that look sophisticated but convey no meaningful information. If the data matters, give it room.', skillSection: 'Visual Details', }, { id: 'generic-drop-shadows', category: 'slop', name: 'Rounded rectangles with generic drop shadows', description: 'The safest, most forgettable shape on the web. Could be the output of any AI. Commit to a stronger visual treatment.', skillSection: 'Visual Details', }, { id: 'modal-reflex', category: 'slop', name: 'Reaching for modals by reflex', description: 'Modals interrupt the user and are lazy as a design default. Use them only when there is truly no better place for the interaction.', skillSection: 'Visual Details', }, { id: 'every-button-primary', category: 'quality', name: 'Every button is a primary button', description: 'When every button looks equally important, nothing reads as the primary action. Use ghost buttons, text links, and secondary styles to build hierarchy.', skillSection: 'Interaction', }, { id: 'redundant-headers', category: 'quality', name: 'Redundant information', description: 'Intros that restate the heading. Section labels that repeat the page title. Cards that echo their own caption. Make every word earn its place.', skillSection: 'Interaction', }, { id: 'mobile-amputation', category: 'quality', name: 'Amputating features on mobile', description: 'Hiding critical functionality on mobile because it is inconvenient. Adapt the interface to the context, do not strip it.', skillSection: 'Responsive', }, ];