PR #50 fixed the output SKILL.md files but the source files, build
scripts, tests, docs, and server code still used the wrong spelling.
Claude Code expects `user-invocable` (with c) for slash command
autocomplete to work.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CI was running `bun test` which ran jsdom fixture tests in bun, causing
timeouts. Now uses `bun run test` which runs unit tests in bun and fixture
tests in node.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix README CLI examples to use `bun bin/impeccable.mjs` instead of `npx impeccable`
- Fix classList.match() crash in Node/jsdom by converting DOMTokenList to string
- Fix overlapping page-level banners by merging all findings into a single banner
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
## Summary
Hey @pbakaus 👋
I ran your skills through `tessl skill review` at work and found some targeted improvements. Here's the full before/after:
| Skill | Before | After | Change |
|-------|--------|-------|--------|
| distill | 31% | 80% | +49% |
| delight | 32% | 74% | +42% |
| polish | 44% | 80% | +36% |
| animate | 55% | 89% | +34% |
| clarify | 49% | 83% | +34% |
| critique | 51% | 85% | +34% |
| normalize | 46% | 80% | +34% |
| optimize | 49% | 83% | +34% |
| adapt | 48% | 81% | +33% |
| extract | 48% | 81% | +33% |
| harden | 49% | 79% | +30% |
| arrange | 51% | 76% | +25% |
| bolder | 51% | 76% | +25% |
| colorize | 48% | 72% | +24% |
| overdrive | 48% | 70% | +22% |
| typeset | 64% | 85% | +21% |
| audit | 61% | 81% | +20% |
| onboard | 56% | 75% | +19% |
| quieter | 59% | 76% | +17% |
| teach-impeccable | 68% | 80% | +12% |
| frontend-design | 76% | 76% | +0% |
**Note:** These skills are auto-generated from `source/skills/` via your build system, so the changes target the source files directly. The `dist/` outputs were regenerated with `bun run build`.
<details>
<summary>Changes made</summary>
All changes are limited to the `description` field in each skill's YAML frontmatter (in `source/skills/*/SKILL.md`). No skill body content was modified.
The consistent improvement across all skills was adding:
- **"Use when..." clauses** with explicit trigger terms — the #1 issue flagged by the reviewer. Without these, agents have no clear signal for when to select a skill from a large library.
- **Natural user keywords** — terms users would actually say (e.g., "too bold", "laggy", "spacing issues") rather than abstract descriptors.
- **Specific concrete actions** — listing what the skill actually does rather than describing outcomes in vague terms.
- **Quoted string format** — ensuring descriptions use standard YAML quoted strings.
`frontend-design` was already well-scored and left unchanged.
</details>
## Type of change
- [x] New / updated skill reference
## Checklist
- [x] Source files updated in `source/`
- [x] `bun run build` ran successfully
- [x] `bun test` passes
- [ ] Tested with at least one provider (Cursor / Claude Code / Gemini CLI / Codex / Copilot / Kiro / OpenCode)
- [ ] README / DEVELOP.md updated if needed
---
Honest disclosure — I work at @tesslio where we build tooling around skills like these. Not a pitch - just saw room for improvement and wanted to contribute.
Want to self-improve your skills? Just point your agent (Claude Code, Codex, etc.) at [this Tessl guide](https://docs.tessl.io/evaluate/optimize-a-skill-using-best-practices) and ask it to optimize your skill. Ping me - [@rohan-tessl](https://github.com/rohan-tessl) - if you hit any snags.
Thanks in advance 🙏
- Add {{scripts_path}} build placeholder that resolves per-provider
(${CLAUDE_PLUGIN_ROOT}/scripts for Claude Code, relative paths for others)
- Restructure critique into sub-agent architecture: LLM review and
detector run independently to avoid bias, results synthesized at end
- Browser overlay is now a visual aid for the user only -- agent reads
findings from console via [impeccable] prefix instead of scrolling
- Add scroll-to-top instruction before overlay injection
- Add allowed-tools frontmatter for detector bash commands
- Fix YAML parser to handle simple string arrays (not just - name: objects)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Claude was running the detector in parallel with the LLM review
despite the section ordering. Added explicit instruction: "Do not
run the detector before or in parallel with your LLM review."
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The detector findings were anchoring the LLM's thinking too heavily,
making the critique feel detector-driven rather than design-driven.
Now the LLM forms its own assessment first, then the detector runs
to catch anything missed. The report structure reflects this: LLM
assessment comes before deterministic scan in the Anti-Patterns
Verdict.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Detection fixes:
- Cramped padding: skip small elements (labels/badges < 100x30px)
and require >20 chars of text content
- Skip browser extension elements (claude-*, cic-*) from scan loop
and quality checks to avoid flagging the Claude Chrome indicator
Skill prompt fixes:
- Use python HTTP server + script tag injection instead of direct
javascript_tool evaluation (avoids CORS issues)
- Add cleanup step for the HTTP server
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
URLs should use browser visualization + LLM analysis instead of
the CLI detector, which would need to install Puppeteer and its
dependencies first.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
- 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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
- Output skills to both .trae-cn/ and .trae/ directories
- Update README with installation instructions for both versions
- Update DEVELOP.md with dual-variant documentation
- Add trae.js transformer with full metadata support (same as Claude Code)
- Update build.js to include Trae in provider transformations
- Update README.md with Trae installation instructions
- Update DEVELOP.md with Trae transformer documentation
Trae uses ~/.trae-cn/builtin_skills/ for skills installation.
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) <noreply@anthropic.com>
Fixes#49 - Skills don't appear in / menu because Claude Code expects
user-invocable (with 'c') not user-invokable (with 'k').
This typo affected all 20 skill files, making them invisible in the
slash command autocomplete menu despite still working when invoked directly.
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
Fixes#42
Added detailed usage examples for the most commonly used commands:
- /audit, /normalize, /critique, /polish with concrete examples
- Shows how to combine commands in a workflow
- Adds 'When to use' guidance for each command
This makes the commands more discoverable and reproducible for users.
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
- 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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>