mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-17 16:46:31 +03:00
The public repo keeps the OSS promise surface: skill, CLI, extension, tests, and the provider build. The site, labs, concept/composition catalogs, image pipeline, Cloudflare functions, and authoring guide move to pbakaus/impeccable-site. concept-seed tests run against a synthetic fixture catalog; the plugin icon and skill categories moved in-repo; build validation narrows to README prose and non-site counts; release notes read from a sibling impeccable-site checkout. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
335 lines
20 KiB
HTML
335 lines
20 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Getting started | Tutorials | Impeccable</title>
|
|
<meta name="description" content="Install Impeccable, run /impeccable teach once to establish project context, and run /impeccable polish on something that already exists. The fastest path to seeing what Impeccable changes about AI-generated design.">
|
|
<meta name="theme-color" content="#fafafa">
|
|
<link rel="canonical" href="https://impeccable.style/tutorials/getting-started">
|
|
<link rel="icon" type="image/svg+xml" href="../favicon.svg">
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,600;1,400&family=Instrument+Sans:wght@400;500;600;700&family=Space+Grotesk:wght@400;500;600&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="../css/sub-pages.css">
|
|
|
|
</head>
|
|
<body class="sub-page skills-layout-page tutorials-page">
|
|
<a href="#main" class="skip-link">Skip to content</a>
|
|
<!-- site-header v1 -->
|
|
<header class="site-header" data-site-header>
|
|
<a href="/" class="site-header-brand" aria-label="Impeccable home">
|
|
<svg class="site-header-brand-logo" viewBox="0 0 32 32" aria-hidden="true"><rect width="32" height="32" rx="6" fill="#1a1a1a"/><text x="16" y="24" font-family="system-ui, -apple-system, sans-serif" font-size="22" font-weight="500" fill="#f5f3ef" text-anchor="middle">/</text></svg>
|
|
<span class="site-header-brand-name">Impeccable</span>
|
|
</a>
|
|
<div class="site-header-right">
|
|
<nav class="site-header-nav" aria-label="Primary">
|
|
<a href="/" data-nav="home">Home</a>
|
|
<a href="/designing" data-nav="designing">Designing</a>
|
|
<a href="/docs" data-nav="docs" aria-current="page">Docs</a>
|
|
<a href="/slop" data-nav="slop">Slop</a>
|
|
<a href="/live-mode" data-nav="live">Live</a>
|
|
</nav>
|
|
<a href="https://github.com/pbakaus/impeccable" class="site-header-github" target="_blank" rel="noopener" aria-label="Impeccable on GitHub, 21k stars">
|
|
<svg 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="site-header-github-label">21k</span>
|
|
<svg class="site-header-github-star" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2l2.76 6.36L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l7.24-.91L12 2z"/></svg>
|
|
</a>
|
|
</div>
|
|
</header>
|
|
<main id="main">
|
|
|
|
<div class="skills-layout">
|
|
|
|
<aside class="skills-sidebar" aria-label="Documentation">
|
|
<button class="skills-sidebar-toggle" type="button" aria-expanded="false" aria-controls="skills-sidebar-inner">
|
|
<span class="skills-sidebar-toggle-label">Getting started</span>
|
|
<svg class="skills-sidebar-toggle-chevron" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg>
|
|
</button>
|
|
<div class="skills-sidebar-inner" id="skills-sidebar-inner">
|
|
<p class="skills-sidebar-label">Docs</p>
|
|
|
|
<div class="skills-sidebar-group" data-category="tutorials">
|
|
<p class="skills-sidebar-group-title">Tutorials</p>
|
|
<ul class="skills-sidebar-list">
|
|
<li><a href="/tutorials/getting-started" aria-current="page">Getting started</a></li>
|
|
<li><a href="/tutorials/iterate-live">Iterate on UI with Live Mode</a></li>
|
|
<li><a href="/tutorials/brand-vs-product">Brand vs product, pick a register</a></li>
|
|
<li><a href="/tutorials/critique-with-overlay">Critique with the visual overlay</a></li>
|
|
</ul>
|
|
</div>
|
|
<hr class="skills-sidebar-divider">
|
|
|
|
<div class="skills-sidebar-group" data-category="create">
|
|
<p class="skills-sidebar-group-title">Create</p>
|
|
<ul class="skills-sidebar-list">
|
|
<li><a href="/docs/impeccable">/impeccable</a></li>
|
|
<li><a href="/docs/craft">craft</a></li>
|
|
<li><a href="/docs/shape">shape</a></li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="skills-sidebar-group" data-category="evaluate">
|
|
<p class="skills-sidebar-group-title">Evaluate</p>
|
|
<ul class="skills-sidebar-list">
|
|
<li><a href="/docs/audit">audit</a></li>
|
|
<li><a href="/docs/critique">critique</a></li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="skills-sidebar-group" data-category="refine">
|
|
<p class="skills-sidebar-group-title">Refine</p>
|
|
<ul class="skills-sidebar-list">
|
|
<li><a href="/docs/animate">animate</a></li>
|
|
<li><a href="/docs/bolder">bolder</a></li>
|
|
<li><a href="/docs/colorize">colorize</a></li>
|
|
<li><a href="/docs/delight">delight</a></li>
|
|
<li><a href="/docs/layout">layout</a></li>
|
|
<li><a href="/docs/overdrive">overdrive</a></li>
|
|
<li><a href="/docs/quieter">quieter</a></li>
|
|
<li><a href="/docs/typeset">typeset</a></li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="skills-sidebar-group" data-category="simplify">
|
|
<p class="skills-sidebar-group-title">Simplify</p>
|
|
<ul class="skills-sidebar-list">
|
|
<li><a href="/docs/adapt">adapt</a></li>
|
|
<li><a href="/docs/clarify">clarify</a></li>
|
|
<li><a href="/docs/distill">distill</a></li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="skills-sidebar-group" data-category="harden">
|
|
<p class="skills-sidebar-group-title">Harden</p>
|
|
<ul class="skills-sidebar-list">
|
|
<li><a href="/docs/harden">harden</a></li>
|
|
<li><a href="/docs/onboard">onboard</a></li>
|
|
<li><a href="/docs/optimize">optimize</a></li>
|
|
<li><a href="/docs/polish">polish</a></li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="skills-sidebar-group" data-category="system">
|
|
<p class="skills-sidebar-group-title">System</p>
|
|
<ul class="skills-sidebar-list">
|
|
<li><a href="/docs/document">document</a></li>
|
|
<li><a href="/docs/extract">extract</a></li>
|
|
<li><a href="/docs/live">live</a></li>
|
|
<li><a href="/docs/teach">teach</a></li>
|
|
</ul>
|
|
</div>
|
|
|
|
</div>
|
|
</aside>
|
|
<div class="skills-main">
|
|
|
|
<article class="tutorial-detail">
|
|
<header class="tutorial-detail-header">
|
|
<p class="skill-detail-eyebrow"><a href="/tutorials">Tutorials</a> / 01</p>
|
|
<h1 class="tutorial-detail-title">Getting started</h1>
|
|
<p class="tutorial-detail-tagline">From zero to your first polish pass in five minutes.</p>
|
|
</header>
|
|
|
|
<section class="tutorial-detail-body prose">
|
|
<h2 id="what-youll-build">What you'll build</h2>
|
|
<p>You will end this tutorial with Impeccable installed in your project, a <code>PRODUCT.md</code> plus <code>DESIGN.md</code> pair that captures your brand, audience, and visual system, and one hand-polished page that went through a polish pass. Total time: about ten minutes.</p>
|
|
<h2 id="prerequisites">Prerequisites</h2>
|
|
<ul>
|
|
<li>An AI coding harness: Claude Code, Cursor, Gemini CLI, Codex CLI, or any of the other supported tools.</li>
|
|
<li>A project with at least one HTML or component file you want to improve. A fresh scaffolded landing page works fine.</li>
|
|
</ul>
|
|
<h2 id="how-impeccable-works">How Impeccable works</h2>
|
|
<p>Impeccable installs as a single agent skill called <code>impeccable</code>. You access all 23 sub-commands through it:</p>
|
|
<div class="code-block-wrap"><pre class="code-block"><code>/impeccable <command> <target></code></pre><button class="code-block-copy" type="button" data-copy="/impeccable <command> <target>" aria-label="Copy to clipboard"></button></div>
|
|
<p>For example: <code>/impeccable polish the pricing page</code>, or <code>/impeccable audit the checkout</code>. Type <code>/impeccable</code> alone to see the full list.</p>
|
|
<p>If you use a command often, pin it with <code>/impeccable pin <command></code> to create a standalone shortcut (for example, <code>/impeccable pin audit</code> gives you <code>/audit</code> directly).</p>
|
|
<h2 id="step-1-install">Step 1. Install</h2>
|
|
<p>From the root of your project, run:</p>
|
|
<div class="code-block-wrap"><pre class="code-block"><code>npx skills add pbakaus/impeccable</code></pre><button class="code-block-copy" type="button" data-copy="npx skills add pbakaus/impeccable" aria-label="Copy to clipboard"></button></div>
|
|
<p>This auto-detects your harness and writes the skill files to the right location (e.g., <code>.claude/skills/</code>, <code>.cursor/skills/</code>). Reload your harness and type <code>/</code>. You should see <code>/impeccable</code> in the autocomplete. Type it and the skill's argument hint will show all available commands.</p>
|
|
<h2 id="step-2-teach-impeccable-about-your-project">Step 2. Teach Impeccable about your project</h2>
|
|
<p>This is the most important step. Design without context produces generic output. The <code>/impeccable teach</code> command runs a short discovery interview and writes a <code>PRODUCT.md</code> file at the root of your project.</p>
|
|
<p>Run:</p>
|
|
<div class="code-block-wrap"><pre class="code-block"><code>/impeccable teach</code></pre><button class="code-block-copy" type="button" data-copy="/impeccable teach" aria-label="Copy to clipboard"></button></div>
|
|
<p>The first question is about <strong>register</strong>: is this a brand surface (marketing site, landing page, portfolio, where design IS the product) or a product surface (app UI, dashboard, tools, where design SERVES the product)? Register shapes every downstream default, from type lanes to motion energy. See <a href="/tutorials/brand-vs-product">brand vs product</a> for how the two diverge. Teach will form a hypothesis from your codebase and ask you to confirm, rather than starting cold.</p>
|
|
<p>Then a handful of shorter questions:</p>
|
|
<ul>
|
|
<li><strong>Who is this product for?</strong> Be specific. Not "users" but "solo founders evaluating a new tool on their phone between meetings".</li>
|
|
<li><strong>What is the brand voice in three words?</strong> Pick real words. "Warm and mechanical and opinionated" is better than "modern and clean".</li>
|
|
<li><strong>Any visual references?</strong> Named brands, products, or printed objects, not adjectives. "Klim Type Foundry specimen pages", not "technical and clean".</li>
|
|
<li><strong>Anti-references?</strong> Things the product should explicitly not look like, equally named.</li>
|
|
</ul>
|
|
<p>Answer in your own words. The skill writes <code>PRODUCT.md</code> with the answers. Every future command run reads it automatically.</p>
|
|
<p>Open <code>PRODUCT.md</code> and read what it wrote. Edit anything that does not feel right. The file is yours.</p>
|
|
<h2 id="step-25-capture-the-visual-system">Step 2.5. Capture the visual system</h2>
|
|
<p>At the end of <code>/impeccable teach</code>, the skill offers to run <code>/impeccable document</code> for you. Say yes. It scans your tokens (CSS custom properties, Tailwind config, CSS-in-JS themes), extracts colors and typography, asks one grouped question for the parts that need creative input (a Creative North Star, descriptive color names), and writes a <code>DESIGN.md</code> that follows the <a href="https://stitch.withgoogle.com/docs/design-md/format/" target="_blank" rel="noopener">Google Stitch DESIGN.md format</a>.</p>
|
|
<p>On a fresh project with no tokens yet, document runs in seed mode: five quick questions about color strategy, type direction, and motion energy, and writes a scaffold you can refresh once there is code.</p>
|
|
<p><code>PRODUCT.md</code> carries strategy (who, what, why). <code>DESIGN.md</code> carries visuals (colors, typography, components). Every command reads both before generating.</p>
|
|
<h2 id="step-3-polish-something">Step 3. Polish something</h2>
|
|
<p>Pick a page that already exists. An about page, a settings screen, a pricing table, anything. Run:</p>
|
|
<div class="code-block-wrap"><pre class="code-block"><code>/impeccable polish the pricing page</code></pre><button class="code-block-copy" type="button" data-copy="/impeccable polish the pricing page" aria-label="Copy to clipboard"></button></div>
|
|
<p>The skill will walk through alignment, spacing, typography, color, interaction states, transitions, and copy. It makes targeted fixes, not a rewrite. Expect a handful of small diffs that together lift the page from "done" to "done well".</p>
|
|
<p>A typical polish pass looks like:</p>
|
|
<div class="code-block-wrap"><pre class="code-block"><code>Visual alignment: fixed 3 off-grid elements
|
|
Typography: tightened h1 kerning, fixed widow on feature list
|
|
Color: replaced one hardcoded hex with --color-accent token
|
|
Interaction: added missing hover state on FAQ items
|
|
Motion: softened modal entrance to 220ms ease-out-quart
|
|
Copy: removed stray 'Lorem' placeholder</code></pre><button class="code-block-copy" type="button" data-copy="Visual alignment: fixed 3 off-grid elements
|
|
Typography: tightened h1 kerning, fixed widow on feature list
|
|
Color: replaced one hardcoded hex with --color-accent token
|
|
Interaction: added missing hover state on FAQ items
|
|
Motion: softened modal entrance to 220ms ease-out-quart
|
|
Copy: removed stray 'Lorem' placeholder" aria-label="Copy to clipboard"></button></div>
|
|
<p>Review the diff. If something does not feel right, ask the model to explain the change. If it still does not feel right, revert it. Impeccable is opinionated but not infallible.</p>
|
|
<h2 id="what-to-try-next">What to try next</h2>
|
|
<ul>
|
|
<li><a href="/tutorials/iterate-live">Iterate visually with Live Mode</a> opens a browser picker on your dev server, generates three production-quality variants per element, and writes the accepted one back to source.</li>
|
|
<li><code>/impeccable critique the landing page</code> runs a full design review with scoring, persona tests, and automated detection. It is the best way to find what to fix next.</li>
|
|
<li><code>/impeccable audit the checkout</code> runs accessibility, performance, theming, responsive, and anti-pattern checks against the implementation. Useful before shipping.</li>
|
|
<li><code>/impeccable craft a pricing page for enterprise customers</code> runs the full shape-then-build flow on a brand new feature.</li>
|
|
<li><strong>Pin your favorites.</strong> If you reach for one command constantly, <code>/impeccable pin audit</code> makes <code>/audit</code> work as a standalone shortcut without reversing the consolidation.</li>
|
|
<li><code>/impeccable redo this hero section</code> works too. Any description after <code>/impeccable</code> applies the design principles to the task.</li>
|
|
</ul>
|
|
<h2 id="common-issues">Common issues</h2>
|
|
<ul>
|
|
<li><strong>The skill says "no design context found"</strong>. You skipped step 2. Run <code>/impeccable teach</code> first.</li>
|
|
<li><strong>Commands do not appear in the harness</strong>. Reload the harness after installing. If they still do not appear, check that the installer wrote files into the expected location (<code>.claude/skills/</code>, <code>.cursor/skills/</code>, etc.) and that your harness is picking up that directory.</li>
|
|
<li><strong>The polish pass rewrote something you liked</strong>. Say so. Revert the change, tell the model which specific edit to undo, and continue from there.</li>
|
|
</ul>
|
|
|
|
</section>
|
|
</article>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
<script>
|
|
// Copy buttons on rendered code blocks
|
|
document.addEventListener('click', (e) => {
|
|
const btn = e.target.closest('[data-copy]');
|
|
if (!btn) return;
|
|
const text = btn.getAttribute('data-copy');
|
|
if (!text) return;
|
|
navigator.clipboard.writeText(text).then(() => {
|
|
btn.classList.add('is-copied');
|
|
setTimeout(() => btn.classList.remove('is-copied'), 1500);
|
|
}).catch(() => {});
|
|
});
|
|
|
|
// Mobile sidebar toggle (shown on narrow viewports, hidden on desktop).
|
|
document.addEventListener('click', (e) => {
|
|
const toggle = e.target.closest('.skills-sidebar-toggle');
|
|
if (!toggle) return;
|
|
const expanded = toggle.getAttribute('aria-expanded') === 'true';
|
|
toggle.setAttribute('aria-expanded', String(!expanded));
|
|
});
|
|
|
|
// Before/after split-compare: drag on touch, hover OR drag on mouse.
|
|
// Pointer events attach to the padded .split-comparison wrapper so
|
|
// there is a ~20px invisible buffer around the visible box. The
|
|
// divider only snaps back when the pointer leaves that outer buffer.
|
|
(function initSplitCompare() {
|
|
const wrappers = document.querySelectorAll('.split-comparison');
|
|
if (wrappers.length === 0) return;
|
|
const hasHover = matchMedia('(hover: hover)').matches;
|
|
const DEFAULT_POSITION = 50;
|
|
|
|
for (const wrapper of wrappers) {
|
|
const container = wrapper.querySelector('.split-container');
|
|
const splitAfter = wrapper.querySelector('.split-after');
|
|
const splitDivider = wrapper.querySelector('.split-divider');
|
|
if (!container || !splitAfter || !splitDivider) continue;
|
|
|
|
const tanAngle = Math.tan(10 * Math.PI / 180);
|
|
let skewOffset = 8;
|
|
const recalcSkew = () => {
|
|
const r = container.getBoundingClientRect();
|
|
if (r.width > 0 && r.height > 0) {
|
|
skewOffset = 50 * r.height * tanAngle / r.width;
|
|
}
|
|
};
|
|
recalcSkew();
|
|
window.addEventListener('resize', recalcSkew, { passive: true });
|
|
|
|
let targetX = DEFAULT_POSITION;
|
|
let currentX = DEFAULT_POSITION;
|
|
let rafId = null;
|
|
|
|
const paint = (pct) => {
|
|
const x = Math.max(-skewOffset, Math.min(100 + skewOffset, pct));
|
|
splitAfter.style.clipPath =
|
|
`polygon(${x + skewOffset}% 0%, 100% 0%, 100% 100%, ${x - skewOffset}% 100%)`;
|
|
splitDivider.style.left = `${x}%`;
|
|
};
|
|
|
|
const step = () => {
|
|
currentX += (targetX - currentX) * 0.2;
|
|
if (Math.abs(targetX - currentX) < 0.1) {
|
|
currentX = targetX;
|
|
rafId = null;
|
|
} else {
|
|
rafId = requestAnimationFrame(step);
|
|
}
|
|
paint(currentX);
|
|
};
|
|
|
|
const setTarget = (pct) => {
|
|
targetX = pct;
|
|
if (rafId === null) rafId = requestAnimationFrame(step);
|
|
};
|
|
|
|
paint(DEFAULT_POSITION);
|
|
|
|
// Percentage is always relative to the VISIBLE .split-container,
|
|
// not the padded .split-comparison wrapper. The pointer event
|
|
// target is the wrapper but the clip-path math uses the inner box.
|
|
const pctFromClientX = (clientX) => {
|
|
const rect = container.getBoundingClientRect();
|
|
return ((clientX - rect.left) / rect.width) * 100;
|
|
};
|
|
|
|
let hovering = false;
|
|
let dragging = false;
|
|
|
|
wrapper.addEventListener('pointerenter', (e) => {
|
|
if (hasHover && e.pointerType === 'mouse') {
|
|
hovering = true;
|
|
}
|
|
});
|
|
|
|
wrapper.addEventListener('pointerdown', (e) => {
|
|
dragging = true;
|
|
wrapper.setPointerCapture(e.pointerId);
|
|
setTarget(pctFromClientX(e.clientX));
|
|
});
|
|
|
|
wrapper.addEventListener('pointermove', (e) => {
|
|
if (dragging || hovering) {
|
|
setTarget(pctFromClientX(e.clientX));
|
|
}
|
|
});
|
|
|
|
const endDrag = (e) => {
|
|
if (dragging) {
|
|
dragging = false;
|
|
try { wrapper.releasePointerCapture(e.pointerId); } catch {}
|
|
}
|
|
};
|
|
|
|
wrapper.addEventListener('pointerup', endDrag);
|
|
wrapper.addEventListener('pointercancel', endDrag);
|
|
|
|
wrapper.addEventListener('pointerleave', (e) => {
|
|
endDrag(e);
|
|
if (hovering) {
|
|
hovering = false;
|
|
setTarget(DEFAULT_POSITION);
|
|
}
|
|
});
|
|
}
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|