- Bump skills plugin version 2.1.1 -> 3.0.0 (plugin.json, marketplace.json,
harness SKILL.md files). CLI and Chrome extension unchanged.
- Remove prefixed universal zip bundle and all related code:
factory.js prefix/outputSuffix options, zip.js variant pass, utils.js
prefixSkillReferences, the "universal-prefixed" entry in
download-providers.js, and the matching test suite in utils.test.js.
- Redesign Get Started step 1 "Install the skill and CLI": two terminal
rows (npx skills + npm i -g impeccable) with paired notes, drop the
Recommended badge.
- Collapse "Other install methods" back into a <details> element so the
primary install path is the first thing users see.
- Simplify step 3 to "Add the Chrome extension": remove the CLI tool
block (now in step 1), use standard .btn .btn-primary for the CTA so
it matches other primary buttons (square corners, accent slide-up
hover), and lay out the preview screenshot next to the button instead
of stacked so the screenshot no longer dominates vertical space.
- CLAUDE.md: rewrite with v3.0 architecture, the "no em dash also means
no --" rule, the harness-dirs-are-tracked gotcha, the named-export
test-spy warning, and the evals inline-skill.ts sync note.
- AGENTS.md, DEVELOP.md: drop prefixed variant references.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Biggest change in a while. Users previously had 18 standalone skill
entries cluttering their /menu; now they have one entry (/impeccable)
that routes to 20 specialized commands via argument dispatch. The pin
mechanism (/impeccable pin audit) restores standalone shortcuts on
demand for commands users hit all the time.
## Architecture
- Single /impeccable skill with command router section in SKILL.md
- 20 commands served via reference files under source/skills/impeccable/reference/
- /impeccable pin <command> creates a lightweight redirect shim so users
who prefer /audit, /polish, etc. can still have them
- Context gathering (teach) auto-runs on first use
- command-metadata.json is the single source of truth for command
descriptions, argument hints, and relationships
## Site rewrite
- Docs URL: /skills renamed to /docs (with /skills permanent redirects)
- Homepage hero frames Impeccable as "one skill with 20 commands"
- "Get Started" split into 50/50 install + how-to-use with editorial
numbered steps, /impeccable shown as the home command with three modes
- New /docs overview: home command hero card + dense category rows
matching the old cheatsheet density, with leads-to/pairs-with/
combines-with relationship metadata served from a shared source
- Cheatsheet merged into /docs, /cheatsheet redirects
- Magazine spread and mobile cards show /impeccable as a stacked
namespace label above the command name at full display size
- Periodic table updated with craft/teach/extract as first-class cells
- Skill detail pages generate from reference files, with an editorial
wrapper per command for tagline + body
- Tutorials and anti-patterns pages updated to use /impeccable <cmd>
## Build system
- Dead code removed (scripts/lib/transformers/shared.js)
- Build log wording fixed ("1 skill" not "1 skills (1 user-invocable)")
- generateApiData fallback branch removed (throws loudly if metadata
missing instead of silently degrading)
- Commands API includes editorial tagline alongside the long description;
UI surfaces prefer tagline for human display, description for auto-
trigger keyword matching
## Gitignore
- Added .claude/scheduled_tasks.lock, .claude/settings.local.json to
ignore list (local Claude Code state that should not be tracked).
- Harness skill directories (.claude/skills/, .agents/skills/, etc.)
remain tracked by design: npx skills reads them from this repo at
install time and they enable clean submodule use.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
generateYamlFrontmatter only re-quoted values starting with `[` or `{`,
but parseFrontmatter strips surrounding quotes on input. Descriptions
containing `: ` (e.g. "Also handles: critique...") round-tripped into
unquoted plain scalars that YAML parsers reject. Added a yamlNeedsQuoting
check covering colon-space, space-hash, YAML indicator chars, reserved
keywords, and number-like strings, plus regression tests.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two detector bugs that produced false positives on sites like uselinkshot.com:
1. The bg-black regex matched Tailwind opacity modifiers (bg-black/3,
hover:bg-black/5) because / is a word boundary. Added negative lookahead.
2. resolveBackground ignored url() background-images, walking past them to
the body's white bg. White text on a dark hero image was flagged as
1.0:1 white-on-white. Now bails on url() images like it does for gradients.
Also: extension build auto-generates dist/extension.zip, version bumps for
CLI (2.1.7) and extension (1.0.1).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The gallery page had broken styling and missing images. The visual
mode page already has the same specimen gallery in a better layout.
- Removed public/gallery.html and its build entry point
- Updated homepage links to point to /visual-mode#try-it-live
- Added 301 redirect from /gallery to /visual-mode#try-it-live
- Added id="try-it-live" anchor to the visual-mode gallery section
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Build system now injects skills version (from plugin.json) into
every SKILL.md frontmatter as a version field
- CLI reads the version from the local impeccable SKILL.md and
displays it in check/update output
- Hash comparison normalizes the version field (so a version bump
alone doesn't trigger a full re-download)
- Removed misleading CLI version display from skills commands
CLI bumped to v2.1.5.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The refactored factory.js transformer dropped script file support that
existed in the old shared.js version. Scripts were read from source
but never written to dist/, so npx skills installed skills without the
cleanup-deprecated.mjs script, causing errors on first load.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The build still generates deprecated stubs in dist/ (so the cleanup
script can redirect users), but now removes them from the repo's own
harness directories so they don't clutter the local skill list.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use h3 headings with custom IDs instead of inline spans so the browser
scrolls to the right position. Added {#id} syntax support to the
markdown heading renderer.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Clarify that /impeccable craft runs /shape internally (not the other way around)
- Add three-mode documentation (freeform/craft/teach) to /impeccable page with anchor links
- Add sidebar sub-links for /impeccable craft and /impeccable teach
- Fix hallucinated npx impeccable live description in tutorial and visual-mode page
- Remove nonsensical "Do not skip the independent part" from critique tutorial
- Make Step 4 less prescriptive (users can fix all at once or one-by-one)
- Improve CLI and browser extension install copy with specific features and use cases
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extension is now live on the Chrome Web Store. Replace all
coming-soon placeholders with install links on the homepage,
visual-mode page, and overlay tutorial.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
These self-contained HTML files and the detector script were served
by the dev server from public/ but never made it into the Cloudflare
Pages build directory, causing 404s on the deployed site.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cloudflare Pages merges headers from all matching rules rather than
overriding, so the path-specific SAMEORIGIN conflicted with the global
DENY. Switch the global rule to SAMEORIGIN since the site only frames
its own antipattern example pages.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The global _headers rule set DENY for all paths, preventing the
/visual-mode/ page from embedding /antipattern-examples/ in an iframe.
Add a path-specific SAMEORIGIN override for /antipattern-examples/*.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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.
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.
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.
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.
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.
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.
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).
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) <noreply@anthropic.com>
Two visual fixes to the skill detail demo block.
1. Remove the 24px padding from .split-content. This padding was the
'persistent indentation' visible in the screenshot: it created a
white band between the container border and demo content that had
its own card background. The overdrive demo (which fills the
container via absolute canvases and 100% divs) lost some of its
bleed to the padding; the polish-style demos (small centered cards)
don't need it because .split-content already uses flex centering.
2. Restructure the skill detail header into a .skill-detail-hero
wrapper that holds both the header text and the demo block.
- At >=1100px viewport: switch to a grid (minmax(0,1fr) auto),
text column on the left, demo on the right, align-items:center
so the eyebrow/title/tagline center with the demo vertically.
The demo floats as an editorial hero element alongside the title.
- Below 1100px: stack (demo under the header) with clamp-based
spacing between them. Same visual as before, just now inside the
hero wrapper.
- skill-detail-hero--has-demo class so skills without a demo
(/shape) keep the single-column layout with no grid quirks.
Caption was sitting with a ~56px gap below the labels (32px container
padding-bottom + 24px caption margin-top). Move the <p> inside
.split-comparison so the padding wraps the caption too, not separates
it from the labels. Drop caption margin-top from 24px to 12px and tone
the color down from charcoal to ash + 0.8125rem to match the rest of
the supporting-text rhythm on these pages.
Ported the homepage's padding-margin trick: .split-comparison now has
20px of padding around the visible box, with a matching negative
top/bottom margin so the padding does not affect layout flow. The
pointer event listeners move from .split-container to .split-comparison
so the hover tracking engages inside the buffer and only resets when
the mouse leaves the full padded area. Percentage math still reads
.split-container.getBoundingClientRect() so the divider position stays
aligned with the visible box.
This matches how the landing-page split demo feels: graze the edge
and the divider holds; leave the box entirely and it eases back.
Four issues reported on /skills/overdrive (and every other skill demo):
1. The demo block was centered inside the content column, looking odd
against the otherwise left-aligned page. Drop 'margin: 0 auto' from
both .split-comparison and .split-container, and remove the nested
max-width so the whole demo left-aligns at 500px max-width with no
centering.
2. The BEFORE and AFTER labels were stretching beyond the demo box
because .split-comparison (560px) was wider than .split-container
(500px) and .split-labels was using justify-content: space-between
across the wider parent. Collapse the two max-widths to a single
500px cap so the labels now sit flush with the container edges.
3. The label row was sitting way below the demo (16px margin-top plus
the height-stretched container). Tighten margin-top to 10px.
4. The inline split-compare handler only supported click-and-drag. The
homepage effect also tracks hover on devices with hover:hover, so
the mouse sweeps the divider and leaving the box eases it back to
the default. Port that behavior: matchMedia('(hover: hover)') to
detect, pointerenter/leave to toggle a hovering flag, pointerdown/up
for drag, and a tiny lerp on requestAnimationFrame so the return to
center feels smooth. Eyebrow text now reads 'Drag or hover to
compare' to signal both modes.
Also drop text-align: center on .skill-demo-eyebrow and .skill-demo-caption
for the same left-align consistency.
Two changes bundled:
1. Before/after split demos on every skill detail page.
- loadCommandDemos() in sub-pages-data.js: dynamically imports each
module in public/js/demos/commands (the same files the homepage
uses), returning a { skillId: { id, caption, before, after } } map.
Falls back to a warn-and-continue if a demo file can't be loaded
so one bad demo doesn't break the whole generator.
- buildSubPageData becomes async; caller in build-sub-pages.js
awaits it.
- Each skill object gets a .demo field (may be null for /shape).
- renderSkillDemo() produces the .split-comparison markup matching
the homepage: .split-container with .split-before + .split-after
+ .split-divider, plus Before/After labels and the caption. The
block sits between the detail header and the editorial wrapper
so readers see the visual before reading any prose.
- sub-pages.css ports the core .split-* layout from main.css (the
.slop-* and .impeccable-* helpers are homepage-specific and not
copied). Height is 360px to match the docs column.
- render-page.js grows a lightweight inline split-compare init
script (60 lines of vanilla JS) that handles drag and the skewed
clip-path without depending on the homepage's full lerp/ResizeObserver
module. Runs only on pages that actually have .split-container.
2. Sidebar reorder: Tutorials first, then skills.
Walk-throughs are the on-ramp; they belong at the top of the sidebar
where a new visitor will find them. Add <hr class="skills-sidebar-divider">
between the Tutorials group and the first skill category so the two
sections read as distinct.
Verified: /skills/polish, /skills/bolder, /skills/critique all render
the demo block. /skills/shape correctly has none. Sidebar on any /skills
or /tutorials page shows Tutorials first, then a thin mist divider,
then Create / Evaluate / Refine / Simplify / Harden / System skill
categories.
Two small-to-medium improvements bundled together.
1. Copy buttons on every rendered code block.
- render-markdown.js: wrap each fenced code block in a .code-block-wrap
container and emit a <button class="code-block-copy" data-copy="...">
alongside it. Button text is set via CSS ::before content so the
'Copy' / 'Copied' label is a single toggle class (.is-copied).
- render-page.js: 12-line inline script at the end of the body wires
a delegated click handler that calls navigator.clipboard.writeText
and flips .is-copied for 1.5s.
- sub-pages.css: button styles matching the dark terminal palette,
hidden until you hover the code block, accent-colored success state.
2. Merge Skills and Tutorials under a single Docs nav item.
- Rename the Skills nav link to 'Docs' in every header (partial +
4 hand-authored pages). Drop the separate Tutorials nav item; it
now lives inside Docs. Anti-patterns stays as its own top-level.
- scripts/build-sub-pages.js: replace renderSkillsSidebar and
renderTutorialsSidebar with a unified renderDocsSidebar that shows
every skill category followed by a Tutorials group. Takes a
current descriptor of shape { kind: 'skill'|'tutorial', id|slug }
so both skill detail and tutorial detail pages can mark the active
row. activeNav on every /skills/* and /tutorials/* page is now
'docs'; the shared site header's data-nav matches.
Verified: /skills/polish and /tutorials/getting-started both render
with the unified Docs sidebar (all 21 skills grouped by category +
both tutorials as a final group). The Docs nav item is aria-current
on both. Copy buttons appear on every fenced code block and toggle
to 'Copied' when clicked.
Feedback round from first review of the skill pages. Six concrete fixes:
1. Contain the auto-rendered SKILL.md in a distinct card.
The "skill itself" section was flowing straight into the editorial
wrapper above, making the two blocks read as one long mixed
document. Wrap the canonical body in .skill-source-card: white
paper background, mist border, rounded, with a small "SKILL.md"
badge header and an italic subtitle. Drop the old full-width
divider treatment; the card does the visual separation work.
2. Rewrite the /impeccable "do not fight the opinion" pitfall.
The old text said "fighting the opinion usually produces worse
output", which discouraged informed pushback. Replace with language
that explicitly encourages users with real reasons (brand guideline,
accessibility, user research) to push back; the skill raises the
floor, not overrules your judgment when you have evidence.
3. Move /onboard from create to refine.
Onboarding is refinement of empty states and first-run experiences,
not greenfield creation. Fixed in:
- scripts/lib/sub-pages-data.js SKILL_CATEGORIES
- public/js/data.js commandCategories
Both locations now list onboard under 'refine'.
4. De-dupe overdrive and animate.
- overdrive: "how it works" listed 7 techniques as bullets and then
"try it" listed 5 concrete examples using the same 7 techniques.
Collapse "how it works" into a tight paragraph and make "try it"
a specific scenario instead of a laundry list.
- animate: pitfalls repeated the "no layout properties" rule that
was already stated in "how it works". Drop the duplicate.
5. Remove outdated tutorial guidance.
getting-started.md said "Cursor needs Nightly channel plus Agent
Skills in Settings. Gemini CLI needs the preview version." Neither
is true anymore. Replace with a generic pointer to check the
harness's own skill docs.
6. Embed the live visual overlay in critique-with-overlay tutorial.
The tutorial now renders the same demo iframe the homepage uses
(/antipattern-examples/visual-mode-demo.html) inside step 2, with
a mac-window chrome frame that mirrors the homepage preview. New
.tutorial-embed CSS in sub-pages.css defines the header with
traffic-light dots + mono title, the iframe body (520px tall),
and an optional caption. The user now sees the overlay in action
before being asked to run it locally.
Third and final new section. Adds the tutorials browser with two
hand-written walk-throughs: "Getting started" and "Critique with the
visual overlay". Same docs-browser shell as skills and anti-patterns.
- content/site/tutorials/getting-started.md
Five-minute install, teach, and first polish pass. Covers npx skills
add, answering the teach interview honestly, and running /polish on
an existing page. Includes common issues and what to try next.
- content/site/tutorials/critique-with-overlay.md
Ten-minute walk-through of the full /critique workflow with the
browser overlay. Covers the two-assessment model (LLM + detector),
the three ways to open the overlay (extension, inside critique, or
standalone via `npx impeccable live`), the fix-reload-verify loop,
and how to interpret merged reports.
- scripts/build-sub-pages.js
- renderTutorialsSidebar(): tutorial list with current-item marking
- renderTutorialsIndexMain(): card grid with mono order numbers,
italic display titles, taglines, arrow affordances
- renderTutorialDetail(): breadcrumb eyebrow, italic display title,
plain tagline, rendered markdown body inside .prose
- Wire both into the main generator loop.
- sub-pages-data.js already reads content/site/tutorials/*.md and
sorts by frontmatter.order, so no data-layer change was needed.
- public/css/sub-pages.css
- .tutorial-card grid (number, body, arrow) with hover border
darken + arrow slide. Mono order numbers, italic display titles.
- .tutorial-detail header with italic display title and plain sans
tagline (matching the visual rhythm of skill detail pages without
the mono command style since tutorials are prose, not commands).
Add the anti-patterns browser. Single page, same docs-browser shell as
the skills section, but the sidebar is a table of contents instead of
a navigation list.
- scripts/build-sub-pages.js
- groupRulesBySection(): bucket the 25 detection rules by skill
section (Visual Details, Typography, Color & Contrast, Layout &
Space, Motion) plus a 'General quality' bucket for the 7 rules
without a skillSection cross-reference. Each bucket sorts slop
rules first, then quality rules alphabetically.
- renderAntiPatternsSidebar(): TOC with anchor links to each
section, per-section rule count on the right, reusing the
.skills-sidebar styles with anti-patterns-specific tweaks for
the two-column item layout.
- renderRuleCard(): rule id + category badge + name + description
+ optional 'See in /impeccable' link that jumps to the right
anchor on the skill detail page.
- renderAntiPatternsMain(): editorial header ('25 detection rules')
+ 'How to read this' legend callout + six sections each with a
grid of rule cards.
- Wire the new page into the main generator loop.
- public/css/sub-pages.css
- .rule-card grid (auto-fill minmax 320px) with header row, mono
rule id, pill category badge, bold name, body description,
arrow skill link at the bottom.
- .rule-card-category variants: slop uses accent-dim background
with accent text, quality uses mist background with charcoal text.
- Section headers with baseline-aligned title and right-aligned
rule count, matching the rhythm of the /skills overview.
- Anti-patterns sidebar count styling (tabular-nums, ash).
Verified: /anti-patterns returns 200 with 25 rule cards rendered across
all six section anchors.
Three tightening passes based on visual feedback.
- Sidebar alignment. The skills layout now uses the same horizontal
padding clamp as the site header, so the sidebar column's left edge
sits under the brand logo above. Sidebar items drop their 10px left
padding and sit flush with the column edge; the 2px active-item
accent lives in a -2px margin so it hangs in the layout gutter
without pushing text right. Drop the max-width constraint so the
docs shell extends wall-to-wall on wide screens like a real docs site.
- Sidebar font. The command list was 0.8125rem which read too small
next to the sidebar group titles. Bump to 0.875rem and tighten the
vertical rhythm (4px vs 3px padding, group title margin-bottom 6px).
- Skill detail title. The title was set in mono at ~3rem and the
tagline in italic display, which inverted the visual weight (the
tagline felt heavier than the title). Rework to match the landing
page's .spread-command-name palette styling:
- Title: display serif, clamp(3rem, 5.5vw, 5rem), line-height 0.95
- Leading '/' split into <span class="skill-detail-title-slash">
and colored with --color-accent at weight 300 so it reads as a
quiet prefix glyph
- Tagline: body sans at 1-1.125rem, no italics, max-width 60ch
Builds on the /skills overview landing. Every skill detail page now lives
inside the same sticky-sidebar shell, with the active skill marked via
aria-current and pulled out of the list with a 2px accent border.
- scripts/build-sub-pages.js: detail pages now wrap the .skill-detail
body in wrapInDocsLayout() and pass currentSkillId into the sidebar
renderer so the active row is marked
- public/css/sub-pages.css sidebar pass:
- Narrow from 260px to 200px (was way too wide for the content)
- Drop the redundant "Skills" label at the top (it was visually
competing with the category titles); keep the node for a11y via
visually-hidden styling
- Group titles shrink to 0.625rem mono uppercase in --color-ash so
they read as quiet signposts, not headings
- Tighten item line-height to 1.5 and vertical padding to 3px
- Replace the box-shadow inset accent with a border-left accent on
the active item, leaving it visually pulled left
- Simplify .skill-detail sizing so it works inside .skills-main
without its own max-width / padding
- Ban em dashes in user-facing copy everywhere:
- Rewrite 9 copy lines in index.html, 2 title tags in gallery.html,
inline category descriptions, the /skills overview lede + how-to,
two skills-generator comments, one aria-label, and the page titles
for Skills and skill detail pages
- New validateNoEmDashes step in scripts/build.js scans content/site,
public/*.html, and the sub-page generator sources. Build fails on
any — or — in user-facing copy. Uses commas, colons, periods,
or parentheses instead
Build green: all 26 HTML entrypoints bundle cleanly, all four hand-authored
pages retain the shared site header, anti-pattern rules still match
SKILL.md, zero em dashes.
The old /skills was one long scrollable list. Replace it with a proper
app-shell layout: sticky left sidebar with every skill grouped by
category, main column on the right with an orientation piece (intro
+ 'how to pick one' callout + category sections with chip lists).
- scripts/build-sub-pages.js
- renderSkillsSidebar(): the sticky left rail used across the /skills
section. Takes an optional currentSkillId so we can mark the active
skill with aria-current when detail pages adopt the same shell in a
later commit
- renderSkillsOverviewMain(): the right column. Editorial header,
'how to pick one' callout that inlines 5 hot-link skill references,
then one block per category (title, count, description, chip row
of all skills in that category)
- wrapInDocsLayout(): tiny helper that pastes the sidebar and main
into a .skills-layout grid
- public/css/sub-pages.css
- Copy .skip-link a11y styles over from main.css so the 'Skip to
content' link is properly hidden until keyboard focus (was showing
unstyled at the top-left of every generated page)
- Add .skills-layout grid shell (260px sidebar + flex main column,
max-width 1400px, clamp-based horizontal padding)
- Add sticky .skills-sidebar with its own scroll-y overflow and a
thin scrollbar; group titles + mono link list; aria-current styling
with a 2px left accent bar
- Add .skills-overview-howto callout, category sections with chip
rows, and mobile collapse (sidebar becomes an inline block above
the main column at <=920px)
- Introduce --site-header-height and --skills-sidebar-width tokens
No detail page layout changes in this commit — those still use the
single-column .skill-detail shell.
Ships the first new sub-page section. Every user-invocable skill now has
its own page at /skills/{id}, with the canonical SKILL.md body rendered
via marked. The index at /skills lists all 21 skills grouped by category.
Editorial wrappers are opt-in: if content/site/skills/{id}.md exists, it
renders above the canonical body (with a "The skill itself" divider).
All 21 pages currently ship with the auto-rendered body only; hand-written
wrappers land in the next few commits.
- scripts/lib/sub-pages-data.js: builds the data model. Reuses
readSourceFiles() from lib/utils.js for skill content; parses the
ANTIPATTERNS array out of src/detect-antipatterns.mjs; reads optional
editorial wrappers from content/site/skills/*.md; validates that every
user-invocable skill has a category entry (build fails loudly if not).
- scripts/build-sub-pages.js: orchestrator. Writes generated HTML into
public/skills/*.html (gitignored). Called from both scripts/build.js
(before buildStaticSite) and server/index.js (at module load) so dev
and prod share the same generation code path.
- scripts/lib/render-page.js: new assetDepth parameter so generated
pages one level deep under public/ use relative paths (../favicon.svg,
../css/sub-pages.css) that Bun's HTML loader can resolve on disk.
- scripts/build.js: pass generated files into Bun.build entrypoints;
post-process to flatten build/public/* → build/* (Bun preserves the
public/ prefix when entrypoints span multiple depths).
- server/index.js: generateSubPages() runs at module load; new routes
/skills, /skills/:id, /anti-patterns, /tutorials, /tutorials/:slug
serve the pre-generated files via Bun.file().
- public/css/sub-pages.css: adds sub-page layout shell, skills index
grouped-list styling, skill detail header/meta chips/divider, collapsed
<details> reference sections, and a .prose block for rendered markdown
with editorial typography, code blocks, and inline code.
Verified: bun run build produces 26 HTML files (4 hand-authored + 22
generated), all flat under build/. Dev server returns 200 on /skills,
/skills/polish, /skills/impeccable, /skills/critique. Tests pass.
Every hand-authored HTML page now carries the same sticky top nav:
Home / Skills / Anti-Patterns / Tutorials / Gallery / GitHub. The three
future sections (skills, anti-patterns, tutorials) will land behind
these links in subsequent commits.
- Add <header class="site-header"> with a <!-- site-header v1 --> marker
to index, cheatsheet, gallery, privacy
- Each page links public/css/sub-pages.css so it picks up the shared
header styling (sticky, backdrop blur, accent-underlined current item)
- Gallery: drop the inline .site-header/.header-brand/.header-nav
definitions that predate the shared component (class names conflicted)
- Cheatsheet: restructure the page title into .cheatsheet-page-header
so it doesn't collide with the new <header class="site-header">; move
the page container max-width/padding off <body> onto .cheatsheet-page-header,
main, and footer so the shared sticky header spans the full viewport
- Privacy: full styling refresh using sub-pages.css tokens + a small
inline block for the page body typography
- Add a validateSiteHeader step to scripts/build.js that fails the build
if any of the four pages drops the marker
- Add /privacy route to server/index.js so extensionless URLs work
All four built pages pass the marker check; dev server serves all four
with 200 and the shared header.
Groundwork for new /skills, /anti-patterns, /tutorials sections.
No user-visible changes yet — this is pure plumbing.
- Split main.css into tokens.css (design tokens + reset, ~100 lines) and
main.css (everything else, imports tokens.css). Lets sub-pages import
only tokens without pulling in the landing-page component CSS.
- Add marked as a dependency.
- Add scripts/lib/render-markdown.js: marked wrapper with a custom link
resolver (skill slugs, reference/*.md anchors, external rel=noopener),
stable heading slugger, and terminal-style code blocks.
- Add scripts/lib/render-page.js: page shell wrapper that injects the
shared site header partial with aria-current marking.
- Add content/site/partials/header.html: shared site header with nav
(Home / Skills / Anti-Patterns / Tutorials / Gallery / GitHub).
- Add public/css/sub-pages.css: shared styles for generated pages, with
.site-header styling (sticky, backdrop blur, accent-underlined active
nav item) and mobile collapse.
Build still produces the same 104 KB landing-page CSS chunk; tests pass.
Tailwind was wired into the build but the project didn't actually use it:
no @tailwind/@theme/@apply directives in source CSS, no utility classes
in any HTML or JS, no tailwindcss dependency in package.json, and no
HTML page linked to the compiled styles.css output.
Bun's HTML loader already handles main.css's @import chain transparently
in both `bun run dev` and `bun run build`. Removing the Tailwind step:
- Deletes buildTailwindCSS() from scripts/build.js (and the now-unused
execSync import)
- Removes public/css/styles.css (dead compiled output that nothing read)
- Updates CLAUDE.md to drop the "must rebuild Tailwind after CSS edits"
instruction
Verified: `bun run build` produces the same 104 KB CSS chunk linked from
index.html via Bun's HTML loader. No more rebuild step after CSS edits.
A new icon-tile-stack detection (the canonical AI feature-card with a
small rounded-square icon container above a heading), backed by a
two-column TDD fixture, plus a single-source-of-truth design that ties
the engine to the impeccable skill so they can no longer drift silently.
Detection
- New icon-tile-stack rule (slop): heading's previousElementSibling is
a 32–128px rounded-square element with a non-transparent background
or border, contains an svg/icon-i child, and sits above (not next to)
the heading. Excludes round avatars, wide thumbnails, side-by-side
layouts, tiny icons, and hero images.
- Two-column fixture convention: a single icon-tile-stack.html with a
flag column (4 cases) and pass column (6 cases), with snippet-text
matching used by the fixture test.
Single source of truth
- Each ANTIPATTERNS entry can now declare skillSection + skillGuideline.
18 of 25 rules carry these fields; the build's new
validateAntipatternRules() in scripts/build.js fails if any declared
skillGuideline isn't found verbatim in the right SKILL.md section.
- scripts/build-extension.js now includes the description field in
extension/detector/antipatterns.json (it was previously dropped).
- The existing count validator was promoted from warn to error so
command count drift fails the build the same way detection drift does.
Impeccable skill DON'Ts
- Added 4 new top-level DON'Ts that target real default AI behavior:
single-font, flat-type-hierarchy, all-caps-body, line-length.
- Cut 7 new DON'Ts I had drafted (tight-leading, tiny-text, wide-tracking,
justified-text, low-contrast, cramped-padding, skipped-heading) because
they teach things every model already knows from CSS/a11y basics. The
detector still catches all of them.
Stale count cleanup
- 22 commands → 21 across 17 references in HTML, README, NOTICE, AGENTS,
plugin.json, marketplace.json (left over from the validate skill removal).
- Dropped the hand-coded "212 design guidelines" marketing copy on the
homepage, which never mapped to any real count.
Sub-agent
- New private .claude/agents/anti-patterns.md captures the full TDD
recipe, schema, plug-in points, jsdom constraints, and pre-commit
checklist so future sessions can add rules end-to-end without
re-investigating the wiring.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Refactors the extension for on-demand injection (no static content_scripts
entry — content script and detector are loaded only when the user actively
opens the Impeccable panel, sidebar pane, or popup). Adds a new "Auto-scan"
preference (default: scan when the Impeccable panel opens, opt-in: scan
when DevTools opens) plus configurable line length (strict/lax) and
highlight blur on/off settings. Adds an Elements panel sidebar that shows
findings for the currently selected element.
Includes substantial overlay UX work: page-pixel-perfect spotlight mask
via clip-path, refined hover/dim states, instant transitions for snappier
feel, copy buttons for findings, hover-from-panel highlighting, and a
brand-aware exception list so the font check no longer flags Roboto on
Google's own properties.
Robustness fixes for the MV3 service worker lifecycle: heartbeat keepalive
plus auto-reconnecting ports across panel/sidebar/devtools so transient
SW restarts don't break the panel UI, and immediate teardown on DevTools
close (replacing an unreliable setTimeout-based defer that didn't survive
SW termination).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a Manifest V3 Chrome extension that injects the detector when
DevTools opens, with a dedicated panel for browsing findings, a toolbar
popup for quick scan/toggle, and per-rule settings synced via
chrome.storage. Categorizes anti-patterns into AI slop vs quality
issues with visual differentiation (sparkle prefix, panel grouping).
Overlay labels are polished with flush positioning, cycling for
multi-finding elements, and synchronized hover darkening.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Merges the impeccable-detect CLI repo (pbakaus/impeccable-cli@831a6cc)
into this repo. The BSL-1.1 license that motivated the split is gone;
everything is now Apache 2.0.
- Add bin/, src/, detection tests and fixtures from CLI repo
- Merge package.json: name → "impeccable", add bin/exports/files fields
- Internal refs now read from local src/ instead of node_modules/
- Update SPDX headers, NOTICE.md, CLAUDE.md, FAQ, npm README
- Add prepack/postpack scripts for CLI-focused README on npm
- Remove terminal license labels (no longer needed)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The CLI and detection engine now live in pbakaus/impeccable-detect
(published as 'impeccable' on npm, BSL-1.1). This repo is purely
Apache 2.0: skills, prompts, website, and build system.
- Remove bin/ (CLI moved to CLI repo)
- Remove README.npm.md (moved to CLI repo)
- Remove @impeccable/detect dependency, add impeccable dependency
- Set package.json to private (no longer published to npm)
- Update all references from @impeccable/detect to impeccable
- Update CLAUDE.md, NOTICE.md, FAQ, and changelog
- Rebuild all provider skill distributions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>