Conversation
… build DRY refactor - Anti-pattern detector script (source/skills/critique/scripts/detect-antipatterns.mjs): CLI tool that scans files/dirs for UI anti-patterns via regex. Detects side-tab accent borders and border-accent-on-rounded patterns across Tailwind, CSS, JSX. Context-aware: skips safe elements (blockquotes, nav, inputs, code), neutral colors, and adjusts thresholds based on border-radius co-occurrence. - Browser visualizer (public/js/detect-antipatterns-browser.js): Drop-in script that highlights anti-patterns directly in the browser with labeled overlays. Two modes: "static" (regex, matches CLI) and "computed" (getComputedStyle, catches CSS cascade). Scans both inline styles and <style> blocks. - Gallery of Shame (public/gallery.html): Standalone page showcasing 11 AI anti-pattern examples with thumbnails and links. Anti-pattern example pages updated from 1080x1080 Twitter format to responsive layouts, labels removed, screenshots retaken at 16:10. - Critique skill updated to run detector before manual review. - Build system: skills now support scripts/ directories alongside reference/. All 8 provider transformers refactored to use shared.js (DRY). - 58 new tests covering detection logic, fixtures, CLI integration. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…lat hierarchy Three new detections: - overused-font: flags Inter, Roboto, Open Sans, Lato, Montserrat, Arial as primary font-family or via Google Fonts imports - single-font: file-level analyzer flags pages using only one non-generic font family (needs pairing for typographic hierarchy) - flat-type-hierarchy: file-level analyzer collects all font-size values (px, rem, Tailwind text-* classes, clamp min/max) and flags when the max/min ratio is below 2.0 Detection engine extended to support file-level analyzers alongside line-level matchers. Typography fixtures added for both should-flag and should-pass cases. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Three detection tiers: - file/dir (default): fast regex scan, zero dependencies - file + --deep: jsdom computed styles, resolves linked local stylesheets by inlining <link rel="stylesheet"> content before parsing - URL (https://...): auto-launches Puppeteer for full browser rendering, handles CDN stylesheets, JS-rendered content, everything New exports: detectAntiPatternsDeep(), detectAntiPatternsUrl() jsdom added as devDependency; puppeteer remains optional (npx cache). TDD: linked-stylesheet fixture demonstrates the gap — regex finds 0 border issues, --deep correctly catches side-tab and top-accent from the external CSS file. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Architecture simplified to two paths: - HTML files: jsdom with getComputedStyle (resolves linked CSS, cascade) - Non-HTML files: regex fallback (CSS, JSX, TSX, etc.) - URLs: Puppeteer (unchanged) - --fast flag forces regex-only for all files Removed --deep flag (jsdom is now the default). Removed static mode from browser script (always uses getComputedStyle — it's in a real browser). Anti-pattern definitions split into: - checkElementBorders() — shared element-level computed style checker - checkPageTypography() — shared page-level checker - REGEX_MATCHERS/REGEX_ANALYZERS — regex fallback for non-HTML Browser script simplified from 470 lines to 250. CLI script reduced from 810 lines to 440. Detection logic is now single-source for jsdom/puppeteer/browser. Fixtures now served via /fixtures/* route in dev server for proper CORS handling of linked stylesheets. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Page-level typography checks (flat hierarchy, single font, overused font) now only run on files that look like full pages (have <!DOCTYPE, <html>, or <head> tags). Partials and components still get element-level border checks. isFullPage() strips HTML comments before checking to avoid false matches on prose that mentions tag names. Added partial-component.html fixture that has Inter, flat sizes, and a side-tab border — verifies only the border is flagged. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Five new detections: - pure-black-white: flags #000/#fff in styles via regex (jsdom bg resolution unreliable for this) - gray-on-color: gray text (low chroma, mid luminance) on colored backgrounds via getComputedStyle + ancestor bg walk - low-contrast: WCAG AA violation (4.5:1 body, 3:1 large text) via computed contrast ratio with resolved effective background - gradient-text: background-clip:text + gradient combo via regex (jsdom doesn't compute background-clip) - ai-color-palette: conservative purple/violet accent detection via regex on known hex values in prominent contexts Background resolution handles jsdom limitation where background shorthand isn't decomposed — falls back to parsing raw style attribute for hex colors. Color fixtures added for both should-flag (all 5 types) and should-pass (tinted neutrals, good contrast, non-purple accents). Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Element-level (jsdom) and regex (--fast) detection for:
- bg-black, bg-white, text-black: pure black/white
- text-white without dark bg class: pure white on light
- text-gray-*/slate-*/zinc-* on bg-{color}-*: gray on colored bg
- text-purple-*/violet-*/indigo-* on headings/large text: AI palette
- from-purple-* to-indigo-*: purple gradient
- bg-clip-text + bg-gradient-to-*: gradient text (already existed)
text-white is NOT flagged when paired with a dark bg class (bg-black,
bg-gray-700+, bg-blue-500+, etc.) since that's intentional contrast.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Mirrors all 5 color anti-pattern checks from the CLI: - pure-black-white (computed bg + Tailwind classes) - gray-on-color (computed style + ancestor bg resolution + Tailwind) - low-contrast (WCAG AA ratio via computed styles) - gradient-text (computed background-clip + Tailwind bg-clip-text) - ai-color-palette (computed hue analysis + Tailwind purple classes) Browser version uses real getComputedStyle so bg resolution works properly (unlike jsdom). Tailwind class checks are shared logic. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…cing, 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) <[email protected]>
Matches CLI behavior: only bg-black and computed #000 backgrounds are flagged. bg-white, text-black, text-white, and #fff are no longer flagged in the browser visualizer. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Adds checkLayout() with nested-cards and identical-card-grid detection using computed styles and DOM tree walking. Same logic as CLI: - isCardLike() checks shadow + rounded + bg/border (2 of 3) - Excludes dropdowns, modals, tiny elements, safe tags - Identical grid fingerprints icon + heading + paragraph structure Layout findings are highlighted on the actual elements (not just in the page banner). Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
- Nested cards: fixed dedup to use WeakSet on actual elements instead of tag-name key, so all nested card instances are found (not just the first div-in-div pair). Now catches all 4+ nesting examples. - Dropped identical-card-grid: too many legitimate uses (data displays, pricing cards, navigation tiles) make false positives unavoidable. - Removed from CLI, browser script, tests, and ANTIPATTERNS registry. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Same bug as the CLI had: Set with tag-name key ('DIV:DIV') deduped
all nested divs to one finding. Now uses WeakSet on actual elements
so each nested card instance gets its own outline.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
… fallback Three fixes: - Only flag innermost nested cards: if L1>L2>L3, only L3 gets flagged (not L2). Uses ancestor-filtering after collection pass. - Lower text threshold from 20 to 10 chars to catch short card content like "Inner card via CSS." - isCardLike now also checks raw inline style attribute for box-shadow and border-radius (jsdom doesn't resolve CSS shorthands). Tightened heuristic: shadow or border is mandatory (not optional). Fixes false positive on layout-should-pass where a tinted subsection (rounded + bg, no shadow) inside a card was incorrectly flagged. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
WeakSet.prototype[Symbol.iterator] doesn't exist — can't use for..of. Changed to Set (same fix as CLI). Also updated isCardLike heuristic to require shadow or border as mandatory, matching the CLI. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…dd browser tests Major cleanup: - detectUrl() now injects the browser script via page.evaluate() and calls window.impeccableScan() instead of reimplementing all detection logic inline. Removes ~80 lines of triple-duplicated code. - Removed dead isPureBlackOrWhite function. - CLI reduced from 1286 to 1212 lines. New: Puppeteer-powered browser parity tests (detect-antipatterns-browser.test.js): - Starts a local HTTP server for fixtures - Loads fixture pages in headless Chrome - Runs the browser detection script via impeccableScan() - Verifies findings match expectations for all fixture categories: borders, colors, layout, typography, partials 8 new browser tests catch desync between CLI and browser script (like the WeakSet iteration bugs we hit earlier). puppeteer added as devDependency. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
DRY refactor: - detect-antipatterns-core.mjs (297 lines): shared constants (SAFE_TAGS, OVERUSED_FONTS, GENERIC_FONTS, ANTIPATTERNS), color utilities (parseRgb, relativeLuminance, contrastRatio, hasChroma, getHue, colorToHex, isNeutralColor), and pure detection functions (checkBorders, checkColors, isCardLikeFromProps). - CLI (889 lines, was 1212): imports from core, keeps jsdom-specific resolveBackground, page-level analyzers, regex fallback, and CLI logic. - Browser wrapper (335 lines): template with browser-specific DOM adapters, highlighting, scan loop. Core is injected at build time. - build-browser-detector.js: reads core, strips exports, injects into wrapper, writes to public/js/detect-antipatterns-browser.js (generated). - Build step added to scripts/build.js (runs before Bun bundling). Source of truth for detection logic is now the core module. Browser script is generated — do not edit public/js/detect-antipatterns-browser.js directly. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
The generated browser detector now lives alongside the CLI script in .claude/skills/critique/scripts/ — clearly a build artifact, not a hand-maintained source file in public/js/. - build-browser-detector.js outputs to .claude/ instead of public/js/ - Dev server serves .claude/skills/* for local testing - All fixture and antipattern-example HTML files updated to new path - Puppeteer detectUrl reads browser script from same directory - Browser parity test server updated to serve from .claude/ - Deleted public/js/detect-antipatterns-browser.js Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
The skill sync wipes .claude/skills/ and re-copies from dist, deleting the generated browser script. Moved build-browser-detector.js to run AFTER the sync. Dev server's /js/* route now falls through to .claude/skills/critique/scripts/ for built artifacts. All fixture HTML references use /js/detect-antipatterns-browser.js (clean URL). Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Combine detect-antipatterns-core.mjs, detect-antipatterns.mjs, and detect-antipatterns-browser-wrapper.js into a single universal file that auto-detects browser vs Node via IS_BROWSER. Shared constants, color utilities, and pure detection logic exist once instead of being duplicated across files. Build script simplified to strip @browser-strip-start/end markers, set IS_BROWSER=true, and wrap in IIFE. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
New detections: - bounce-easing: flags bounce/elastic animation names, animate-bounce (Tailwind), and cubic-bezier curves with overshoot (y values outside [0, 1]) - layout-transition: flags explicit transition of width, height, padding, margin, and max-height/min-width variants; skips transition: all - dark-glow: flags colored box-shadow with blur > 4px on dark backgrounds (luminance < 0.1); skips gray shadows, focus rings (no blur), and non-dark backgrounds Includes 48 new tests across unit, regex, and jsdom fixture tests with dedicated should-flag and should-pass HTML fixtures for both categories. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Replace the buried "Gallery of Shame" inline link with a compact detector summary card showing all 16 auto-detected anti-patterns as chips grouped by category (Borders, Typography, Color, Layout, Motion). Prominent gallery link in the footer. Revised lead copy to mention the automated detector. Keeps the tabbed Do/Don't pattern reference below unchanged. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Replace the detector summary card and badge system with a simple inline note below the tabbed Do/Don't patterns: "/critique catches all of these. 16 deterministically, the rest through LLM analysis." Gallery of Shame and Suggest a pattern links sit inline alongside. Subtle divider separates the note from the patterns above. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Extract all regex-on-HTML checks into shared checkHtmlPatterns() function called by both browser and Node paths. Eliminates drift between checkTypography/checkPageTypography and removes separate checkPageMotion/checkPageGlow functions. Add parseGradientColors() utility and checkElementGradientDOM() to detect purple/violet gradient backgrounds on any element including buttons (bypasses SAFE_TAGS). Fix false low-contrast findings on gradient backgrounds by returning null from resolveBackground when a gradient is encountered. Fix "Only font:" double-colon in browser labels. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Detection improvements: - Remove SAFE_TAGS from glow check (buttons/links with glows are valid) - Add gradient color parsing (parseGradientColors) for AI palette detection on gradient backgrounds including buttons - Detect cyan neon text on dark backgrounds as AI palette - Resolve gradient backgrounds as dark for glow detection - Fix pure-black false positive on semi-transparent overlays (a >= 0.9) - Skip low-contrast/gray-on-color when background is a gradient - Fix "Only font:" double-colon in browser labels Test performance: - Split jsdom fixture tests to Node's test runner (bun + jsdom hangs after ~13 instances due to resource leak) - bun test for unit/regex/CLI tests (94 tests, 4s) - node --test for jsdom fixtures (15 tests, 1.3s) - Total: 109 tests in ~5s (was 280s+) Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
New detections (browser-only, DOM-based): - line-length: text wider than ~85 chars per line - cramped-padding: <8px padding in bordered/bg containers (2+ borders) - tight-leading: line-height < 1.3x on body text - small-target: interactive elements < 44x44px - skipped-heading: heading levels that skip (h1 then h3) - justified-text: text-align: justify without hyphens: auto - tiny-text: font-size < 12px on body text (>20 chars) - all-caps-body: text-transform: uppercase on >30 chars of body text - wide-tracking: letter-spacing > 0.05em on non-uppercase body text Browser overlay improvements: - Hover swaps label for detail tooltip (CSS-based, not JS events) - Border goes transparent on hover to reveal element underneath - Fixtures: quality-should-flag.html and quality-should-pass.html Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
- Switch from border to CSS outline for overlays (cleaner, animatable) - On hover: outline expands outward 4px, label shifts up to match, tooltip slides in from below with fade, z-index elevates above others - Exclude page banner from hover transitions via .impeccable-banner class - Reposition overlays on window resize via requestAnimationFrame - CSS-only hover states (no JS event listeners) Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Usage: npx impeccable detect [file-or-dir-or-url...] Subcommand structure designed for future expansion. The detect subcommand delegates to the existing detection engine with all its modes (jsdom, regex, Puppeteer). - bin/impeccable.mjs: CLI entry point with bun shebang - package.json: bin field added - Export detectCli (main) from detection script - Updated help text to show impeccable detect usage - Updated CLAUDE.md and README.md with CLI docs Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
The critique skill now has three layers of detection:
1. CLI-first pass (always): Runs the deterministic detector via
`node {{skills_dir}}/critique/scripts/detect-antipatterns.mjs --json`
with scope checks (file count estimation, --fast for 200-500 files,
user prompt for >500 files)
2. Browser visualization (when available): If the AI harness has
browser automation (Chrome MCP, Cursor browser), injects the
detection script into the page for live visual overlays. Reads
the browser script via cat, injects via javascript_tool.
3. LLM analysis (always): The existing deep design critique across
10 dimensions, now informed by deterministic scan results.
Add {{skills_dir}} placeholder to build system for cross-provider
script paths (.claude/skills, .cursor/skills, .gemini/skills, etc).
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
- Add trigger context to description ("Use when...")
- Compress scan workflow from 4 verbose steps to concise block
- Remove false "zero false negatives" claim, add false-positive guidance
- Avoid loading 55KB browser script into context (inject directly)
- Remove time-sensitive "2024-2025" reference
- Compress 10 evaluation dimensions to AI Slop (expanded) + single
holistic review paragraph (Claude knows how to evaluate these)
- Revert {{skills_dir}} placeholder (relative paths per skill docs)
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Recognize "DO NOT" / "DO" lines (with optional colon) inside <rules> and <absolute_bans> blocks, and make skillGuideline substring matching case-insensitive so the validator handles the new XML-structured SKILL.md without rejecting the refactored prose. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
The eyebrow ('Drag or hover to compare') was a sibling of
.split-comparison, sitting at the left edge of the outer .skill-demo
section. Because .split-comparison has 32px padding, the visible card
inside sat 32px to the right of the eyebrow, creating a visible
indentation mismatch. Move the eyebrow inside .split-comparison so it
inherits the same 32px offset and aligns with the card's left edge
(same as how .split-labels and .skill-demo-caption already align).
Note: the HTML order inside .split-comparison is now eyebrow -> container
-> labels -> caption, which matches the homepage's before/after demo
flow (card -> BEFORE/AFTER -> descriptive caption).
…ptions The skill's "don't use Inter / don't use dark / don't center" negatives were creating new attractors (the model picks Fraunces / light / grid instead, every time). Inline always-applicable principles into SKILL.md, add a font selection anti-attractor procedure that forces the model to enumerate AND reject its reflex defaults, switch high-stakes blocks to XML structure, tighten side-tab and gradient-text bans to specific CSS patterns, ban Syne explicitly, and strip named font/color prescriptions from the references. Validated against the internal eval framework on Qwen 3.6 Plus across 7 niches: Fraunces dropped from 92% to 0% on kids reading, side-tabs from 76% to 20% on vintage moto, no theme regressions. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Adds an Evals Framework section pointing future Claude sessions at evals/AGENT.md (the comprehensive private guide) and inlines the highest-leverage facts: primary baseline model is gpt-5.4 medium reasoning, n=20 standard sample size, do not use Haiku as primary target, always smoke test before sweep. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
The demo's Before/After labels and the descriptive caption were on two separate rows below the card. Merge them into one row: Before pinned left, caption centered in the middle, After pinned right. - Move the caption <p> inside .split-labels between the two label spans. If a skill has no caption, emit an empty <span> placeholder so the grid still has three cells and Before/After sit at the edges. - Switch .split-labels from flex space-between to a 3-column grid (auto minmax(0,1fr) auto) with baseline alignment. Before is justify-self: start, After is justify-self: end, caption is justify-self: center. - Reset the caption's typography inside the grid (default body font, not mono; text-transform: none; letter-spacing: 0) since it inherits the label row's monospace caps by default.
Three docs sidebar improvements. 1. Collapsible mobile menu. The sidebar on narrow viewports used to dump 21 skill links and 2 tutorial links inline above the content, forcing a long scroll past the nav. Add a toggle button at the top of the sidebar that shows the current page label (e.g. "/overdrive" or "Getting started") plus a chevron, and collapses the menu behind it on mobile. Click the button to open/close. On desktop (>=920px) the toggle is hidden and the menu shows unconditionally as before. Pure aria-expanded state driven by a small delegated click handler in render-page.js. 2. Active-state breathing room. The left-border accent on the current sidebar item used to sit 2px from the text, which felt cramped. Pull the border 14px to the left via margin-left and push the text 12px to the right via padding-left. The net result: the accent bar sits in the layout gutter, the text keeps its alignment with the brand logo in the header, and there's now 12px of comfortable space between the border and the text. 3. Active state visibility. The same change makes the accent bar more visible on desktop, since it no longer hugs the text. 'aria-current' was already being set correctly on /skills/* and /tutorials/* pages; the bar just looked too subtle at 2px of clearance.
jsdom's CSSOM silently drops any border shorthand containing var(), leaving the computed style empty — which hid the canonical real-world side-tab pattern (border-left: Npx solid var(--brand)) from the Node detector path. Real browsers resolve var() natively, so this only affected the jsdom path. Add a pre-pass that walks the stylesheets, reads border shorthands off rule.style (jsdom preserves them there even when it drops them from cssText), resolves var() against :root custom properties via the documentElement's computed style, and attaches the result to a per- element override map. checkElementBorders consults the map whenever jsdom returned an empty width, or substitutes a resolved color when jsdom kept a literal var() string. Hex and named colors are normalized to rgb() so isNeutralColor can classify them correctly — without that, --line:#e5e7eb slipped through as non-neutral. Adds four flag cases and three pass cases to modern-color-borders.html covering shorthand, mixed neutral+colored, border-right, card-shaped label, neutral-resolving var, thin var, and uniform all-sides var. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
The previous attempt put the active-state border at margin-left: -14px so it would sit in the layout gutter while keeping the link text aligned with the header logo. Problem: .skills-sidebar uses overflow-y: auto, and per CSS spec that coerces overflow-x from visible to auto too, which clips any content outside the column. The border was being painted and then clipped, so the user saw nothing. Rework: - Border now sits inside the normal flow. padding: 4px 0 4px 12px with a 2px border-left means link text is 14px inset from the column edge. Group titles pick up the same 14px padding-left so the two align vertically. - Add a subtle accent-dim background on the active item (not just the border) so the cell reads as highlighted, not just marked. - Add a hover background tint so items feel interactive. - Remove the duplicate .skills-sidebar-list a[aria-current] block that was left over from the previous rewrite. Trade-off: links are now 14px to the right of where the header logo sits (before, they aligned). Worth it: the active state is now clearly visible on both desktop and mobile.
The accent-dim background fill on active items was too loud. Keep only the border-left accent, ink color, and bold weight. Hover tint on other items still works as a subtle interactivity hint.
Expands the v2.0 entry from 5 flat bullets to 9 grouped highlights and surfaces the additions the existing entry missed: the data-driven skill rewrite (validated against the internal eval framework with concrete per-niche metrics), the Chrome DevTools extension, the rebuilt site and docs, /critique's persona sub-agents, Rovo Dev support, and Apache 2.0 unification. Each item leads with a bold label so the list stays scannable despite the length. Hero version link tightened to signal the three most visible pieces of the release (detection engine, Chrome extension, data-driven skill) instead of just the detector. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Three additions to the anti-patterns catalog page, all sourced from a
new content/site/anti-patterns-catalog.js file so the user's parallel
edits to src/detect-antipatterns.mjs don't conflict with display metadata.
1. Detection layer badge per rule. Three layers:
cli - static analysis or jsdom. Runs from `npx impeccable detect`
on files, no browser required. 23 of 25 current rules.
browser - needs real browser layout (getBoundingClientRect).
Runs via the browser extension or Puppeteer, not the
plain CLI. Only 2 rules: cramped-padding and line-length,
as documented in tests/detect-antipatterns-browser.test.mjs.
llm - no deterministic detector. Flagged by /critique's LLM
review pass. 13 rules live only in the skill's DON'T list.
Each card renders a mono pill with the layer label, color-coded per
layer (neutral mist for CLI, blue tint for browser, amber tint for LLM).
The How-to-read legend grows a dl explaining what each layer means.
2. Inline visual example per detected rule. All 25 detection rules get
a ~140px tall preview area at the top of the card showing the bad
pattern as live HTML (cream background, self-contained inline styles).
Visuals for side-tab, gradient-text, dark-glow, nested-cards, and the
rest let you see what the detector is actually flagging. LLM-only
rules ship without visuals for now; their card bodies take the full
card height.
3. LLM-only rules merged into the sections. Parsed out from
source/skills/impeccable/SKILL.md DON'T lines that the detector
doesn't cover: Syne, monospace-as-technical, dark-mode-default,
everything-in-cards, identical-card-grids, hero-metric-layout,
glassmorphism, sparkline-decoration, generic-drop-shadows,
modal-reflex, every-button-primary, redundant-headers,
mobile-amputation. Each renders like a detection rule card but
shows the 'LLM only' layer badge and has no rule id chip. They
slot into the same section groups as detected rules (Interaction
and Responsive sections added to the section order so these get
real headings).
- scripts/lib/sub-pages-data.js: imports the catalog, enriches
detected rules with { layer, visual }, appends LLM_ONLY_RULES with
layer: 'llm'. Re-exports LAYER_LABELS and LAYER_DESCRIPTIONS for
the generator.
- scripts/build-sub-pages.js: renderRuleCard adds the visual block
and the layer badge; LLM rules drop the rule id chip since their id
is just an internal slug. groupRulesBySection now extends the
primary order with whatever extra sections rules reference.
- public/css/sub-pages.css: .rule-card now has a .rule-card-visual
preview area on top with border-bottom, body section below. New
.rule-card-layer pill styling per layer. Layer legend dl using a
2-column grid for badge -> description.
Dev server serves 38 total cards (25 detected + 13 LLM) across 8
sections: Visual Details, Typography, Color & Contrast, Layout & Space,
Motion, Interaction, Responsive, General quality.
… 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.
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.
The 'In the wild' section at the bottom of /anti-patterns was
mischaracterizing synthetic fixtures as real examples and was buried
deep in a taxonomy of detection rules. The specimens belong somewhere
that frames them as what they actually are: live pages you can click
into to experience Visual Mode. Split them off into a new top-level
page that also finally gives Visual Mode first-class treatment.
- New /visual-mode page, top-level nav item, single-column layout (no
sidebar). Structure:
1. Editorial header with an "Live detection overlay" eyebrow.
2. Live iframe embed of visual-mode-demo.html inside mac-window
chrome, same preview component the homepage uses.
3. "Three ways to run it" section with three method cards:
- /critique runs the overlay inside its browser pass
- `npx impeccable live` starts a standalone overlay server
- Chrome extension, marked coming soon, with a cream bg
4. "Try it live" gallery of the 11 synthetic specimens as
clickable cards. Each links to /antipattern-examples/{id}.html
where the detector script is already injected so the reader
lands on a live overlay.
- scripts/build-sub-pages.js: new renderVisualModeMain(); visualMode
added to outDirs; generator loop writes /visual-mode/index.html.
- server/index.js: new /visual-mode route serving the generated file.
- Top nav on every page gains 'Visual Mode' between Anti-Patterns
and the GitHub pill. Updated the partial + all 4 hand-authored
HTML pages.
- .gitignore adds public/visual-mode/.
- /anti-patterns: 'In the wild' section and its TOC entry removed.
Replaced with a one-line pointer at the end of the lede: "Want to
see them live on real pages? Try Visual Mode." GALLERY_ITEMS stays
in the catalog file (now used by /visual-mode only).
- public/css/sub-pages.css: new .visual-mode-page-body + .visual-mode-*
classes. Ports the mac-window chrome (dots + mono title) from
main.css, adds three-card method grid, and reuses the existing
.gallery-card styles for the specimen list.
Clean up a few em-dashes in the catalog (block comments + one visible
visual example) so the build-time validator stays clean.
Server restart required to pick up the new /visual-mode route.
Replaces the brand-only card with a split layout: wordmark left, floating Chrome extension detection panel right. Generator now counts user-invocable, non-deprecated skills from source/skills/ (v2.0 unified structure) instead of the removed source/commands/ directory. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
The min-height: calc(100vh - var(--site-header-height)) added earlier so the sticky sidebar's border-right divider reaches the bottom of the viewport on desktop was applying on mobile too. On mobile the sidebar is static (not sticky) and collapses behind a toggle, so the min-height reserved a full viewport of empty space above the main content whenever the menu was collapsed. The result: opening /anti-patterns on mobile showed just the 'Sections' dropdown in the first screen, then a blank viewport, then the rules below the fold. Wrap the min-height rule in a min-width: 921px media query so it only applies on desktop, matching the breakpoint that switches the layout to the two-column grid.
Tightened the v2.0 changelog on the homepage. Same information density, fewer words, no em dashes, and dropped what does not concern users. - Skill rewrite bullet: same numbers, shorter framing. - Detection engine bullet: dropped the 'hard-to-hit cases that slip past regex-only scanners' flourish at the end. - CLI bullet: collapsed parenthetical clauses into short phrases. - Chrome extension bullet: replaced the em dash with a colon. - /critique bullet: tightened. - /shape bullet: replaced the em dash with a period break. - "Rebuilt site and docs" renamed to "New docs site" and trimmed to just what users experience (top-level sections, skill pages, tutorials, rule cards). Dropped the 'mobile experience overhauled' line — implementation detail, not a user-facing feature. - Licensing bullet: renamed to 'Apache 2.0 throughout'.
Impeccable has always been Apache 2.0; the back-and-forth on licensing was internal to the v2.0 PR and is not a user-facing change.
Drop the metric-heavy framing and lead with the user-facing wins (font/color diversity, design quality, Codex support) plus a brief nod to the eval framework and anti-attractor technique. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Call out the frontend-design to impeccable rename on its own (and the /teach-impeccable to /impeccable teach move), and reframe the /shape bullet to cover both /shape and /impeccable craft as the new ways to create with Impeccable. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Two unrelated breakages were stacking on the v2.0 PR: 1. The static site build crashed because the generated /visual-mode page referenced images via root-absolute paths (/antipattern-images/*.png). Bun's HTML loader resolves <img src> at build time relative to the source HTML file and treats a leading slash as filesystem-absolute, so it could not find the images. Use a relative path so Bun bundles and hashes them the same way the homepage already does. 2. The Puppeteer-backed fixture tests crashed in GitHub Actions because the Ubuntu runners block unprivileged user namespaces, so Chrome's sandbox cannot initialize. Pass --no-sandbox / --disable-setuid-sandbox only when process.env.CI is set, so local users keep the hardened default launch. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…page The /visual-mode sub-page rules added in 27d1b13 duplicated .visual-mode-preview (and its header/dot/title children) in sub-pages.css with a max-width + margin: 0 auto. Because sub-pages.css loads after main.css on index.html, those styles won on the homepage too. Auto margins on a grid item disable justify-self: stretch, so the preview collapsed to the iframe's 300px intrinsic width instead of filling its 3fr cell in .visual-mode-demo. Scope the rules to .visual-mode-page so they only apply on the sub-page and the homepage falls back to main.css's .visual-mode-preview rule. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
When Bun.build aggregates resolution failures, the thrown error keeps the real causes on error.errors (an array). The previous handler only printed error.message and error.stack, both of which are generic / undefined for this kind of failure, so CI logs read as 'Bundle failed / undefined' with no clue what was unresolved. Walk error.errors first so the actual file + import that failed shows up in CI output. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Cloudflare Pages ships an older Bun than the one used locally. That version emits shared CSS chunks via the default 'chunk-[hash]' naming template, but the [hash] token isn't always populated when the chunk is shared across multiple HTML entrypoints — every sub-page that imports sub-pages.css ends up wanting the same './chunk-' filename and the build aborts with 'Multiple files share the same output path'. Pin the chunk and asset naming explicitly so [hash] is always present regardless of Bun version. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Bundles recent CLI/detector work that landed on v2.0 since 2.0.6: side-tab border detection on oklch/oklab/lch/lab and CSS variables, emoji-only handling in contrast/icon-tile rules, asymmetric font-size-aware cramped-padding rule, full anti-pattern names in overlay labels, and the CI sandbox flags for Puppeteer fixture tests. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
The Bun shipped with Cloudflare Pages doesn't dedupe shared CSS chunks across HTML entrypoints — each entry tries to emit its own copy. With chunk: '[name]-[hash].[ext]', three sub-pages all named index.html (skills/, tutorials/, anti-patterns/) plus shared CSS content end up producing chunks with identical name+hash and the build aborts on 'Multiple files share the same output path'. Including [dir] in the chunk and asset templates scopes each chunk to its entry's source directory, so the per-entry copies land in unique paths even when dedupe is off. Local Bun (1.3.x) still emits a single shared chunk because [dir] is only used when there are multiple chunk candidates. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
|
I CAN'T WAIT!!!!!!!! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
v2.0 of Impeccable. Three big themes stack on top of each other:
npx impeccable detect), a browser overlay, and a Chrome DevTools extension. AI agents can now ground their design review in something other than vibes.frontend-designtoimpeccable). Built against an internal eval framework that runs the same brief through frontier models with and without the skill loaded. Result: dramatically more font and color diversity, sharper overall design quality, and much stronger Codex support.What's in this PR
Anti-pattern detection engine
oklch/oklab/lch/labcolor formats, CSS variables inside border shorthands, gradient-backed text, and emoji-only nodes.--fast) for huge codebases.src/detect-antipatterns.mjs) auto-detects environment and adapts to Node and the browser. The browser bundle is generated from the same source at build time.CLI:
npx impeccable detectScans HTML, CSS, JSX/TSX, Vue, Svelte, and CSS-in-JS. Framework detection, multi-file import tracking, Puppeteer-backed live URL scanning, CI-ready JSON output, and a
--fastregex mode.Chrome DevTools extension
One-click detection on any page: yours, staging, production, or someone else's. Reads live computed styles, surfaces findings in an interactive panel, and highlights elements on the page. Currently in Chrome Web Store review.
/critiquegot teethVisual Mode (live browser overlay)
npx impeccable live) and inline use from/critique./visual-modetop-level page demos it on synthetic slop pages.Data-driven
impeccableskill rewritefrontend-designtoimpeccable.gpt-5.4+ Qwen 3.6 Plus across 15 niches) that measures how much the output collapses into monoculture.SKILL.mdso they always load.New docs site
New command:
/shapeInterviews you about purpose, audience, and goals before any code is written, so the model has a brief in hand before the first line.
New harness: Rovo Dev
11 supported AI tools total.
Build / infrastructure
shared.js) replaces per-provider duplication.{{scripts_path}}build placeholder resolves per-provider._headers,_redirects,_routes.jsongenerated by the build, with cache headers for hashed assets and SWR for HTML.Test plan
bun run test— full unit + fixture suite passes (Puppeteer fixtures use--no-sandboxin CI).bun run build— static site, all 11 provider bundles, both ZIPs, API JSON, and CF config produced cleanly.bun bin/impeccable.mjs detect public/index.html— CLI runs end-to-end./critique— sub-agent flow, console-based overlay reading, persona scoring./visual-modepage renders live overlay on synthetic slop demos.🤖 Generated with Claude Code