CLI changes (bumped to v2.1.2, skills stay at v2.1.1):
- `npx impeccable skills check` compares local skill files against
the latest bundle and reports whether updates are available
- `npx impeccable skills update` now downloads the bundle first,
compares hashes, and skips with "up to date" if nothing changed
- Removed the local-modifications warning (was confusing for users
who installed via npx skills add)
Versioning:
- CLI (package.json), skills (plugin.json/marketplace.json), and
Chrome extension (manifest.json) are now versioned independently
- CLAUDE.md updated to document when to bump each
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
npx skills update has a known upstream bug (vercel-labs/skills#775)
where it can't find the lock file. Instead of trying it first and
falling back, always use our direct download which is reliable.
Also:
- Site now recommends `npx impeccable skills update` everywhere
instead of `npx skills update`
- Direct download path now re-applies prefix after updating
- Runs cleanup after download to strip deprecated stubs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
npx skills update has a known bug where it can't find the lock file
(vercel-labs/skills#775). Our direct download fallback is the primary
working path, so the message should not sound like an error.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The post-update-cleanup section now instructs the AI to tell the user
what's happening and why before running the script, so file deletions
don't feel unexpected.
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>
- Add max-width: 56ch to install-step-body so long descriptions
don't run edge to edge
- Rewrite FAQ update answer as a scannable list instead of dense
paragraphs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Delete source/skills/ directories for deprecated skills (arrange,
normalize, onboard, extract, frontend-design, teach-impeccable).
The cleanup script handles migration; stubs are no longer needed.
- Add "npx skills update" command to the Stay Updated install section
- Rewrite FAQ update answer: lead with npx skills update, add
troubleshooting for failed updates (re-install + run /impeccable)
- Run cleanup script in `npx impeccable skills update` before
delegating to npx skills update, preventing failures from
deprecated entries in skills-lock.json
- Run cleanup script after `npx impeccable skills install` to remove
leftover files from previous versions
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>
Bundled as source/skills/impeccable/scripts/cleanup-deprecated.mjs,
runs via the self-deleting <post-update-cleanup> section in the skill.
The script:
- Finds all harness skill dirs (.claude, .cursor, .agents, etc.)
- Deletes deprecated skill directories (arrange, normalize, onboard,
extract, frontend-design, teach-impeccable) and i-prefixed variants
- Verifies each file contains "impeccable" before deleting to avoid
touching unrelated user skills with the same name
- Handles both symlinks and regular directories
- Removes matching entries from skills-lock.json (only if source is
pbakaus/impeccable)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The build counts active commands by checking for DEPRECATED in the
skill description. Without this, the old source files were still
counted as active (22) while the site says 18, failing CI.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Version bump across package.json, plugin.json, marketplace.json
- Changelog entry for v2.1 in index.html
- Hero version link updated
- Added <post-update-cleanup> section to impeccable SKILL.md that
detects and removes leftover files from renamed/merged skills
(arrange, normalize, onboard, extract, frontend-design,
teach-impeccable). Verifies files contain "impeccable" before
deleting to avoid touching unrelated user skills. Self-deletes
after first run so it only executes once per update.
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>
Syne stays in reflex_fonts_to_reject but no longer gets a standalone
DON'T rule in the skill or a separate LLM-only entry in the catalog.
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>
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) <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>
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) <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>
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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'.
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.
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.
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) <noreply@anthropic.com>
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.
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.
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) <noreply@anthropic.com>
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.
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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>