Merge main into v2.0: consolidate critique skill with scoring, personas, and detection

Merges 54 commits from main including factory-based build system, Trae support,
improved skill descriptions, and security hardening. Consolidates the critique
skill to combine v2.0's sub-agent architecture and automated anti-pattern
detection with main's Nielsen heuristics scoring, cognitive load assessment,
persona-based testing, and structured follow-up workflow. Fixes browser detector
build to create target directory after skill sync.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Paul Bakaus
2026-03-27 22:20:04 -07:00
co-authored by Claude Opus 4.6
375 changed files with 47897 additions and 5781 deletions
+46 -8
View File
@@ -6,6 +6,7 @@ import { initLensEffect } from "./js/components/lens.js";
import { initFrameworkViz } from "./js/components/framework-viz.js";
import { initScrollReveal } from "./js/utils/reveal.js";
import { initAnchorScroll, initHashTracking } from "./js/utils/scroll.js";
import { initSectionNav } from "./js/components/section-nav.js";
// ============================================
// STATE
@@ -17,6 +18,16 @@ let allCommands = [];
// CONTENT LOADING
// ============================================
function escapeHtml(value) {
if (typeof value !== "string") return "";
return value
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;")
.replaceAll("'", "&#39;");
}
async function loadContent() {
try {
const [commandsRes, patternsRes] = await Promise.all([
@@ -96,13 +107,13 @@ function renderPatternsWithTabs(patterns, antipatterns) {
const tabsHTML = patterns
.map((category, i) => `<button
class="pattern-tab${i === 0 ? ' active' : ''}"
data-tab="${category.name}"
data-tab="${escapeHtml(category.name)}"
role="tab"
id="${tabId(category.name)}"
aria-selected="${i === 0 ? 'true' : 'false'}"
aria-controls="${panelId(category.name)}"
tabindex="${i === 0 ? '0' : '-1'}"
>${category.name}</button>`)
>${escapeHtml(category.name)}</button>`)
.join("");
// Build panels with WAI-ARIA attributes
@@ -112,7 +123,7 @@ function renderPatternsWithTabs(patterns, antipatterns) {
return `
<div
class="pattern-panel${i === 0 ? ' active' : ''}"
data-panel="${category.name}"
data-panel="${escapeHtml(category.name)}"
role="tabpanel"
id="${panelId(category.name)}"
aria-labelledby="${tabId(category.name)}"
@@ -122,13 +133,13 @@ function renderPatternsWithTabs(patterns, antipatterns) {
<div class="pattern-column pattern-column--anti">
<span class="pattern-column-label" id="dont-label-${i}">Don't</span>
<ul class="pattern-list" aria-labelledby="dont-label-${i}">
${antiItems.map((item) => `<li class="pattern-item pattern-item--anti">${item}</li>`).join("")}
${antiItems.map((item) => `<li class="pattern-item pattern-item--anti">${escapeHtml(item)}</li>`).join("")}
</ul>
</div>
<div class="pattern-column pattern-column--do">
<span class="pattern-column-label" id="do-label-${i}">Do</span>
<ul class="pattern-list" aria-labelledby="do-label-${i}">
${category.items.map((item) => `<li class="pattern-item pattern-item--do">${item}</li>`).join("")}
${category.items.map((item) => `<li class="pattern-item pattern-item--do">${escapeHtml(item)}</li>`).join("")}
</ul>
</div>
</div>
@@ -167,7 +178,9 @@ function renderPatternsWithTabs(patterns, antipatterns) {
p.classList.remove('active');
p.setAttribute('hidden', '');
});
const activePanel = container.querySelector(`[data-panel="${tabName}"]`);
const escapedName = CSS.escape(tabName);
const activePanel = container.querySelector(`[data-panel="${escapedName}"]`);
if (!activePanel) return;
activePanel.classList.add('active');
activePanel.removeAttribute('hidden');
};
@@ -214,6 +227,20 @@ function renderPatternsWithTabs(patterns, antipatterns) {
// EVENT HANDLERS
// ============================================
// Sync prefix radio buttons to hidden checkbox + update download button label
document.querySelectorAll('input[name="prefix-choice"]').forEach((radio) => {
radio.addEventListener('change', () => {
const prefixToggle = document.getElementById('prefix-toggle');
if (prefixToggle) prefixToggle.checked = radio.value === 'prefixed';
const btnLabel = document.querySelector('#download-zip-btn span');
if (btnLabel) {
btnLabel.textContent = radio.value === 'prefixed'
? 'Download prefixed zip'
: 'Download universal zip';
}
});
});
// Handle bundle download clicks via event delegation
document.addEventListener("click", (e) => {
const bundleBtn = e.target.closest("[data-bundle]");
@@ -229,10 +256,20 @@ document.addEventListener("click", (e) => {
const copyBtn = e.target.closest("[data-copy]");
if (copyBtn) {
const textToCopy = copyBtn.dataset.copy;
navigator.clipboard.writeText(textToCopy).then(() => {
const onCopied = () => {
copyBtn.classList.add('copied');
setTimeout(() => copyBtn.classList.remove('copied'), 1500);
});
};
if (navigator.clipboard?.writeText) {
navigator.clipboard.writeText(textToCopy).then(onCopied).catch(() => {});
} else {
// Fallback for non-HTTPS or older browsers
const ta = Object.assign(document.createElement('textarea'), { value: textToCopy, style: 'position:fixed;left:-9999px' });
document.body.appendChild(ta);
ta.select();
try { document.execCommand('copy'); onCopied(); } catch {}
ta.remove();
}
}
});
@@ -248,6 +285,7 @@ function init() {
initScrollReveal();
initGlassTerminal();
initFrameworkViz();
initSectionNav();
loadContent();
document.body.classList.add("loaded");
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

+18
View File
@@ -0,0 +1,18 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="256" height="256">
<defs>
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#1f1f1f"/>
<stop offset="100%" stop-color="#141414"/>
</linearGradient>
<linearGradient id="accent" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#d4456a"/>
<stop offset="100%" stop-color="#b03058"/>
</linearGradient>
</defs>
<!-- Background -->
<rect width="256" height="256" rx="48" fill="url(#bg)"/>
<!-- Subtle accent line -->
<rect x="56" y="196" width="144" height="2.5" rx="1.25" fill="url(#accent)" opacity="0.6"/>
<!-- Slash mark -->
<text x="128" y="178" font-family="system-ui, -apple-system, 'Helvetica Neue', sans-serif" font-size="160" font-weight="400" fill="#f5f3ef" text-anchor="middle" letter-spacing="-8">/</text>
</svg>

After

Width:  |  Height:  |  Size: 887 B

+9
View File
@@ -1,6 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-TEXGHC7V34"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-TEXGHC7V34');
</script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Impeccable Command Cheatsheet</title>
+507 -62
View File
@@ -133,6 +133,98 @@ button, input, textarea, select {
outline-offset: 2px;
}
/* ============================================
FLOATING BOTTOM NAV
============================================ */
.section-nav {
position: fixed;
bottom: var(--spacing-md);
left: 50%;
transform: translateX(-50%) translateY(20px);
z-index: 100;
display: flex;
align-items: center;
gap: 2px;
padding: 4px;
background: oklch(98% 0 0 / 0.85);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 1px solid var(--color-mist);
border-radius: 100px;
box-shadow: 0 4px 24px -4px rgba(0,0,0,0.12), 0 1px 3px rgba(0,0,0,0.06);
opacity: 0;
pointer-events: none;
transition: opacity 0.4s var(--ease-out), transform 0.4s var(--ease-out);
}
.section-nav.is-visible {
opacity: 1;
pointer-events: auto;
transform: translateX(-50%) translateY(0);
}
.section-nav-item {
display: flex;
align-items: center;
gap: 4px;
padding: 8px 12px;
text-decoration: none;
border-radius: 100px;
transition: background 0.2s ease, color 0.2s ease;
}
.section-nav-item:hover {
background: var(--color-mist);
text-decoration: none;
}
.section-nav-item.is-active {
background: var(--color-ink);
}
.section-nav-item.is-active .section-nav-num {
color: var(--color-paper);
}
.section-nav-item.is-active .section-nav-label {
color: var(--color-paper);
}
.section-nav-num {
font-family: var(--font-mono);
font-size: 0.5625rem;
font-weight: 500;
color: var(--color-ash);
letter-spacing: 0.02em;
transition: color 0.2s ease;
}
.section-nav-label {
font-family: var(--font-body);
font-size: 0.6875rem;
font-weight: 500;
color: var(--color-charcoal);
transition: color 0.2s ease;
white-space: nowrap;
}
@media (max-width: 700px) {
.section-nav-label {
display: none;
}
.section-nav-item {
padding: 8px 10px;
}
}
@media (max-width: 400px) {
.section-nav {
display: none;
}
}
/* ============================================
BASE STYLES
============================================ */
@@ -297,7 +389,7 @@ code {
justify-content: center;
min-height: 100vh;
min-height: 100dvh;
padding: var(--spacing-2xl) 0;
padding: var(--spacing-lg) 0 var(--spacing-2xl);
background: var(--color-paper);
}
@@ -308,15 +400,27 @@ code {
z-index: 10;
color: var(--color-ash);
transition: color 0.2s ease;
display: flex;
align-items: center;
gap: 6px;
text-decoration: none;
}
.github-link:hover {
color: var(--color-ink);
text-decoration: none;
}
.github-link svg {
width: 28px;
height: 28px;
.github-stars {
font-family: var(--font-mono);
font-size: 0.75rem;
font-weight: 500;
color: var(--color-ash);
transition: color 0.2s ease;
}
.github-link:hover .github-stars {
color: var(--color-ink);
}
.hero-combined-container {
@@ -468,8 +572,9 @@ code {
.hero-logos-inline {
display: flex;
align-items: center;
gap: var(--spacing-sm);
flex-direction: column;
align-items: flex-start;
gap: 6px;
}
.hero-logos-inline .hero-logos-label {
@@ -482,6 +587,7 @@ code {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.hero-logos-inline .hero-logos-row img {
@@ -586,7 +692,7 @@ code {
.split-after {
/* Angled clip-path matching divider's skewX(-10deg): top-right, bottom-left */
clip-path: polygon(78% 0%, 100% 0%, 100% 100%, 62% 100%);
clip-path: polygon(58% 0%, 100% 0%, 100% 100%, 42% 100%);
z-index: 2;
background: var(--color-paper);
}
@@ -595,7 +701,7 @@ code {
position: absolute;
top: 0;
bottom: 0;
left: 70%;
left: 50%;
width: 3px;
background: var(--color-accent);
transform: translateX(-50%) skewX(-10deg);
@@ -2136,10 +2242,126 @@ code {
max-width: 60ch;
}
/* Install steps layout */
.install-steps {
display: flex;
flex-direction: column;
gap: var(--spacing-xl);
max-width: 640px;
margin: 0 auto var(--spacing-xl);
}
.install-step-header {
display: flex;
align-items: flex-start;
gap: var(--spacing-md);
margin-bottom: var(--spacing-md);
}
.install-step-number {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
flex-shrink: 0;
border-radius: 50%;
border: 1.5px solid var(--color-accent);
color: var(--color-accent);
font-family: var(--font-display);
font-size: 1.125rem;
font-weight: 600;
line-height: 1;
}
.install-step-meta {
padding-top: 6px;
}
.install-step-title {
font-size: 1.125rem;
font-weight: 600;
margin: 0;
line-height: 1.4;
}
.install-step-desc {
font-size: 0.875rem;
color: var(--color-ash);
margin: 2px 0 0;
}
/* Subscribe card (step 2) */
.install-subscribe {
max-width: 640px;
margin: 0 auto;
}
.install-subscribe iframe {
display: block;
max-width: 100%;
}
.install-subscribe-btn {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 0.9375rem;
font-weight: 600;
letter-spacing: 0.03em;
color: var(--color-paper);
text-decoration: none;
padding: 1rem 2rem;
background: var(--color-ink);
border: 1px solid var(--color-ink);
transition: all var(--duration-base) var(--ease-out);
flex-shrink: 0;
position: relative;
overflow: hidden;
}
.install-subscribe-btn::before {
content: '';
position: absolute;
inset: 0;
background: var(--color-accent);
transform: translateY(100%);
transition: transform var(--duration-base) var(--ease-out);
z-index: 0;
}
.install-subscribe-btn:hover::before {
transform: translateY(0);
}
.install-subscribe-btn svg,
.install-subscribe-btn span {
position: relative;
z-index: 1;
}
.install-subscribe-btn:hover {
color: var(--color-paper);
}
.install-subscribe-note {
font-size: 0.8125rem;
color: var(--color-ash);
margin: 0;
line-height: 1.5;
}
@media (max-width: 600px) {
.install-subscribe {
flex-direction: column;
align-items: flex-start;
}
}
/* Install terminal (glass terminal style) */
.install-terminal {
max-width: 640px;
margin: 0 auto var(--spacing-xl);
margin: 0 auto;
}
.install-terminal .glass-terminal {
@@ -2266,88 +2488,311 @@ code {
border-radius: 4px;
}
/* Prefix toggle (inline with download button) */
.install-terminal-cmd--download {
/* Install alternatives (collapsible) */
.install-alternatives {
max-width: 640px;
margin: var(--spacing-sm) auto 0;
}
.install-alternatives-toggle {
font-family: var(--font-mono);
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--color-ash);
cursor: pointer;
padding: var(--spacing-sm) 0;
list-style: none;
display: flex;
align-items: center;
gap: var(--spacing-sm);
flex-wrap: wrap;
gap: 6px;
transition: color var(--duration-fast) var(--ease-out);
}
.prefix-toggle {
display: inline-flex;
align-items: center;
gap: 8px;
cursor: pointer;
padding: 6px 12px;
background: rgba(0, 0, 0, 0.06);
border: 1px solid rgba(0, 0, 0, 0.08);
border-radius: 6px;
transition: border-color var(--duration-fast) var(--ease-out);
.install-alternatives-toggle::-webkit-details-marker {
display: none;
}
.prefix-toggle:hover {
border-color: rgba(0, 0, 0, 0.2);
}
.prefix-toggle input {
position: absolute;
opacity: 0;
.install-alternatives-toggle::before {
content: '';
display: inline-block;
width: 0;
height: 0;
}
.prefix-toggle-slider {
position: relative;
width: 32px;
height: 18px;
background: var(--color-mist);
border-radius: 18px;
transition: background var(--duration-fast) var(--ease-out);
flex-shrink: 0;
}
.prefix-toggle-slider::after {
content: '';
position: absolute;
top: 2px;
left: 2px;
width: 14px;
height: 14px;
background: var(--color-paper);
border-radius: 50%;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
border-left: 5px solid currentColor;
border-top: 4px solid transparent;
border-bottom: 4px solid transparent;
transition: transform var(--duration-fast) var(--ease-out);
}
.prefix-toggle input:checked + .prefix-toggle-slider {
.install-alternatives[open] .install-alternatives-toggle::before {
transform: rotate(90deg);
}
.install-alternatives-toggle:hover {
color: var(--color-charcoal);
}
.install-alternatives-content {
border: 1px solid var(--color-mist);
border-radius: 8px;
background: var(--color-paper);
overflow: hidden;
animation: altFadeIn 0.2s var(--ease-out);
}
@keyframes altFadeIn {
from { opacity: 0; transform: translateY(-4px); }
to { opacity: 1; transform: translateY(0); }
}
.install-alt-method {
padding: var(--spacing-md) var(--spacing-lg);
}
.install-alt-label {
display: block;
font-family: var(--font-mono);
font-size: 0.625rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.1em;
color: var(--color-ash);
margin-bottom: 6px;
}
.install-alt-method .install-terminal-cmd {
display: flex;
align-items: center;
gap: var(--spacing-sm);
}
.install-alt-method .terminal-prompt {
color: var(--color-accent);
font-family: var(--font-mono);
font-weight: bold;
user-select: none;
}
.install-alt-method code {
font-family: var(--font-mono);
font-size: 0.875rem;
color: var(--color-ink);
}
.install-alt-note {
display: block;
font-size: 0.75rem;
color: var(--color-ash);
margin-top: 4px;
}
.install-alt-note code {
font-family: var(--font-mono);
font-size: 0.6875rem;
background: var(--color-mist);
padding: 2px 5px;
border-radius: 3px;
color: var(--color-ink);
}
.install-alt-download {
display: flex;
align-items: center;
gap: var(--spacing-sm);
}
.install-alt-divider {
height: 1px;
background: var(--color-mist);
}
.install-prefix-options {
display: flex;
align-items: flex-start;
gap: var(--spacing-sm);
margin-top: var(--spacing-md);
}
.install-prefix-option {
flex: 1;
display: flex;
align-items: flex-start;
gap: 8px;
padding: 10px 12px;
border: 1.5px solid var(--color-mist);
border-radius: 8px;
cursor: pointer;
transition: all var(--duration-fast) var(--ease-out);
line-height: 1;
}
.install-prefix-option:hover {
border-color: var(--color-charcoal);
}
.install-prefix-option:has(input:checked) {
border-color: var(--color-accent);
}
.install-prefix-option input {
appearance: none;
-webkit-appearance: none;
width: 16px;
height: 16px;
border: 1.5px solid var(--color-mist);
border-radius: 50%;
flex-shrink: 0;
margin: 0;
transition: all var(--duration-fast) var(--ease-out);
position: relative;
}
.install-prefix-option input:checked {
border-color: var(--color-accent);
background: var(--color-accent);
}
.prefix-toggle input:checked + .prefix-toggle-slider::after {
transform: translateX(14px);
.install-prefix-option input:checked::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 6px;
height: 6px;
background: var(--color-paper);
border-radius: 50%;
}
.prefix-toggle input:focus-visible + .prefix-toggle-slider {
.install-prefix-option input:focus-visible {
outline: 2px solid var(--color-accent);
outline-offset: 2px;
}
.prefix-toggle-label {
font-size: 0.75rem;
color: var(--color-charcoal);
white-space: nowrap;
.install-prefix-option-content {
display: flex;
flex-direction: column;
gap: 3px;
padding-top: 1px;
}
.prefix-toggle-label code {
.install-prefix-option-label {
font-size: 0.8125rem;
font-weight: 600;
color: var(--color-ink);
line-height: 1;
}
.install-prefix-option-examples {
display: flex;
flex-wrap: wrap;
gap: 4px;
}
.install-prefix-option-examples code {
font-family: var(--font-mono);
font-size: 0.6875rem;
background: var(--color-accent-dim);
color: var(--color-accent);
padding: 1px 4px;
padding: 2px 6px;
border-radius: 3px;
}
.install-prefix-option-wrap {
flex: 1;
display: flex;
flex-direction: column;
gap: 4px;
align-self: flex-start;
}
.install-prefix-reason {
font-size: 0.6875rem;
color: var(--color-ash);
line-height: 1.4;
padding-left: 2px;
}
.install-prefix-option-label code {
font-family: var(--font-mono);
font-size: 0.6875rem;
background: var(--color-accent-dim);
color: var(--color-accent);
padding: 1px 5px;
border-radius: 3px;
}
.install-alt-method .install-prefix-options {
margin-top: var(--spacing-sm);
}
.install-alt-method .install-alt-download {
margin-top: var(--spacing-sm);
}
/* Hidden prefix checkbox for download logic */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
@media (max-width: 600px) {
.install-prefix-options {
flex-direction: column;
}
.install-alt-method {
padding: var(--spacing-sm) var(--spacing-md);
}
.install-alt-method code {
font-size: 0.75rem;
}
}
/* Tooltips */
.has-tooltip {
position: relative;
cursor: default;
}
.has-tooltip::after {
content: attr(data-tooltip);
position: absolute;
bottom: calc(100% + 8px);
left: 50%;
transform: translateX(-50%);
padding: 6px 10px;
background: var(--color-ink);
color: var(--color-paper);
font-size: 0.6875rem;
line-height: 1.4;
border-radius: 6px;
white-space: nowrap;
pointer-events: none;
opacity: 0;
transition: opacity 0.15s ease;
z-index: 100;
}
.has-tooltip:hover::after {
opacity: 1;
}
/* Hero logo icon wrapper */
.hero-logo-icon {
display: inline-flex;
align-items: center;
}
.download-tip {
font-size: 0.8125rem;
color: var(--color-ash);
File diff suppressed because one or more lines are too long
+49 -10
View File
@@ -93,8 +93,8 @@
}
.manual-cmd-desc {
font-size: 1rem;
line-height: 1.6;
font-size: 0.875rem;
line-height: 1.5;
color: var(--color-charcoal);
margin: 0;
}
@@ -123,10 +123,11 @@
color: var(--color-ink);
}
/* Right: The Terminal Stack (Sticky) */
/* Right: The Terminal Stack (Sticky, vertically centered) */
.glass-terminal-wrapper {
position: sticky;
top: var(--spacing-xl);
top: 50vh;
transform: translateY(-50%);
height: calc(100vh - var(--spacing-xl) * 2);
max-height: 800px;
min-height: 500px;
@@ -742,10 +743,9 @@
.changelog-entry {
padding: var(--spacing-md) 0;
border-bottom: 1px solid var(--color-mist);
}
.changelog-entry:first-child {
.changelog-entry + .changelog-entry {
border-top: 1px solid var(--color-mist);
}
@@ -788,6 +788,49 @@
color: var(--color-ink);
}
/* Changelog: collapsible older entries */
.changelog-older {
margin-top: var(--spacing-sm);
}
.changelog-older-toggle {
font-family: var(--font-body);
font-size: 0.875rem;
font-weight: 500;
color: var(--color-accent);
padding: var(--spacing-md) 0;
cursor: pointer;
list-style: none;
display: flex;
align-items: center;
gap: 6px;
transition: color 0.2s ease;
}
.changelog-older-toggle::-webkit-details-marker {
display: none;
}
.changelog-older-toggle::before {
content: '+';
font-size: 1.25rem;
font-weight: 300;
transition: transform 0.3s var(--ease-out);
}
.changelog-older[open] .changelog-older-toggle::before {
transform: rotate(45deg);
}
.changelog-older-toggle:hover {
color: var(--color-accent-hover);
}
.changelog-older-entries {
animation: faqFadeIn 0.3s var(--ease-out);
}
/* ============================================
FAQ SECTION
============================================ */
@@ -808,10 +851,6 @@
border-bottom: 1px solid var(--color-mist);
}
.faq-item:first-child {
border-top: 1px solid var(--color-mist);
}
.faq-question {
font-family: var(--font-display);
font-size: 1.125rem;
+224 -178
View File
@@ -1,11 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-TEXGHC7V34"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-TEXGHC7V34');
</script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Impeccable: The missing upgrade to Anthropic's frontend-design skill</title>
<meta name="description" content="1 skill, 20 commands, and curated anti-patterns for impeccable frontend design. Works with Cursor, Claude Code, Gemini CLI, and Codex CLI.">
<meta name="theme-color" content="#1a1a1a">
<meta name="theme-color" content="#fafafa">
<link rel="canonical" href="https://impeccable.style">
<!-- OpenGraph -->
@@ -38,11 +47,21 @@
<!-- Grain Overlay -->
<div class="grain-overlay" aria-hidden="true"></div>
<!-- Sticky Section Nav -->
<nav class="section-nav" id="section-nav" aria-label="Page sections">
<a href="#antidote" class="section-nav-item" data-section="antidote"><span class="section-nav-num">01</span><span class="section-nav-label">Anti-Patterns</span></a>
<a href="#solution" class="section-nav-item" data-section="solution"><span class="section-nav-num">02</span><span class="section-nav-label">Framework</span></a>
<a href="#commands-section" class="section-nav-item" data-section="commands-section"><span class="section-nav-num">03</span><span class="section-nav-label">Commands</span></a>
<a href="#downloads" class="section-nav-item" data-section="downloads"><span class="section-nav-num">04</span><span class="section-nav-label">Install</span></a>
<a href="#changelog" class="section-nav-item" data-section="changelog"><span class="section-nav-num">05</span><span class="section-nav-label">Changelog</span></a>
<a href="#faq" class="section-nav-item" data-section="faq"><span class="section-nav-num">06</span><span class="section-nav-label">FAQ</span></a>
</nav>
<!-- 1. HERO - Combined with Problem Section -->
<section id="hero" class="hero-combined">
<a href="https://github.com/pbakaus/impeccable" class="github-link" aria-label="View on GitHub">
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"/></svg>
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"/></svg>
<span class="github-stars">13.3k</span>
</a>
<div class="hero-combined-container">
<!-- Left: Title & Info -->
@@ -66,19 +85,19 @@
<div class="hero-logos-inline">
<span class="hero-logos-label">Works with</span>
<div class="hero-logos-row">
<img src="assets/cursor-logo.png" alt="Cursor" width="24" height="24" loading="lazy">
<img src="assets/claude-logo.png" alt="Claude Code" width="24" height="24" loading="lazy">
<img src="assets/gemini-logo.png" alt="Gemini CLI" width="20" height="20" loading="lazy">
<img src="assets/openai-logo.png" alt="Codex CLI" width="20" height="20" loading="lazy">
<img src="assets/github-logo.png" alt="VS Code Copilot" width="20" height="20" loading="lazy">
<img src="assets/antigravity-logo.png" alt="Antigravity" width="20" height="20" loading="lazy">
<img src="assets/kiro-logo.png" alt="Kiro" width="20" height="20" loading="lazy">
<img src="assets/opencode-logo.png" alt="OpenCode" width="20" height="20" loading="lazy">
<img src="assets/pi-logo.svg" alt="Pi" width="20" height="20" loading="lazy" style="background:#1a1a1a;border-radius:50%;padding:3px">
<span class="hero-logo-icon has-tooltip" data-tooltip="Cursor"><img src="assets/cursor-logo.png" alt="Cursor" width="24" height="24" loading="lazy"></span>
<span class="hero-logo-icon has-tooltip" data-tooltip="Claude Code"><img src="assets/claude-logo.png" alt="Claude Code" width="24" height="24" loading="lazy"></span>
<span class="hero-logo-icon has-tooltip" data-tooltip="Gemini CLI"><img src="assets/gemini-logo.png" alt="Gemini CLI" width="20" height="20" loading="lazy"></span>
<span class="hero-logo-icon has-tooltip" data-tooltip="Codex CLI"><img src="assets/openai-logo.png" alt="Codex CLI" width="20" height="20" loading="lazy"></span>
<span class="hero-logo-icon has-tooltip" data-tooltip="VS Code Copilot"><img src="assets/github-logo.png" alt="VS Code Copilot" width="20" height="20" loading="lazy"></span>
<span class="hero-logo-icon has-tooltip" data-tooltip="Antigravity"><img src="assets/antigravity-logo.png" alt="Antigravity" width="20" height="20" loading="lazy"></span>
<span class="hero-logo-icon has-tooltip" data-tooltip="Kiro"><img src="assets/kiro-logo.png" alt="Kiro" width="20" height="20" loading="lazy"></span>
<span class="hero-logo-icon has-tooltip" data-tooltip="OpenCode"><img src="assets/opencode-logo.png" alt="OpenCode" width="20" height="20" loading="lazy"></span>
<span class="hero-logo-icon has-tooltip" data-tooltip="Pi"><img src="assets/pi-logo.svg" alt="Pi" width="20" height="20" loading="lazy" style="background:#1a1a1a;border-radius:50%;padding:3px"></span>
</div>
</div>
</div>
<p class="hero-version-link"><a href="#changelog">v2.0.0 Anti-pattern detection engine</a></p>
<p class="hero-version-link"><a href="#changelog">v2.0.0 -- Anti-pattern detection engine</a></p>
</div>
<!-- Right: Before/After Demo -->
@@ -180,7 +199,7 @@
<div class="section-header" data-reveal>
<span class="section-number">03</span>
<h2 class="section-title">Commands in Action</h2>
<p class="section-subtitle">See the commands in action <a href="/cheatsheet" class="cheatsheet-link">View cheatsheet &rarr;</a></p>
<p class="section-subtitle">Interactive demos for every command <a href="/cheatsheet" class="cheatsheet-link">View cheatsheet &rarr;</a></p>
</div>
<div class="commands-gallery">
@@ -193,117 +212,119 @@
<section class="platforms-section" id="downloads">
<div class="section-header" data-reveal>
<span class="section-number">04</span>
<h2 class="section-title">Install</h2>
<p class="section-subtitle">One command. Every provider.</p>
<h2 class="section-title">Get Started</h2>
<p class="section-subtitle">Two steps to impeccable design.</p>
</div>
<!-- Primary install: glass terminal style -->
<div class="install-terminal" data-reveal>
<div class="glass-terminal">
<div class="terminal-header">
<span class="terminal-dot red"></span>
<span class="terminal-dot yellow"></span>
<span class="terminal-dot green"></span>
<span class="terminal-title">install</span>
<div class="install-steps">
<!-- Step 1: Install -->
<div class="install-step" data-reveal>
<div class="install-step-header">
<span class="install-step-number">1</span>
<div class="install-step-meta">
<h3 class="install-step-title">Install the skills</h3>
<p class="install-step-desc">One command. Every provider.</p>
</div>
</div>
<div class="terminal-body">
<div class="install-terminal-row">
<span class="install-terminal-label">All tools</span>
<div class="install-terminal-cmd">
<span class="terminal-prompt">$</span>
<code>npx skills add pbakaus/impeccable</code>
<button class="copy-btn" aria-label="Copy command" data-copy="npx skills add pbakaus/impeccable">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"></path>
</svg>
</button>
<!-- Primary: npx command -->
<div class="install-terminal">
<div class="glass-terminal">
<div class="terminal-header">
<span class="terminal-dot red"></span>
<span class="terminal-dot yellow"></span>
<span class="terminal-dot green"></span>
<span class="terminal-title">install</span>
</div>
<span class="install-terminal-note">Auto-detects your AI harness and installs to the right location</span>
</div>
<div class="install-terminal-divider"></div>
<div class="install-terminal-row">
<span class="install-terminal-label">Claude Code</span>
<div class="install-terminal-cmd">
<span class="terminal-prompt">$</span>
<code>/plugin marketplace add pbakaus/impeccable</code>
<button class="copy-btn" aria-label="Copy command" data-copy="/plugin marketplace add pbakaus/impeccable">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"></path>
</svg>
</button>
<div class="terminal-body">
<div class="install-terminal-row">
<div class="install-terminal-cmd">
<span class="terminal-prompt">$</span>
<code>npx skills add pbakaus/impeccable</code>
<button class="copy-btn" aria-label="Copy command" data-copy="npx skills add pbakaus/impeccable">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"></path>
</svg>
</button>
</div>
<span class="install-terminal-note">Works with Cursor, Claude Code, Gemini CLI, Codex CLI, and more. Auto-detects your AI harness.</span>
</div>
</div>
<span class="install-terminal-note">Then open <code>/plugin</code> to install from Discover tab</span>
</div>
<div class="install-terminal-divider"></div>
<div class="install-terminal-row">
<span class="install-terminal-label">Manual</span>
<div class="install-terminal-cmd install-terminal-cmd--download">
<button class="btn btn-secondary btn-small" data-bundle="universal" aria-label="Download universal ZIP">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3"/>
</svg>
<span>Download universal ZIP</span>
</button>
<label class="prefix-toggle">
<input type="checkbox" id="prefix-toggle" />
<span class="prefix-toggle-slider"></span>
<span class="prefix-toggle-label">
Prefix with <code>/i-</code>
</span>
</label>
</div>
<!-- Alternatives (collapsible) -->
<details class="install-alternatives">
<summary class="install-alternatives-toggle">Other install methods</summary>
<div class="install-alternatives-content">
<div class="install-alt-method">
<span class="install-alt-label">Claude Code plugin</span>
<div class="install-terminal-cmd">
<span class="terminal-prompt">$</span>
<code>/plugin marketplace add pbakaus/impeccable</code>
<button class="copy-btn" aria-label="Copy command" data-copy="/plugin marketplace add pbakaus/impeccable">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"></path>
</svg>
</button>
</div>
<span class="install-alt-note">Then open <code>/plugin</code> to install from Discover tab</span>
</div>
<div class="install-alt-divider"></div>
<div class="install-alt-method">
<span class="install-alt-label">Manual download</span>
<span class="install-alt-note">Contains all provider directories. Extract to your project root.</span>
<div class="install-prefix-options">
<label class="install-prefix-option" data-prefix="default">
<input type="radio" name="prefix-choice" value="default" checked />
<div class="install-prefix-option-content">
<span class="install-prefix-option-label">Default names</span>
<span class="install-prefix-option-examples"><code>/polish</code> <code>/audit</code> <code>/typeset</code></span>
</div>
</label>
<div class="install-prefix-option-wrap">
<label class="install-prefix-option" data-prefix="prefixed">
<input type="radio" name="prefix-choice" value="prefixed" />
<div class="install-prefix-option-content">
<span class="install-prefix-option-label">Prefixed with <code>/i-</code></span>
<span class="install-prefix-option-examples"><code>/i-polish</code> <code>/i-audit</code> <code>/i-typeset</code></span>
</div>
</label>
<span class="install-prefix-reason">Avoids conflicts with built-in commands or other skill packs</span>
</div>
</div>
<div class="install-alt-download">
<button class="btn btn-primary" data-bundle="universal" aria-label="Download universal zip" id="download-zip-btn">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3"/>
</svg>
<span>Download universal zip</span>
</button>
</div>
<input type="checkbox" id="prefix-toggle" class="sr-only" />
</div>
<span class="install-terminal-note">Contains all provider directories — extract to project root</span>
</div>
</details>
</div>
<!-- Step 2: Subscribe -->
<div class="install-step" data-reveal>
<div class="install-step-header">
<span class="install-step-number">2</span>
<div class="install-step-meta">
<h3 class="install-step-title">Stay up to date</h3>
<p class="install-step-desc">New skills, pattern updates, and design tips.</p>
</div>
</div>
<div class="install-subscribe">
<iframe src="https://impeccablestyle.substack.com/embed" width="100%" height="150" style="border: 1px solid #e8e4df; border-radius: 8px; background: white;" frameborder="0" scrolling="no"></iframe>
</div>
</div>
</div>
<!-- Works with badges -->
<div class="install-providers" data-reveal>
<span class="install-providers-label">Works with</span>
<div class="install-providers-row">
<div class="install-provider-badge">
<img src="assets/cursor-logo.png" alt="Cursor" width="20" height="20" loading="lazy">
<span>Cursor</span>
</div>
<div class="install-provider-badge">
<img src="assets/claude-logo.png" alt="Claude Code" width="20" height="20" loading="lazy">
<span>Claude Code</span>
</div>
<div class="install-provider-badge">
<img src="assets/gemini-logo.png" alt="Gemini CLI" width="20" height="20" loading="lazy">
<span>Gemini CLI</span>
</div>
<div class="install-provider-badge">
<img src="assets/openai-logo.png" alt="Codex CLI" width="20" height="20" loading="lazy">
<span>Codex CLI</span>
</div>
<div class="install-provider-badge">
<img src="assets/github-logo.png" alt="VS Code Copilot" width="20" height="20" loading="lazy">
<span>Copilot</span>
</div>
<div class="install-provider-badge">
<img src="assets/antigravity-logo.png" alt="Antigravity" width="20" height="20" loading="lazy">
<span>Antigravity</span>
</div>
<div class="install-provider-badge">
<img src="assets/kiro-logo.png" alt="Kiro" width="20" height="20" loading="lazy">
<span>Kiro</span>
</div>
<div class="install-provider-badge">
<img src="assets/opencode-logo.png" alt="OpenCode" width="20" height="20" loading="lazy">
<span>OpenCode</span>
</div>
<div class="install-provider-badge">
<img src="assets/pi-logo.svg" alt="Pi" width="20" height="20" loading="lazy" style="background:#1a1a1a;border-radius:50%;padding:3px">
<span>Pi</span>
</div>
</div>
</div>
<p class="download-tip" data-reveal>Once installed, keep the <a href="/cheatsheet">command cheatsheet</a> handy for quick reference. To update, run <code>npx skills update</code>.</p>
<p class="download-tip" data-reveal>Keep the <a href="/cheatsheet">command cheatsheet</a> handy for quick reference. To update skills, run <code>npx skills update</code>.</p>
</section>
<!-- 5. CHANGELOG -->
@@ -323,85 +344,105 @@
<li>Anti-pattern detection engine: 25 rules across typography, color, layout, visual details, motion, and design quality</li>
<li>CLI tool: <code>npx impeccable detect [target]</code> for standalone scanning outside AI harnesses</li>
<li>Browser visualizer: live overlays highlighting detected anti-patterns directly on the page</li>
<li><code>/critique</code> overhaul: sub-agent architecture, provider-aware script paths, console-based overlay reading</li>
<li><code>/critique</code> overhaul: sub-agent architecture with scoring, personas, and cognitive load assessment</li>
<li>Gallery of Shame: <a href="/gallery">real examples</a> with live detection overlays</li>
<li>Shared transformer build system, prefix-aware <code>{{scripts_path}}</code> placeholder</li>
</ul>
</div>
<div class="changelog-entry">
<div class="changelog-version-header">
<span class="changelog-version">v1.5.1</span>
<span class="changelog-date">March 17, 2026</span>
<span class="changelog-version">v1.6.0</span>
<span class="changelog-date">March 24, 2026</span>
</div>
<ul class="changelog-items">
<li><code>/typeset</code> now recommends fixed type scales for app UIs, reserving fluid typography for marketing/content pages</li>
<li>New provider: <strong>Trae</strong> (China + International)</li>
<li><code>/critique</code> now scores against Nielsen's 10 heuristics, tests with persona archetypes, and assesses cognitive load</li>
<li><code>/audit</code> now scores 5 dimensions with P0-P3 severity ratings and structured action plans</li>
<li>Improved skill descriptions for better agent auto-discovery</li>
<li>Fixed invalid YAML frontmatter that broke GitHub preview and Codex loading (<a href="https://github.com/pbakaus/impeccable/issues/67">#67</a>)</li>
<li>Codex CLI now uses correct <code>$</code> prefix for command references</li>
</ul>
</div>
<div class="changelog-entry">
<div class="changelog-version-header">
<span class="changelog-version">v1.5.0</span>
<span class="changelog-date">March 16, 2026</span>
</div>
<ul class="changelog-items">
<li>3 new skills: <code>/typeset</code> (fix typography), <code>/arrange</code> (fix layout & spacing), <code>/overdrive</code> (technically extraordinary effects, beta)</li>
<li>Skills now auto-gather design context via <code>.impeccable.md</code> — run <code>/teach-impeccable</code> once, all skills benefit</li>
<li>Deep linking to commands (<code>#cmd-overdrive</code>, etc.)</li>
</ul>
</div>
<details class="changelog-older">
<summary class="changelog-older-toggle">View older releases</summary>
<div class="changelog-older-entries">
<div class="changelog-entry">
<div class="changelog-version-header">
<span class="changelog-version">v1.5.1</span>
<span class="changelog-date">March 17, 2026</span>
</div>
<ul class="changelog-items">
<li><code>/typeset</code> now recommends fixed type scales for app UIs, reserving fluid typography for marketing/content pages</li>
</ul>
</div>
<div class="changelog-entry">
<div class="changelog-version-header">
<span class="changelog-version">v1.3.0</span>
<span class="changelog-date">March 12, 2026</span>
</div>
<ul class="changelog-items">
<li>Added OpenCode provider support</li>
<li>Added Pi provider support</li>
<li>Recategorized <code>/onboard</code> as an enhancement command</li>
</ul>
</div>
<div class="changelog-entry">
<div class="changelog-version-header">
<span class="changelog-version">v1.5.0</span>
<span class="changelog-date">March 16, 2026</span>
</div>
<ul class="changelog-items">
<li>3 new skills: <code>/typeset</code> (fix typography), <code>/arrange</code> (fix layout & spacing), <code>/overdrive</code> (technically extraordinary effects, beta)</li>
<li>Skills now auto-gather design context via <code>.impeccable.md</code> — run <code>/teach-impeccable</code> once, all skills benefit</li>
<li>Deep linking to commands (<code>#cmd-overdrive</code>, etc.)</li>
</ul>
</div>
<div class="changelog-entry">
<div class="changelog-version-header">
<span class="changelog-version">v1.2.0</span>
<span class="changelog-date">March 5, 2026</span>
</div>
<ul class="changelog-items">
<li>Added Kiro support (<code>.kiro/skills/</code>)</li>
<li>Restored prefix toggle — download <code>i-</code> prefixed bundles to avoid naming conflicts</li>
<li>Audit and critique skills only suggest real, installed commands</li>
</ul>
</div>
<div class="changelog-entry">
<div class="changelog-version-header">
<span class="changelog-version">v1.3.0</span>
<span class="changelog-date">March 12, 2026</span>
</div>
<ul class="changelog-items">
<li>Added OpenCode provider support</li>
<li>Added Pi provider support</li>
<li>Recategorized <code>/onboard</code> as an enhancement command</li>
</ul>
</div>
<div class="changelog-entry">
<div class="changelog-version-header">
<span class="changelog-version">v1.1.0</span>
<span class="changelog-date">March 4, 2026</span>
</div>
<ul class="changelog-items">
<li>Unified skills architecture — commands are now skills with <code>user-invokable: true</code></li>
<li>Added VS Code Copilot and Google Antigravity support (<code>.agents/skills/</code>)</li>
<li>New install flow: <code>npx skills add</code> as primary, universal ZIP as fallback</li>
<li>Added universal ZIP containing all 5 provider directories</li>
<li>Renamed <code>/simplify</code> to <code>/distill</code> to avoid Claude Code conflict</li>
</ul>
</div>
<div class="changelog-entry">
<div class="changelog-version-header">
<span class="changelog-version">v1.2.0</span>
<span class="changelog-date">March 5, 2026</span>
</div>
<ul class="changelog-items">
<li>Added Kiro support (<code>.kiro/skills/</code>)</li>
<li>Restored prefix toggle — download <code>i-</code> prefixed bundles to avoid naming conflicts</li>
<li>Audit and critique skills only suggest real, installed commands</li>
</ul>
</div>
<div class="changelog-entry">
<div class="changelog-version-header">
<span class="changelog-version">v1.0.0</span>
<span class="changelog-date">February 28, 2026</span>
<div class="changelog-entry">
<div class="changelog-version-header">
<span class="changelog-version">v1.1.0</span>
<span class="changelog-date">March 4, 2026</span>
</div>
<ul class="changelog-items">
<li>Unified skills architecture — commands are now skills with <code>user-invocable: true</code></li>
<li>Added VS Code Copilot and Google Antigravity support (<code>.agents/skills/</code>)</li>
<li>New install flow: <code>npx skills add</code> as primary, universal ZIP as fallback</li>
<li>Added universal ZIP containing all 5 provider directories</li>
<li>Renamed <code>/simplify</code> to <code>/distill</code> to avoid Claude Code conflict</li>
</ul>
</div>
<div class="changelog-entry">
<div class="changelog-version-header">
<span class="changelog-version">v1.0.0</span>
<span class="changelog-date">February 28, 2026</span>
</div>
<ul class="changelog-items">
<li>Initial release with enhanced frontend-design skill</li>
<li>17 design commands: /polish, /audit, /distill, /bolder, and more</li>
<li>Support for Cursor, Claude Code, Gemini CLI, and Codex CLI</li>
<li>Interactive command cheatsheet</li>
</ul>
</div>
</div>
<ul class="changelog-items">
<li>Initial release with enhanced frontend-design skill</li>
<li>17 design commands: /polish, /audit, /distill, /bolder, and more</li>
<li>Support for Cursor, Claude Code, Gemini CLI, and Codex CLI</li>
<li>Interactive command cheatsheet</li>
</ul>
</div>
</details>
</div>
</section>
<!-- 6. FAQ -->
@@ -491,15 +532,13 @@
<div class="footer-content">
<div class="footer-brand">
<span class="footer-logo">Impeccable</span>
<p class="footer-tagline">impeccable.style</p>
</div>
<div class="footer-links">
<a href="#antidote">Anti-Patterns</a>
<a href="/gallery">Gallery of Shame</a>
<a href="#commands-section">Commands</a>
<a href="/cheatsheet">Cheatsheet</a>
<a href="#downloads">Downloads</a>
<a href="#changelog">Changelog</a>
<a href="/privacy">Privacy</a>
<a href="https://github.com/pbakaus/impeccable">GitHub</a>
</div>
</div>
@@ -518,7 +557,14 @@
</a>
<span class="footer-author-divider"></span>
<a href="https://www.paulbakaus.com" class="footer-newsletter" target="_blank" rel="noopener">
Renaissance Geek
Blog
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<path d="M5 12h14M12 5l7 7-7 7"/>
</svg>
</a>
<span class="footer-author-divider"></span>
<a href="https://impeccablestyle.substack.com" class="footer-newsletter" target="_blank" rel="noopener">
Newsletter
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<path d="M5 12h14M12 5l7 7-7 7"/>
</svg>
+66 -143
View File
@@ -1,11 +1,11 @@
/**
* Periodic Table of Commands
* Clean grid visualization showing all commands organized by category
* Hover tooltips show description and relationships inline.
*/
import { commandCategories, commandRelationships, betaCommands } from '../data.js';
// Colors now reference CSS custom properties for dark mode support
const categoryColors = {
diagnostic: { bg: 'var(--cat-diagnostic-bg)', border: 'var(--cat-diagnostic-border)', text: 'var(--cat-diagnostic-text)' },
quality: { bg: 'var(--cat-quality-bg)', border: 'var(--cat-quality-border)', text: 'var(--cat-quality-text)' },
@@ -24,7 +24,6 @@ const categoryLabels = {
system: 'System'
};
// Short symbols for each command (like element symbols)
const commandSymbols = {
'teach-impeccable': 'Ti',
audit: 'Au',
@@ -48,34 +47,18 @@ const commandSymbols = {
overdrive: 'Od'
};
// Atomic numbers (just for visual interest)
const commandNumbers = {
'teach-impeccable': 0,
audit: 1,
critique: 2,
normalize: 3,
polish: 4,
optimize: 5,
harden: 6,
clarify: 7,
distill: 8,
adapt: 9,
extract: 10,
animate: 11,
colorize: 12,
delight: 13,
bolder: 14,
quieter: 15,
onboard: 16,
typeset: 17,
arrange: 18,
overdrive: 19
audit: 1, critique: 2, normalize: 3, polish: 4, optimize: 5,
harden: 6, clarify: 7, distill: 8, adapt: 9, extract: 10,
animate: 11, colorize: 12, delight: 13, bolder: 14, quieter: 15,
onboard: 16, typeset: 17, arrange: 18, overdrive: 19
};
export class PeriodicTable {
constructor(container) {
this.container = container;
this.infoPanel = null;
this.activeTooltip = null;
this.activeElement = null;
this.init();
}
@@ -89,24 +72,21 @@ export class PeriodicTable {
padding: 20px;
height: 100%;
box-sizing: border-box;
position: relative;
`;
this.renderTable();
this.renderInfoPanel();
}
renderTable() {
// Group commands by category
const groups = {};
Object.entries(commandCategories).forEach(([cmd, cat]) => {
if (!groups[cat]) groups[cat] = [];
groups[cat].push(cmd);
});
// Category order
const categoryOrder = ['diagnostic', 'quality', 'adaptation', 'enhancement', 'intensity', 'system'];
// Create grid container
const grid = document.createElement('div');
grid.style.cssText = `
display: grid;
@@ -118,7 +98,6 @@ export class PeriodicTable {
categoryOrder.forEach(cat => {
const commands = groups[cat];
if (!commands) return;
const group = this.createCategoryGroup(cat, commands);
grid.appendChild(group);
});
@@ -126,50 +105,10 @@ export class PeriodicTable {
this.container.appendChild(grid);
}
renderInfoPanel() {
this.infoPanel = document.createElement('div');
this.infoPanel.style.cssText = `
background: var(--color-cream);
border: 1px solid var(--color-mist);
border-radius: 8px;
padding: 16px 20px;
height: 96px;
display: flex;
align-items: center;
gap: 20px;
flex-shrink: 0;
overflow: hidden;
`;
showTooltip(el, cmd) {
this.hideTooltip();
// Default state
this.showDefaultInfo();
this.container.appendChild(this.infoPanel);
}
showDefaultInfo() {
// Use "Tap" on touch devices, "Hover over" on desktop
const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
const action = isTouchDevice ? 'Tap' : 'Hover over';
this.infoPanel.innerHTML = `
<div style="
font-family: var(--font-body);
font-size: 14px;
color: var(--color-ash);
font-style: italic;
">
${action} a command to see details
</div>
`;
}
showCommandInfo(cmd) {
const category = commandCategories[cmd];
const colors = categoryColors[category];
const rel = commandRelationships[cmd] || {};
// Normalize to arrays (pairs can be string or array)
const toArray = (val) => {
if (!val) return [];
if (Array.isArray(val)) return val;
@@ -180,69 +119,67 @@ export class PeriodicTable {
const leadsTo = toArray(rel.leadsTo);
const combinesWith = toArray(rel.combinesWith);
// Build related info
let relatedHtml = '';
// Build relationships line
let relParts = [];
if (pairs.length > 0) relParts.push(`pairs with ${pairs.map(p => '/' + p).join(', ')}`);
if (combinesWith.length > 0) relParts.push(`+ ${combinesWith.map(p => '/' + p).join(', ')}`);
if (leadsTo.length > 0) relParts.push(`then ${leadsTo.map(p => '/' + p).join(', ')}`);
if (pairs.length > 0) {
const list = pairs.map(p => `<span style="font-family: var(--font-mono); color: var(--color-ink)">/${p}</span>`).join(', ');
relatedHtml += `<span style="color: var(--color-ash)">Pairs with:</span> ${list}`;
}
if (combinesWith.length > 0) {
const list = combinesWith.map(p => `<span style="font-family: var(--font-mono); color: var(--color-ink)">/${p}</span>`).join(', ');
if (relatedHtml) relatedHtml += '<span style="color: var(--color-mist); margin: 0 8px;">•</span>';
relatedHtml += `<span style="color: var(--color-ash)">Combines with:</span> ${list}`;
}
if (leadsTo.length > 0) {
const list = leadsTo.map(p => `<span style="font-family: var(--font-mono); color: var(--color-ink)">/${p}</span>`).join(', ');
if (relatedHtml) relatedHtml += '<span style="color: var(--color-mist); margin: 0 8px;">•</span>';
relatedHtml += `<span style="color: var(--color-ash)">Then:</span> ${list}`;
}
// Strip category prefix from flow for cleaner display
const flow = (rel.flow || '').replace(/^[^:]+:\s*/, '');
this.infoPanel.innerHTML = `
<div style="flex: 1; min-width: 0;">
<div style="
display: flex;
align-items: baseline;
gap: 10px;
margin-bottom: 4px;
">
<span style="
font-family: var(--font-mono);
font-size: 15px;
font-weight: 500;
color: var(--color-ink);
">/${cmd}</span>
<span style="
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.05em;
color: ${colors.text};
font-weight: 500;
">${categoryLabels[category]}</span>
</div>
<div style="
font-family: var(--font-body);
font-size: 13px;
color: var(--color-charcoal);
line-height: 1.4;
margin-bottom: ${relatedHtml ? '6px' : '0'};
">${rel.flow || 'Design command'}</div>
${relatedHtml ? `<div style="font-size: 12px; line-height: 1.4;">${relatedHtml}</div>` : ''}
</div>
const tooltip = document.createElement('div');
tooltip.className = 'ptable-tooltip';
tooltip.style.cssText = `
position: absolute;
z-index: 20;
background: var(--color-paper);
border: 1px solid var(--color-mist);
border-radius: 6px;
padding: 10px 14px;
box-shadow: 0 8px 24px -4px rgba(0,0,0,0.12);
pointer-events: none;
max-width: 280px;
opacity: 0;
transition: opacity 0.15s ease;
`;
tooltip.innerHTML = `
<div style="font-family: var(--font-body); font-size: 13px; color: var(--color-charcoal); line-height: 1.4; margin-bottom: ${relParts.length ? '6px' : '0'};">${flow}</div>
${relParts.length ? `<div style="font-family: var(--font-mono); font-size: 11px; color: var(--color-ash); line-height: 1.4;">${relParts.join(' · ')}</div>` : ''}
`;
this.container.appendChild(tooltip);
// Position relative to element
const elRect = el.getBoundingClientRect();
const containerRect = this.container.getBoundingClientRect();
const left = elRect.left - containerRect.left;
const top = elRect.bottom - containerRect.top + 6;
tooltip.style.left = `${Math.min(left, containerRect.width - 290)}px`;
tooltip.style.top = `${top}px`;
// Fade in
requestAnimationFrame(() => { tooltip.style.opacity = '1'; });
this.activeTooltip = tooltip;
}
hideTooltip() {
if (this.activeTooltip) {
this.activeTooltip.remove();
this.activeTooltip = null;
}
}
createCategoryGroup(category, commands) {
const colors = categoryColors[category];
const group = document.createElement('div');
group.style.cssText = `
display: flex;
flex-direction: column;
gap: 6px;
`;
group.style.cssText = `display: flex; flex-direction: column; gap: 6px;`;
// Category label
const label = document.createElement('div');
label.style.cssText = `
font-family: var(--font-body);
@@ -256,13 +193,8 @@ export class PeriodicTable {
label.textContent = categoryLabels[category];
group.appendChild(label);
// Elements row
const row = document.createElement('div');
row.style.cssText = `
display: flex;
flex-wrap: wrap;
gap: 6px;
`;
row.style.cssText = `display: flex; flex-wrap: wrap; gap: 6px;`;
commands.forEach(cmd => {
const element = this.createElement(cmd, category);
@@ -352,16 +284,12 @@ export class PeriodicTable {
el.appendChild(badge);
}
// Shared handler for activation (hover or focus)
// Hover/focus: show tooltip
const activate = () => {
// Visual feedback
el.style.transform = 'translateY(-2px)';
el.style.boxShadow = `0 4px 12px ${colors.border}40`;
this.showTooltip(el, cmd);
// Update info panel
this.showCommandInfo(cmd);
// Track active element
if (this.activeElement && this.activeElement !== el) {
this.activeElement.style.transform = 'translateY(0)';
this.activeElement.style.boxShadow = 'none';
@@ -369,29 +297,24 @@ export class PeriodicTable {
this.activeElement = el;
};
// Shared handler for deactivation
const deactivate = () => {
el.style.transform = 'translateY(0)';
el.style.boxShadow = 'none';
this.hideTooltip();
};
// Mouse events (desktop)
el.addEventListener('mouseenter', activate);
el.addEventListener('mouseleave', deactivate);
// Keyboard focus events
el.addEventListener('focus', activate);
el.addEventListener('blur', deactivate);
// Touch events - activate on tap, stay active until another tap
el.addEventListener('touchstart', (e) => {
e.preventDefault(); // Prevent double-firing with click
e.preventDefault();
activate();
}, { passive: false });
// Click/Enter to scroll to command
el.addEventListener('click', () => {
activate(); // Also activate on click for touch devices
activate();
const target = document.getElementById(`cmd-${cmd}`);
if (target) {
target.scrollIntoView({ behavior: 'smooth', block: 'center' });
+12 -1
View File
@@ -127,6 +127,16 @@ function renderDesktopLayout(container, commands) {
}
}
function truncateDescription(text, maxLen = 120) {
if (text.length <= maxLen) return text;
// Cut at last sentence boundary within limit, or last word boundary
const truncated = text.slice(0, maxLen);
const lastPeriod = truncated.lastIndexOf('.');
if (lastPeriod > maxLen * 0.5) return truncated.slice(0, lastPeriod + 1);
const lastSpace = truncated.lastIndexOf(' ');
return truncated.slice(0, lastSpace) + '...';
}
function renderManualEntry(cmd) {
const relationship = commandRelationships[cmd.id];
let relationshipHTML = '';
@@ -142,11 +152,12 @@ function renderManualEntry(cmd) {
}
const isBeta = betaCommands.includes(cmd.id);
const shortDesc = truncateDescription(cmd.description);
return `
<div class="manual-entry" data-id="${cmd.id}" id="cmd-${cmd.id}">
<h3 class="manual-cmd-name">/${cmd.id}${isBeta ? ' <span class="beta-badge">BETA</span>' : ''}</h3>
<p class="manual-cmd-desc">${cmd.description}</p>
<p class="manual-cmd-desc">${shortDesc}</p>
${relationshipHTML}
</div>
`;
+1 -1
View File
@@ -5,7 +5,7 @@ export function initLensEffect() {
if (!container) return;
initSplitCompare(container, {
defaultPosition: 70
defaultPosition: 50
});
}
+66
View File
@@ -0,0 +1,66 @@
/**
* Sticky Section Nav
* Shows/hides based on scroll position and highlights current section.
*/
export function initSectionNav() {
const nav = document.getElementById('section-nav');
if (!nav) return;
const items = nav.querySelectorAll('.section-nav-item');
const sectionIds = Array.from(items).map(item => item.dataset.section);
// Show/hide nav based on scroll position
const hero = document.getElementById('hero');
const footer = document.querySelector('.site-footer');
if (!hero) return;
let ticking = false;
function updateNav() {
const scrollY = window.scrollY;
const heroBottom = hero.offsetTop + hero.offsetHeight - 100;
const footerTop = footer ? footer.offsetTop : Infinity;
const viewportBottom = scrollY + window.innerHeight;
// Show nav after hero, hide when footer is visible
if (scrollY > heroBottom && viewportBottom < footerTop + 60) {
nav.classList.add('is-visible');
} else {
nav.classList.remove('is-visible');
}
// Find current section
let currentSection = null;
const viewportMiddle = scrollY + window.innerHeight * 0.4;
for (let i = sectionIds.length - 1; i >= 0; i--) {
const section = document.getElementById(sectionIds[i]);
if (section && section.offsetTop <= viewportMiddle) {
currentSection = sectionIds[i];
break;
}
}
// Update active state
items.forEach(item => {
if (item.dataset.section === currentSection) {
item.classList.add('is-active');
} else {
item.classList.remove('is-active');
}
});
ticking = false;
}
window.addEventListener('scroll', () => {
if (!ticking) {
requestAnimationFrame(updateNav);
ticking = true;
}
}, { passive: true });
// Initial check
updateNav();
}
+41
View File
@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Privacy Policy - Impeccable</title>
<meta name="robots" content="noindex">
<link rel="icon" type="image/svg+xml" href="./favicon.svg">
<style>
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; max-width: 680px; margin: 0 auto; padding: 2rem 1.5rem; line-height: 1.6; color: #1a1a1a; }
h1 { font-size: 1.8rem; margin-bottom: 0.5rem; }
h2 { font-size: 1.2rem; margin-top: 2rem; }
.updated { color: #666; font-size: 0.9rem; margin-bottom: 2rem; }
a { color: #1a1a1a; }
.back { display: inline-block; margin-bottom: 2rem; font-size: 0.9rem; }
</style>
</head>
<body>
<a href="/" class="back">&larr; Back to impeccable.style</a>
<h1>Privacy Policy</h1>
<p class="updated">Last updated: March 24, 2026</p>
<h2>What Impeccable is</h2>
<p>Impeccable is an open-source collection of agent skills (text files) that run locally in your AI coding tool. The skills themselves collect no data, make no network requests, and have no analytics.</p>
<h2>Website analytics</h2>
<p>The Impeccable website (<a href="https://impeccable.style">impeccable.style</a>) uses Google Analytics to understand traffic patterns (page views, referrers, country). No personal information is collected beyond what Google Analytics provides by default. No cookies are used for advertising.</p>
<h2>Downloads</h2>
<p>When you download a skill bundle from the website, we log the download event (which bundle, timestamp) for usage statistics. No personal information is attached to these logs.</p>
<h2>Claude Code Plugin</h2>
<p>When installed as a Claude Code plugin, Impeccable runs entirely within your local Claude Code session. No data is sent to Impeccable's servers. Anthropic's own privacy policy governs the Claude Code application itself.</p>
<h2>GitHub</h2>
<p>The source code is hosted on GitHub. Interactions with the repository (issues, pull requests, stars) are governed by <a href="https://docs.github.com/en/site-policy/privacy-policies/github-general-privacy-statement">GitHub's privacy policy</a>.</p>
<h2>Contact</h2>
<p>Questions about this policy? Open an issue on <a href="https://github.com/pbakaus/impeccable">GitHub</a> or reach out to <a href="https://x.com/pbakaus">@pbakaus</a>.</p>
</body>
</html>