mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-21 18:47:02 +03:00
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:
+46
-8
@@ -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("&", "&")
|
||||
.replaceAll("<", "<")
|
||||
.replaceAll(">", ">")
|
||||
.replaceAll('"', """)
|
||||
.replaceAll("'", "'");
|
||||
}
|
||||
|
||||
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 |
@@ -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 |
@@ -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
@@ -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
@@ -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
@@ -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 →</a></p>
|
||||
<p class="section-subtitle">Interactive demos for every command <a href="/cheatsheet" class="cheatsheet-link">View cheatsheet →</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>
|
||||
|
||||
@@ -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' });
|
||||
|
||||
@@ -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>
|
||||
`;
|
||||
|
||||
@@ -5,7 +5,7 @@ export function initLensEffect() {
|
||||
if (!container) return;
|
||||
|
||||
initSplitCompare(container, {
|
||||
defaultPosition: 70
|
||||
defaultPosition: 50
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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">← 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>
|
||||
Reference in New Issue
Block a user