1/**
2 * Manual metadata for the /anti-patterns page.
3 *
4 * The detection rules themselves live in src/detect-antipatterns.mjs and
5 * are parsed at build time. This file adds three pieces of content that
6 * can't be automated:
7 *
8 * 1. DETECTION_LAYERS: which layer (cli, browser, or llm) catches the
9 * rule. Manually classified by reading the detector source and the
10 * browser-only test file.
11 *
12 * 2. VISUAL_EXAMPLES: a tiny inline HTML snippet showing what the
13 * bad pattern actually looks like. Rendered inside each rule card.
14 * Snippets should be self-contained with inline styles, use the
15 * cream/paper/ink palette when possible, and sit naturally at
16 * ~100% width by ~120px height.
17 *
18 * 3. LLM_ONLY_RULES: DON'T lines from source/skills/impeccable/SKILL.md
19 * that do not map to any detection rule. These can only be caught by
20 * the /critique skill's LLM pass. They appear on the /anti-patterns
21 * page alongside detected rules with an 'llm' layer badge.
22 */
23
24// ─── Detection layers ────────────────────────────────────────────────
25
26/**
27 * Which layer catches each rule.
28 *
29 * 'cli': static analysis or jsdom (works with `npx impeccable detect`
30 * on files, no browser required)
31 * 'browser': requires real browser layout (getBoundingClientRect with
32 * actual dimensions). Works via Puppeteer or the browser
33 * extension, NOT via the CLI on raw HTML.
34 * 'llm': no deterministic detector; only caught by /critique's LLM
35 * assessment pass.
36 *
37 * Per tests/detect-antipatterns-browser.test.mjs: only two rules genuinely
38 * need real browser layout. Everything else is 'cli'.
39 */
40export const DETECTION_LAYERS = {
41 'side-tab': 'cli',
42 'border-accent-on-rounded': 'cli',
43 'overused-font': 'cli',
44 'single-font': 'cli',
45 'flat-type-hierarchy': 'cli',
46 'icon-tile-stack': 'cli',
47 'gradient-text': 'cli',
48 'ai-color-palette': 'cli',
49 'dark-glow': 'cli',
50 'nested-cards': 'cli',
51 'monotonous-spacing': 'cli',
52 'everything-centered': 'cli',
53 'bounce-easing': 'cli',
54 'all-caps-body': 'cli',
55 'pure-black-white': 'cli',
56 'gray-on-color': 'cli',
57 'low-contrast': 'cli',
58 'layout-transition': 'cli',
59 'tight-leading': 'cli',
60 'skipped-heading': 'cli',
61 'justified-text': 'cli',
62 'tiny-text': 'cli',
63 'wide-tracking': 'cli',
64 // Browser-only: need real layout measurements.
65 'cramped-padding': 'browser',
66 'line-length': 'browser',
67};
68
69export const LAYER_LABELS = {
70 cli: 'CLI',
71 browser: 'Browser',
72 llm: 'LLM only',
73};
74
75export const LAYER_DESCRIPTIONS = {
76 cli: 'Deterministic. Runs from `npx impeccable detect` on files, no browser required.',
77 browser: 'Deterministic, but needs real browser layout. Runs via the browser extension or Puppeteer, not the plain CLI.',
78 llm: 'Not caught by any deterministic detector. Flagged by /critique during its LLM design review.',
79};
80
81// ─── Visual examples ─────────────────────────────────────────────────
82
83/**
84 * One tiny inline HTML snippet per rule showing what the bad pattern
85 * looks like. Snippets use inline styles only and are sized to fit the
86 * rule card preview area (~100% wide, ~120px tall).
87 */
88export const VISUAL_EXAMPLES = {
89 'side-tab': `<div style="background: #fff; border: 1px solid #e8e4df; border-left: 4px solid oklch(60% 0.22 265); border-radius: 6px; padding: 14px 16px; width: 220px; font-family: system-ui, sans-serif; font-size: 13px; color: #111;"><div style="font-weight: 600; margin-bottom: 4px;">Alert title</div><div style="color: #666; font-size: 12px;">Thick colored stripe on one side.</div></div>`,
90
91 'border-accent-on-rounded': `<div style="background: #fff; border: 2px solid oklch(60% 0.22 290); border-radius: 16px; padding: 14px 18px; width: 220px; font-family: system-ui, sans-serif; font-size: 13px; color: #111;"><div style="font-weight: 600;">Rounded card</div><div style="color: #666; font-size: 12px;">Thick colored border clashes with the radius.</div></div>`,
92
93 'overused-font': `<div style="font-family: Inter, system-ui, sans-serif; font-size: 15px; color: #111; line-height: 1.4;"><div style="font-weight: 600; margin-bottom: 4px;">Just another Inter headline</div><div style="color: #555; font-size: 13px;">Every SaaS homepage looks like this.</div></div>`,
94
95 'single-font': `<div style="font-family: system-ui, sans-serif; font-size: 14px; color: #111;"><div style="font-size: 19px; font-weight: 600; margin-bottom: 6px;">Heading in the body font</div><div style="color: #555;">Body in the same font. No contrast. Flat.</div></div>`,
96
97 'flat-type-hierarchy': `<div style="font-family: system-ui, sans-serif; color: #111; line-height: 1.3;"><div style="font-size: 17px; font-weight: 600;">Heading</div><div style="font-size: 16px; font-weight: 500; margin: 2px 0;">Subheading</div><div style="font-size: 15px; color: #555;">Body text at almost the same size.</div></div>`,
98
99 'icon-tile-stack': `<div style="font-family: system-ui, sans-serif; color: #111;"><div style="width: 44px; height: 44px; border-radius: 10px; background: linear-gradient(135deg, oklch(62% 0.22 265), oklch(70% 0.20 320)); display: flex; align-items: center; justify-content: center; font-size: 20px; color: #fff; margin-bottom: 10px;">✦</div><div style="font-size: 14px; font-weight: 600; margin-bottom: 2px;">Feature name</div><div style="font-size: 12px; color: #666;">Rounded icon tile above heading.</div></div>`,
100
101 'gradient-text': `<div style="font-family: system-ui, sans-serif;"><div style="font-size: 28px; font-weight: 700; background: linear-gradient(135deg, oklch(65% 0.25 320), oklch(60% 0.25 265)); -webkit-background-clip: text; background-clip: text; color: transparent; line-height: 1.1;">Build the Future</div><div style="font-size: 12px; color: #888; margin-top: 4px;">Gradient text kills scannability.</div></div>`,
102
103 'ai-color-palette': `<div style="display: flex; gap: 6px;"><div style="width: 44px; height: 44px; border-radius: 6px; background: oklch(60% 0.22 265);"></div><div style="width: 44px; height: 44px; border-radius: 6px; background: oklch(62% 0.25 300);"></div><div style="width: 44px; height: 44px; border-radius: 6px; background: oklch(64% 0.25 340);"></div><div style="width: 44px; height: 44px; border-radius: 6px; background: oklch(70% 0.20 200);"></div></div>`,
104
105 'dark-glow': `<div style="background: #0a0b14; padding: 18px 20px; border-radius: 10px; font-family: system-ui, sans-serif;"><div style="color: oklch(78% 0.22 280); text-shadow: 0 0 12px oklch(78% 0.22 280 / 0.7); font-size: 16px; font-weight: 600;">Neon on dark</div><div style="color: oklch(60% 0.12 260); font-size: 12px; margin-top: 4px;">Cyberpunk-by-default slop.</div></div>`,
106
107 'nested-cards': `<div style="background: #f5f3ef; border: 1px solid #e0dcd4; border-radius: 10px; padding: 10px;"><div style="background: #fff; border: 1px solid #e8e4df; border-radius: 8px; padding: 10px;"><div style="background: #f5f3ef; border: 1px solid #e8e4df; border-radius: 6px; padding: 8px; font-size: 12px; font-family: system-ui, sans-serif; color: #555;">Card inside card inside card.</div></div></div>`,
108
109 'monotonous-spacing': `<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px;"><div style="background: #fff; border: 1px solid #e8e4df; border-radius: 6px; height: 48px;"></div><div style="background: #fff; border: 1px solid #e8e4df; border-radius: 6px; height: 48px;"></div><div style="background: #fff; border: 1px solid #e8e4df; border-radius: 6px; height: 48px;"></div><div style="background: #fff; border: 1px solid #e8e4df; border-radius: 6px; height: 48px;"></div><div style="background: #fff; border: 1px solid #e8e4df; border-radius: 6px; height: 48px;"></div><div style="background: #fff; border: 1px solid #e8e4df; border-radius: 6px; height: 48px;"></div></div>`,
110
111 'everything-centered': `<div style="font-family: system-ui, sans-serif; text-align: center; color: #111;"><div style="font-size: 16px; font-weight: 600; margin-bottom: 6px;">Centered headline</div><div style="font-size: 12px; color: #555; margin-bottom: 10px;">Everything centered by default.</div><div style="display: inline-block; background: #111; color: #fff; padding: 6px 14px; border-radius: 6px; font-size: 12px;">Call to action</div></div>`,
112
113 'bounce-easing': `<div style="font-family: system-ui, sans-serif; color: #111; display: flex; align-items: center; gap: 10px;"><div style="width: 36px; height: 36px; border-radius: 50%; background: oklch(65% 0.22 265); animation: bouncey 0.9s cubic-bezier(0.68, -0.55, 0.27, 1.55) infinite;"></div><div style="font-size: 12px; color: #555;">Bounce + elastic easing feels dated.</div><style>@keyframes bouncey { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-10px); } }</style></div>`,
114
115 'all-caps-body': `<div style="font-family: system-ui, sans-serif; color: #111; font-size: 12px; text-transform: uppercase; letter-spacing: 0.03em; line-height: 1.5;">Long passages in uppercase are hard to read. We recognize words by their shape, which all-caps removes.</div>`,
116
117 'pure-black-white': `<div style="background: #ffffff; padding: 16px 18px; color: #000000; font-family: system-ui, sans-serif; font-size: 14px;"><div style="font-weight: 600; margin-bottom: 4px;">Pure black on pure white</div><div style="font-size: 12px; color: #000;">Neither exists in nature. Always tint.</div></div>`,
118
119 'gray-on-color': `<div style="background: oklch(60% 0.20 265); padding: 16px 18px; border-radius: 6px; font-family: system-ui, sans-serif;"><div style="color: #9ca3af; font-size: 13px;">Gray text on a colored background. Washed out and hard to read.</div></div>`,
120
121 'low-contrast': `<div style="background: #fff; padding: 16px 18px; font-family: system-ui, sans-serif;"><div style="color: #d4d4d4; font-size: 13px;">Light gray text on a white background. 1.6:1 contrast, fails WCAG.</div></div>`,
122
123 'layout-transition': `<div style="font-family: system-ui, sans-serif; color: #111; display: flex; align-items: center; gap: 10px;"><div style="background: oklch(65% 0.22 265); border-radius: 6px; animation: janky 1.2s ease-in-out infinite; width: 60px; height: 30px;"></div><div style="font-size: 12px; color: #555;">Animating width/height causes layout jank.</div><style>@keyframes janky { 0%,100% { width: 60px; } 50% { width: 120px; } }</style></div>`,
124
125 'cramped-padding': `<div style="font-family: system-ui, sans-serif;"><button style="background: #111; color: #fff; border: none; border-radius: 4px; padding: 2px 6px; font-size: 13px; font-weight: 500;">Buy now</button> <span style="color: #555; font-size: 12px; margin-left: 8px;">2px vertical padding.</span></div>`,
126
127 'tight-leading': `<div style="font-family: system-ui, sans-serif; font-size: 13px; color: #111; line-height: 1.0; max-width: 220px;">Tight leading makes multi-line body text feel crammed and hard for the eye to track between lines.</div>`,
128
129 'skipped-heading': `<div style="font-family: system-ui, sans-serif; color: #111;"><h1 style="font-size: 20px; font-weight: 700; margin: 0 0 4px;">Page title (h1)</h1><h3 style="font-size: 13px; font-weight: 600; margin: 0; color: #555;">Subsection (h3), skipped h2</h3></div>`,
130
131 'justified-text': `<div style="font-family: system-ui, sans-serif; font-size: 12px; color: #111; text-align: justify; max-width: 230px; line-height: 1.5;">Justified text on screens creates rivers of whitespace because browsers can't hyphenate well. Leave this for print.</div>`,
132
133 'tiny-text': `<div style="font-family: system-ui, sans-serif; color: #111;"><div style="font-size: 15px; margin-bottom: 6px;">Regular body text</div><div style="font-size: 9px; color: #555;">And then fine print at 9 pixels that no one will ever read.</div></div>`,
134
135 'wide-tracking': `<div style="font-family: system-ui, sans-serif; font-size: 13px; color: #111; letter-spacing: 0.22em; max-width: 230px; line-height: 1.6;">Wide tracking on body text slows reading by breaking up natural character groupings.</div>`,
136
137 'line-length': `<div style="font-family: system-ui, sans-serif; font-size: 13px; color: #111; line-height: 1.55; max-width: 100%;">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.</div>`,
138
139 // ── LLM-only rule visuals ─────────────────────────────────────────
140
141 'monospace-as-technical': `<div style="font-family: 'Courier New', monospace; color: #111;"><div style="font-size: 18px; font-weight: 700; margin-bottom: 6px;">TECHNICAL_TOOL</div><div style="font-size: 11px; color: #555;">Mono for "developer" vibes. Lazy.</div></div>`,
142
143 'dark-mode-default': `<div style="background: #0f1117; padding: 18px; border-radius: 8px; font-family: system-ui, sans-serif;"><div style="color: #e5e7eb; font-size: 14px; font-weight: 600; margin-bottom: 4px;">Dark by default</div><div style="color: #9ca3af; font-size: 11px;">Defaulting to dark is a retreat from a decision.</div></div>`,
144
145 'everything-in-cards': `<div style="background: #fff; border: 1px solid #e8e4df; border-radius: 8px; padding: 10px;"><div style="background: #fff; border: 1px solid #e8e4df; border-radius: 6px; padding: 8px; font-family: system-ui, sans-serif; font-size: 12px; color: #111;"><div style="background: #fff; border: 1px solid #e8e4df; border-radius: 4px; padding: 6px;">Title</div></div><div style="background: #fff; border: 1px solid #e8e4df; border-radius: 6px; padding: 8px; margin-top: 6px; font-family: system-ui, sans-serif; font-size: 11px; color: #555;">Card around every single thing.</div></div>`,
146
147 'identical-card-grids': `<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; font-family: system-ui, sans-serif;">${'<div style="background: #fff; border: 1px solid #e8e4df; border-radius: 6px; padding: 10px; display: flex; flex-direction: column; align-items: flex-start; gap: 4px;"><div style="width: 18px; height: 18px; background: oklch(62% 0.20 265); border-radius: 4px;"></div><div style="font-size: 10px; font-weight: 600; color: #111;">Feature</div><div style="font-size: 9px; color: #888;">Short copy.</div></div>'.repeat(6)}</div>`,
148
149 'hero-metric-layout': `<div style="font-family: system-ui, sans-serif; text-align: left;"><div style="font-size: 42px; font-weight: 800; background: linear-gradient(135deg, oklch(65% 0.25 265), oklch(65% 0.25 340)); -webkit-background-clip: text; background-clip: text; color: transparent; line-height: 1;">10M+</div><div style="font-size: 10px; color: #888; text-transform: uppercase; letter-spacing: 0.1em; margin-top: 2px;">Active users</div><div style="display: flex; gap: 14px; margin-top: 10px; font-size: 10px; color: #555;"><span><strong>99.9%</strong> uptime</span><span><strong>200ms</strong> p50</span></div></div>`,
150
151 'glassmorphism': `<div style="position: relative; width: 100%; height: 100%; background: linear-gradient(135deg, oklch(70% 0.22 265), oklch(70% 0.25 340)); border-radius: 10px; overflow: hidden; display: flex; align-items: center; justify-content: center;"><div style="background: rgba(255,255,255,0.25); -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px); border: 1px solid rgba(255,255,255,0.4); border-radius: 10px; padding: 14px 18px; color: #fff; font-family: system-ui, sans-serif; font-size: 12px; font-weight: 600; box-shadow: 0 8px 30px rgba(0,0,0,0.12);">Frosted glass card</div></div>`,
152
153 'sparkline-decoration': `<div style="background: #fff; border: 1px solid #e8e4df; border-radius: 8px; padding: 14px 16px; width: 220px; font-family: system-ui, sans-serif;"><div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 8px;"><div><div style="font-size: 10px; color: #888; text-transform: uppercase; letter-spacing: 0.08em;">Revenue</div><div style="font-size: 20px; font-weight: 700; color: #111;">$42.1k</div></div><svg width="60" height="28" viewBox="0 0 60 28" style="flex-shrink: 0;"><polyline points="0,20 10,18 20,22 30,10 40,14 50,6 60,12" stroke="oklch(62% 0.22 265)" stroke-width="2" fill="none"/></svg></div><div style="font-size: 10px; color: #888;">Tiny chart, no real information.</div></div>`,
154
155 'generic-drop-shadows': `<div style="display: flex; gap: 10px;"><div style="background: #fff; border: 1px solid #e8e4df; border-radius: 10px; width: 70px; height: 70px; box-shadow: 0 10px 30px rgba(0,0,0,0.08);"></div><div style="background: #fff; border: 1px solid #e8e4df; border-radius: 10px; width: 70px; height: 70px; box-shadow: 0 10px 30px rgba(0,0,0,0.08);"></div><div style="background: #fff; border: 1px solid #e8e4df; border-radius: 10px; width: 70px; height: 70px; box-shadow: 0 10px 30px rgba(0,0,0,0.08);"></div></div>`,
156
157 'modal-reflex': `<div style="position: relative; width: 100%; height: 100%; background: #f5f3ef; border-radius: 8px; overflow: hidden;"><div style="position: absolute; inset: 0; background: rgba(0,0,0,0.35);"></div><div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: #fff; border-radius: 8px; padding: 14px 18px; width: 200px; font-family: system-ui, sans-serif; box-shadow: 0 20px 60px rgba(0,0,0,0.2);"><div style="font-size: 13px; font-weight: 600; color: #111; margin-bottom: 4px;">Are you sure?</div><div style="font-size: 11px; color: #666; margin-bottom: 8px;">Really, truly sure about this?</div><div style="display: flex; gap: 6px; justify-content: flex-end;"><div style="background: #eee; color: #555; padding: 4px 8px; border-radius: 4px; font-size: 10px;">Cancel</div><div style="background: oklch(60% 0.22 265); color: #fff; padding: 4px 8px; border-radius: 4px; font-size: 10px;">OK</div></div></div></div>`,
158
159 'every-button-primary': `<div style="display: flex; flex-direction: column; gap: 6px; font-family: system-ui, sans-serif;"><div style="display: flex; gap: 6px;"><button style="background: oklch(60% 0.22 265); color: #fff; border: none; border-radius: 5px; padding: 6px 12px; font-size: 11px; font-weight: 600;">Save</button><button style="background: oklch(60% 0.22 265); color: #fff; border: none; border-radius: 5px; padding: 6px 12px; font-size: 11px; font-weight: 600;">Cancel</button><button style="background: oklch(60% 0.22 265); color: #fff; border: none; border-radius: 5px; padding: 6px 12px; font-size: 11px; font-weight: 600;">Delete</button></div><div style="font-size: 10px; color: #888;">Every action shouts equally.</div></div>`,
160
161 'redundant-headers': `<div style="font-family: system-ui, sans-serif; color: #111; max-width: 230px;"><div style="font-size: 14px; font-weight: 600; margin-bottom: 4px;">Overview</div><div style="font-size: 11px; color: #555; line-height: 1.5;">This is the overview section, which provides an overview of the overview.</div></div>`,
162
163 'mobile-amputation': `<div style="font-family: system-ui, sans-serif;"><div style="display: flex; align-items: center; gap: 8px; padding: 8px 10px; background: #fff; border: 1px solid #e8e4df; border-radius: 6px; margin-bottom: 4px; font-size: 12px; color: #999; text-decoration: line-through;"><span>Export to CSV</span></div><div style="font-size: 10px; color: #888; margin-top: 4px;">"Not available on mobile."</div></div>`,
164};
165
166/**
167 * Anti-patterns that live in the /impeccable skill's DON'T list but
168 * don't have a deterministic detector. These can only be caught by
169 * /critique running an LLM assessment pass.
170 *
171 * Each entry looks like a detection rule: id, category, name,
172 * description, skillSection. The generator merges these into the
173 * grouped sections alongside detected rules with an 'llm' layer badge.
174 */
175// ─── Gallery: real examples in the wild ──────────────────────────────
176
177/**
178 * Curated real-world examples of anti-patterns caught in the wild.
179 * Each entry maps to:
180 * - public/antipattern-images/{id}.png (preview thumbnail)
181 * - public/antipattern-examples/{id}.html (standalone live example)
182 * Rendered as a dedicated section on the /anti-patterns page, replacing
183 * the old /gallery route which was confusingly labeled in the top nav.
184 */
185export const GALLERY_ITEMS = [
186 {
187 id: 'purple-gradients',
188 title: 'Purple Gradients Everywhere',
189 desc:
190 'The AI color palette: purple-to-blue gradients on everything. Buttons, text, backgrounds, orbs. The new "make it pop."',
191 },
192 {
193 id: 'lazy-cool',
194 title: 'Lazy "Cool"',
195 desc:
196 'Glassmorphism, neon glows, blurred orbs, monospace everything. Looks like a hackathon project, not a product.',
197 },
198 {
199 id: 'lazy-impact',
200 title: 'Lazy "Impact"',
201 desc:
202 'When in doubt, animate everything. Bouncing buttons, wiggling icons, gradient text, floating badges. Motion without meaning.',
203 },
204 {
205 id: 'thick-border-cards',
206 title: 'Side-Tab Cards',
207 desc:
208 'A thick colored border on one side of a rounded card. The single most recognizable tell of AI-generated UI.',
209 },
210 {
211 id: 'cardocalypse',
212 title: 'Cardocalypse',
213 desc:
214 'Cards inside cards inside cards. Five levels of nesting, each with its own padding and shadow.',
215 },
216 {
217 id: 'layout-templates',
218 title: 'Copy-Paste Layouts',
219 desc:
220 'The same hero-metric-features template repeated with different colors. When every section looks the same, nothing stands out.',
221 },
222 {
223 id: 'inter-everywhere',
224 title: 'Inter Everywhere',
225 desc:
226 'One font for everything. Headings, body, labels, buttons. No typographic hierarchy, no personality, no design.',
227 },
228 {
229 id: 'massive-icons',
230 title: 'Massive Icons',
231 desc:
232 'Icon containers larger than the content they introduce. When the decoration is bigger than the message, priorities are backwards.',
233 },
234 {
235 id: 'bad-contrast',
236 title: 'Bad Contrast Choices',
237 desc:
238 'Gray text on colored backgrounds, low-contrast labels, unreadable combinations. Looking good and being readable should not conflict.',
239 },
240 {
241 id: 'redundant-ux-writing',
242 title: 'Redundant UX Writing',
243 desc:
244 'Label, sublabel, helper text, and hint text all saying the same thing in slightly different words. Say it once, say it well.',
245 },
246 {
247 id: 'modal-abuse',
248 title: 'Modal Abuse',
249 desc:
250 'Complex settings crammed into a modal. If it needs a scroll bar and three columns, it deserves its own page.',
251 },
252];
253
254// ─── LLM-only rules ──────────────────────────────────────────────────
255
256export const LLM_ONLY_RULES = [
257 {
258 id: 'monospace-as-technical',
259 category: 'slop',
260 name: 'Monospace as "technical" shorthand',
261 description:
262 'Using a monospace typeface to signal "developer / technical" vibes. Reach for real type choices instead of a lazy stereotype.',
263 skillSection: 'Typography',
264 },
265 {
266 id: 'dark-mode-default',
267 category: 'slop',
268 name: 'Defaulting to dark mode for "safety"',
269 description:
270 '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.',
271 skillSection: 'Color & Contrast',
272 },
273 {
274 id: 'everything-in-cards',
275 category: 'slop',
276 name: 'Wrapping everything in cards',
277 description:
278 'Not every piece of content needs a bordered container. Spacing and alignment create visual grouping without the overhead of a card.',
279 skillSection: 'Layout & Space',
280 },
281 {
282 id: 'identical-card-grids',
283 category: 'slop',
284 name: 'Identical card grids',
285 description:
286 'Same-sized cards with icon + heading + text repeated endlessly. The default AI homepage layout.',
287 skillSection: 'Layout & Space',
288 },
289 {
290 id: 'hero-metric-layout',
291 category: 'slop',
292 name: 'Hero metric layout',
293 description:
294 'Big number, small label, three supporting stats, gradient accent. Used everywhere, trusted nowhere.',
295 skillSection: 'Layout & Space',
296 },
297 {
298 id: 'glassmorphism',
299 category: 'slop',
300 name: 'Glassmorphism everywhere',
301 description:
302 'Blur effects, glass cards, and glow borders used as decoration rather than to solve a real layering problem.',
303 skillSection: 'Visual Details',
304 },
305 {
306 id: 'sparkline-decoration',
307 category: 'slop',
308 name: 'Sparklines as decoration',
309 description:
310 'Tiny charts that look sophisticated but convey no meaningful information. If the data matters, give it room.',
311 skillSection: 'Visual Details',
312 },
313 {
314 id: 'generic-drop-shadows',
315 category: 'slop',
316 name: 'Rounded rectangles with generic drop shadows',
317 description:
318 'The safest, most forgettable shape on the web. Could be the output of any AI. Commit to a stronger visual treatment.',
319 skillSection: 'Visual Details',
320 },
321 {
322 id: 'modal-reflex',
323 category: 'slop',
324 name: 'Reaching for modals by reflex',
325 description:
326 'Modals interrupt the user and are lazy as a design default. Use them only when there is truly no better place for the interaction.',
327 skillSection: 'Visual Details',
328 },
329 {
330 id: 'every-button-primary',
331 category: 'quality',
332 name: 'Every button is a primary button',
333 description:
334 'When every button looks equally important, nothing reads as the primary action. Use ghost buttons, text links, and secondary styles to build hierarchy.',
335 skillSection: 'Interaction',
336 },
337 {
338 id: 'redundant-headers',
339 category: 'quality',
340 name: 'Redundant information',
341 description:
342 'Intros that restate the heading. Section labels that repeat the page title. Cards that echo their own caption. Make every word earn its place.',
343 skillSection: 'Interaction',
344 },
345 {
346 id: 'mobile-amputation',
347 category: 'quality',
348 name: 'Amputating features on mobile',
349 description:
350 'Hiding critical functionality on mobile because it is inconvenient. Adapt the interface to the context, do not strip it.',
351 skillSection: 'Responsive',
352 },
353];