mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-12 06:06:37 +03:00
Anti-patterns page polish: visuals for LLM rules, wider grid, compact legend, sidebar divider fix
Six fixes from the first-pass review. 1. Visuals for all 13 LLM-only rules. The catalog now ships a preview snippet for every card: Syne-style display, monospace-as-technical, dark-mode-default, everything-in-cards (nested), identical card grids (literal 3x2), hero metric layout (big number + gradient + supporting stats), glassmorphism (backdrop-filter on a gradient), sparkline decoration, generic drop shadows (three rounded squares), modal reflex (backdrop + centered dialog), every-button-primary, redundant-headers, mobile-amputation. Every rule card now has the same ~160px preview treatment. 2. Lede font normalized to match skill detail pages. .sub-page-lede dropped from clamp(1.0625, 1.6vw, 1.25rem) to clamp(1, 1.4vw, 1.125rem) so the paragraph under the anti-patterns title is the same size as the tagline on every other /skills page. 3. "How to read this" legend collapsed into a <details> disclosure. Summary is a single compact row with the title + chevron, padding 14px vertical. Body appears when opened, same content as before. Chevron rotates on open. 4. Visual example height bumped 140px -> 160px for more breathing room with the complex snippets. 5. Wider grid on the anti-patterns page. .anti-patterns-content no longer has a 820px max-width; only the header (720px max) and legend (720px max) are capped. The rule card grid fills the full main column width on wide viewports, so 38 cards stop wasting horizontal space. 6. Sidebar divider extends to the bottom of the viewport. Add min-height: calc(100vh - var(--site-header-height)) to .skills-sidebar so the sticky column fills the full viewport vertically regardless of content height, and the border-right reaches the footer.
This commit is contained in:
@@ -135,6 +135,34 @@ export const VISUAL_EXAMPLES = {
|
||||
'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>`,
|
||||
|
||||
'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>`,
|
||||
|
||||
// ── LLM-only rule visuals ─────────────────────────────────────────
|
||||
|
||||
'syne-display-font': `<div style="font-family: system-ui, sans-serif; color: #111; text-align: left;"><div style="font-size: 32px; font-weight: 800; letter-spacing: -0.02em; font-stretch: 125%; text-transform: none; line-height: 1;">Synelike</div><div style="font-size: 11px; color: #888; margin-top: 6px;">Every AI landing page uses this shape.</div></div>`,
|
||||
|
||||
'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>`,
|
||||
|
||||
'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>`,
|
||||
|
||||
'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>`,
|
||||
|
||||
'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>`,
|
||||
|
||||
'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>`,
|
||||
|
||||
'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>`,
|
||||
|
||||
'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>`,
|
||||
|
||||
'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>`,
|
||||
|
||||
'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>`,
|
||||
|
||||
'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>`,
|
||||
|
||||
'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>`,
|
||||
|
||||
'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>`,
|
||||
};
|
||||
|
||||
// ─── LLM-only rules ──────────────────────────────────────────────────
|
||||
|
||||
+55
-19
@@ -297,7 +297,7 @@ main#main {
|
||||
}
|
||||
|
||||
.sub-page-lede {
|
||||
font-size: clamp(1.0625rem, 1.6vw, 1.25rem);
|
||||
font-size: clamp(1rem, 1.4vw, 1.125rem);
|
||||
line-height: 1.55;
|
||||
color: var(--color-charcoal);
|
||||
max-width: 62ch;
|
||||
@@ -340,6 +340,9 @@ main#main {
|
||||
position: sticky;
|
||||
top: var(--site-header-height);
|
||||
align-self: start;
|
||||
/* Always full viewport height below the site header so the divider
|
||||
line extends to the bottom of the viewport even on short pages. */
|
||||
min-height: calc(100vh - var(--site-header-height));
|
||||
max-height: calc(100vh - var(--site-header-height));
|
||||
overflow-y: auto;
|
||||
padding: var(--spacing-lg) 0 var(--spacing-2xl);
|
||||
@@ -622,12 +625,15 @@ main#main {
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* The anti-patterns page lets the grid of rule cards use the full
|
||||
column width. Header, lede, and legend stay capped for line length. */
|
||||
.anti-patterns-content {
|
||||
max-width: 820px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.anti-patterns-header {
|
||||
margin-bottom: clamp(2.5rem, 5vw, 4rem);
|
||||
max-width: 720px;
|
||||
margin-bottom: clamp(2rem, 4vw, 3rem);
|
||||
}
|
||||
|
||||
.anti-patterns-header .sub-page-lede code {
|
||||
@@ -652,30 +658,63 @@ main#main {
|
||||
}
|
||||
|
||||
.anti-patterns-legend {
|
||||
padding: var(--spacing-lg);
|
||||
background: var(--color-cream);
|
||||
border: 1px solid var(--color-mist);
|
||||
border-radius: 10px;
|
||||
margin-bottom: clamp(2.5rem, 5vw, 4rem);
|
||||
margin-bottom: clamp(2rem, 4vw, 3rem);
|
||||
max-width: 720px;
|
||||
}
|
||||
|
||||
.anti-patterns-legend-title {
|
||||
font-family: var(--font-display);
|
||||
font-size: 1.25rem;
|
||||
font-style: italic;
|
||||
font-weight: 500;
|
||||
color: var(--color-ink);
|
||||
margin-bottom: var(--spacing-sm);
|
||||
.anti-patterns-legend-summary {
|
||||
list-style: none;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--spacing-sm);
|
||||
padding: 14px var(--spacing-lg);
|
||||
transition: background var(--duration-fast) var(--ease-out);
|
||||
}
|
||||
|
||||
.anti-patterns-legend p {
|
||||
.anti-patterns-legend-summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.anti-patterns-legend-summary:hover {
|
||||
background: color-mix(in oklch, var(--color-mist) 40%, transparent);
|
||||
}
|
||||
|
||||
.anti-patterns-legend-title {
|
||||
font-family: var(--font-body);
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-ink);
|
||||
}
|
||||
|
||||
.anti-patterns-legend-chevron {
|
||||
color: var(--color-ash);
|
||||
transition: transform var(--duration-base) var(--ease-out);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.anti-patterns-legend[open] .anti-patterns-legend-chevron {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.anti-patterns-legend-body {
|
||||
padding: 0 var(--spacing-lg) var(--spacing-lg);
|
||||
border-top: 1px solid var(--color-mist);
|
||||
margin-top: 4px;
|
||||
padding-top: var(--spacing-md);
|
||||
}
|
||||
|
||||
.anti-patterns-legend-body p {
|
||||
font-size: 0.9375rem;
|
||||
line-height: 1.7;
|
||||
color: var(--color-charcoal);
|
||||
}
|
||||
|
||||
.anti-patterns-legend a {
|
||||
.anti-patterns-legend-body a {
|
||||
color: var(--color-ink);
|
||||
text-decoration: none;
|
||||
border-bottom: 1px solid var(--color-accent);
|
||||
@@ -684,7 +723,7 @@ main#main {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.anti-patterns-legend a:hover {
|
||||
.anti-patterns-legend-body a:hover {
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
@@ -745,13 +784,10 @@ main#main {
|
||||
/* Visual example preview at the top of each card. */
|
||||
.rule-card-visual {
|
||||
position: relative;
|
||||
height: 140px;
|
||||
height: 160px;
|
||||
background: var(--color-cream);
|
||||
border-bottom: 1px solid var(--color-mist);
|
||||
overflow: hidden;
|
||||
/* The inline demo snippets often contain text they don't want to
|
||||
inherit from the card; isolate their context with `all: revert`
|
||||
on children via .rule-card-visual-inner. */
|
||||
}
|
||||
|
||||
.rule-card-visual-inner {
|
||||
|
||||
@@ -489,15 +489,20 @@ ${rules.map(renderRuleCard).join('\n')}
|
||||
<p class="sub-page-lede">The full catalog of patterns <a href="/skills/impeccable">/impeccable</a> teaches against. ${detectedCount} are caught by a deterministic detector (<code>npx impeccable detect</code> or the browser extension). ${llmCount} can only be flagged by <a href="/skills/critique">/critique</a>'s LLM review pass.</p>
|
||||
</header>
|
||||
|
||||
<section class="anti-patterns-legend">
|
||||
<h2 class="anti-patterns-legend-title">How to read this</h2>
|
||||
<p><strong>AI slop</strong> rules flag the visible tells of AI-generated UIs. <strong>Quality</strong> rules flag general design mistakes that are not AI-specific but still hurt the work. Each rule also shows how it is detected:</p>
|
||||
<dl class="anti-patterns-legend-layers">
|
||||
<div><dt><span class="rule-card-layer" data-layer="cli">CLI</span></dt><dd>Deterministic. Runs from <code>npx impeccable detect</code> on files, no browser required.</dd></div>
|
||||
<div><dt><span class="rule-card-layer" data-layer="browser">Browser</span></dt><dd>Deterministic, but needs real browser layout. Runs via the browser extension or Puppeteer, not the plain CLI.</dd></div>
|
||||
<div><dt><span class="rule-card-layer" data-layer="llm">LLM only</span></dt><dd>No deterministic detector. Caught by <a href="/skills/critique">/critique</a> during its LLM design review.</dd></div>
|
||||
</dl>
|
||||
</section>
|
||||
<details class="anti-patterns-legend">
|
||||
<summary class="anti-patterns-legend-summary">
|
||||
<span class="anti-patterns-legend-title">How to read this</span>
|
||||
<svg class="anti-patterns-legend-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>
|
||||
</summary>
|
||||
<div class="anti-patterns-legend-body">
|
||||
<p><strong>AI slop</strong> rules flag the visible tells of AI-generated UIs. <strong>Quality</strong> rules flag general design mistakes that are not AI-specific but still hurt the work. Each rule also shows how it is detected:</p>
|
||||
<dl class="anti-patterns-legend-layers">
|
||||
<div><dt><span class="rule-card-layer" data-layer="cli">CLI</span></dt><dd>Deterministic. Runs from <code>npx impeccable detect</code> on files, no browser required.</dd></div>
|
||||
<div><dt><span class="rule-card-layer" data-layer="browser">Browser</span></dt><dd>Deterministic, but needs real browser layout. Runs via the browser extension or Puppeteer, not the plain CLI.</dd></div>
|
||||
<div><dt><span class="rule-card-layer" data-layer="llm">LLM only</span></dt><dd>No deterministic detector. Caught by <a href="/skills/critique">/critique</a> during its LLM design review.</dd></div>
|
||||
</dl>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<div class="anti-patterns-sections">
|
||||
${sectionsHtml}
|
||||
|
||||
Reference in New Issue
Block a user