mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-18 00:56:30 +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>
497 lines
29 KiB
HTML
497 lines
29 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>/impeccable teach | Impeccable</title>
|
|
<meta name="description" content="Teach Impeccable who your product is for, once per project.">
|
|
<meta name="theme-color" content="#fafafa">
|
|
<link rel="canonical" href="https://impeccable.style/docs/teach">
|
|
<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">
|
|
<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">/teach</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">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" aria-current="page">teach</a></li>
|
|
</ul>
|
|
</div>
|
|
|
|
</div>
|
|
</aside>
|
|
<div class="skills-main">
|
|
|
|
<article class="skill-detail">
|
|
<div class="skill-detail-hero">
|
|
<header class="skill-detail-header">
|
|
<p class="skill-detail-eyebrow"><a href="/docs">Docs</a> / System</p>
|
|
<h1 class="skill-detail-title"><span class="skill-detail-title-namespace"><span class="skill-detail-title-slash">/</span>impeccable</span>teach</h1>
|
|
<p class="skill-detail-tagline">Teach Impeccable who your product is for, once per project.</p>
|
|
|
|
<div class="skill-meta-strip">
|
|
<span class="skill-meta-chip skill-meta-category" data-category="system">System</span>
|
|
<span class="skill-meta-chip">User-invocable</span>
|
|
|
|
</div>
|
|
</header>
|
|
|
|
</div>
|
|
|
|
<section class="skill-detail-editorial prose">
|
|
<div class="docs-viz-hero">
|
|
<div class="docs-viz-file">
|
|
<div class="docs-viz-file-header">
|
|
<span class="docs-viz-file-name">PRODUCT.md</span>
|
|
<span class="docs-viz-file-status">Loaded on every command</span>
|
|
</div>
|
|
<div class="docs-viz-file-body">
|
|
<div class="docs-viz-file-row">
|
|
<span class="docs-viz-file-k">Register</span>
|
|
<span class="docs-viz-file-v">Product. Design serves the task.</span>
|
|
</div>
|
|
<div class="docs-viz-file-row">
|
|
<span class="docs-viz-file-k">Users</span>
|
|
<span class="docs-viz-file-v">SREs on call, reading fast, often in the dark.</span>
|
|
</div>
|
|
<div class="docs-viz-file-row">
|
|
<span class="docs-viz-file-k">Brand voice</span>
|
|
<span class="docs-viz-file-v">Calm, clinical, no hype.</span>
|
|
</div>
|
|
<div class="docs-viz-file-row">
|
|
<span class="docs-viz-file-k">Anti-references</span>
|
|
<span class="docs-viz-file-v">Purple gradients. Glassmorphism. "Boost your productivity."</span>
|
|
</div>
|
|
</div>
|
|
<div class="docs-viz-file-footer">Every command reads this before writing a line of code.</div>
|
|
</div>
|
|
<p class="docs-viz-caption">A finished PRODUCT.md. Strategy only: who, what, why. No colors, no fonts, no pixel values, those live in DESIGN.md.</p>
|
|
</div>
|
|
|
|
<h2 id="when-to-use-it">When to use it</h2>
|
|
<p>Run <code>/impeccable teach</code> once at the start of a project. It is the onramp. Without it, every other command will produce design that is technically competent but generically toned: stock SaaS voice, safe-default fonts, the AI color palette. With it, every command reads your answers before it generates.</p>
|
|
<p>Reach for it when:</p>
|
|
<ul>
|
|
<li><strong>You just installed Impeccable in a new project.</strong> First thing to run. Other commands will nudge you toward it if you skip.</li>
|
|
<li><strong>The project's brand direction has shifted.</strong> New positioning, new audience, new voice. Re-run <code>teach</code> and the updated context flows through every command.</li>
|
|
<li><strong>Another command said "no design context found"</strong> and stopped. That is the signal: run teach, then resume.</li>
|
|
</ul>
|
|
<h2 id="how-it-works">How it works</h2>
|
|
<p>Teach writes two complementary files at the project root:</p>
|
|
<ul>
|
|
<li><strong><code>PRODUCT.md</code></strong> is the strategic file. Register (brand or product), target users, product purpose, brand personality, anti-references, design principles, accessibility needs. Answers "who, what, why".</li>
|
|
<li><strong><code>DESIGN.md</code></strong> is the visual file. Colors, typography, elevation, components, do's and don'ts. Answers "how it looks". Written by the delegated <code>/impeccable document</code> command, which teach invokes at the end.</li>
|
|
</ul>
|
|
<p>The flow scans the codebase first (README, package.json, components, tokens, brand assets) and forms a <strong>register hypothesis</strong>: brand (landing, marketing, portfolio, where design IS the product) or product (app UI, dashboards, tools, where design SERVES the product). Register is the first question, because it shapes every downstream answer: typography defaults, motion energy, color strategy, the reference set commands like <code>/impeccable typeset</code> pull from. After register, teach asks only what it could not infer: users, personality in three real words, references and anti-references, accessibility requirements.</p>
|
|
<p>PRODUCT.md is strategic only. No colors, no fonts, no pixel values. Those live in DESIGN.md. Keeping the two files separate is deliberate: strategy can stay stable while the visual system evolves.</p>
|
|
<h2 id="try-it">Try it</h2>
|
|
<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>Expect a 5 to 8 minute interview. The first question is usually about register; the rest are short. Teach will quote back what it inferred from your code ("from the routes, this looks like a product surface, match?") so you are confirming, not starting from scratch.</p>
|
|
<p>At the end, teach offers to run <code>/impeccable document</code> for you. Say yes unless you have a specific reason to hold off. A real DESIGN.md is what keeps variants, polishes, and audits on-brand.</p>
|
|
<h2 id="pitfalls">Pitfalls</h2>
|
|
<ul>
|
|
<li><strong>Skipping it to "just try a command quickly".</strong> Every other command will interview you mid-flight instead. Running teach first is faster, not slower.</li>
|
|
<li><strong>Giving generic answers.</strong> "Modern and clean" is not useful. "Warm, mechanical, opinionated" is. Be specific. Be willing to disagree with safe defaults.</li>
|
|
<li><strong>Treating PRODUCT.md as immutable.</strong> The file is yours. If teach put something in there that is not quite right, edit it. Every command reads the current file.</li>
|
|
<li><strong>Listing only adjectives for references.</strong> Brands, products, printed objects: named, not described. "Klim Type Foundry specimen pages", not "technical and clean". Anti-references should be equally specific.</li>
|
|
</ul>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section class="skill-source-card">
|
|
<header class="skill-source-card-header">
|
|
<span class="skill-source-card-label">reference/teach.md</span>
|
|
<span class="skill-source-card-subtitle">Loaded when the impeccable skill routes to this command.</span>
|
|
</header>
|
|
<div class="skill-source-card-body prose">
|
|
<h1 id="teach-flow">Teach Flow</h1>
|
|
<p>Gathers design context for a project and writes two complementary files at the project root:</p>
|
|
<ul>
|
|
<li><strong>PRODUCT.md</strong> (strategic): register, target users, product purpose, brand personality, anti-references, strategic design principles. Answers "who/what/why".</li>
|
|
<li><strong>DESIGN.md</strong> (visual): visual theme, color palette, typography, components, layout. Follows the <a href="https://stitch.withgoogle.com/docs/design-md/format/" target="_blank" rel="noopener">Google Stitch DESIGN.md format</a>. Answers "how it looks".</li>
|
|
</ul>
|
|
<p>Every other impeccable command reads these files before doing any work.</p>
|
|
<h2 id="step-1-load-current-state">Step 1: Load current state</h2>
|
|
<p>Run the shared loader first so you know what already exists:</p>
|
|
<div class="code-block-wrap"><pre class="code-block code-block--bash"><code>node {{scripts_path}}/load-context.mjs</code></pre><button class="code-block-copy" type="button" data-copy="node {{scripts_path}}/load-context.mjs" aria-label="Copy to clipboard"></button></div>
|
|
<p>The output tells you whether PRODUCT.md and/or DESIGN.md already exist. If <code>migrated: true</code>, legacy <code>.impeccable.md</code> was auto-renamed to <code>PRODUCT.md</code>. Mention this once to the user.</p>
|
|
<p>Decision tree:</p>
|
|
<ul>
|
|
<li><strong>Neither file exists (empty project or no context yet)</strong>: do Steps 2-4 (write PRODUCT.md), then decide on DESIGN.md based on whether there's code to analyze.</li>
|
|
<li><strong>PRODUCT.md exists, DESIGN.md missing</strong>: skip to Step 5 — offer to run <code>/impeccable document</code> for DESIGN.md.</li>
|
|
<li><strong>PRODUCT.md exists but has no <code>## Register</code> section (legacy)</strong>: add it. Infer a hypothesis from the codebase (see Step 2), confirm with the user, write the field.</li>
|
|
<li><strong>Both exist</strong>: STOP and call the AskUserQuestion tool to clarify. Ask which file to refresh. Skip the one the user doesn't want changed.</li>
|
|
<li><strong>Just DESIGN.md exists (unusual)</strong>: do Steps 2-4 to produce PRODUCT.md.</li>
|
|
</ul>
|
|
<p>Never silently overwrite an existing file. Always confirm first.</p>
|
|
<p>If teach was invoked as a setup blocker by another command, such as <code>/impeccable craft landing page</code>, pause that command here. Complete teach, re-run the loader, then resume the original command with the freshly loaded context. For craft, resume into shape next; teach creates project context, but it is not a substitute for the task-specific shape interview and confirmed design brief.</p>
|
|
<h2 id="step-2-explore-the-codebase">Step 2: Explore the codebase</h2>
|
|
<p>Before asking questions, thoroughly scan the project to discover what you can:</p>
|
|
<ul>
|
|
<li><strong>README and docs</strong>: Project purpose, target audience, any stated goals</li>
|
|
<li><strong>Package.json / config files</strong>: Tech stack, dependencies, existing design libraries</li>
|
|
<li><strong>Existing components</strong>: Current design patterns, spacing, typography in use</li>
|
|
<li><strong>Brand assets</strong>: Logos, favicons, color values already defined</li>
|
|
<li><strong>Design tokens / CSS variables</strong>: Existing color palettes, font stacks, spacing scales</li>
|
|
<li><strong>Any style guides or brand documentation</strong></li>
|
|
</ul>
|
|
<p>Also form a <strong>register hypothesis</strong> from what you find:</p>
|
|
<ul>
|
|
<li>Brand signals: <code>/</code>, <code>/about</code>, <code>/pricing</code>, <code>/blog/*</code>, <code>/docs/*</code>, hero sections, big typography, scroll-driven sections, landing-page-shaped content.</li>
|
|
<li>Product signals: <code>/app/*</code>, <code>/dashboard</code>, <code>/settings</code>, <code>/(auth)</code>, forms, data tables, side/top nav, app-shell components.</li>
|
|
</ul>
|
|
<p>Register is a hypothesis at this point, not a decision — Step 3 confirms it.</p>
|
|
<p>Note what you've learned and what remains unclear. This exploration feeds both PRODUCT.md and DESIGN.md.</p>
|
|
<h2 id="step-3-ask-strategic-questions-for-productmd">Step 3: Ask strategic questions (for PRODUCT.md)</h2>
|
|
<p>STOP and call the AskUserQuestion tool to clarify. Ask only about what you couldn't infer from the codebase.</p>
|
|
<h3 id="interview-mode-not-confirmation-mode">Interview mode, not confirmation mode</h3>
|
|
<p>If the repo is empty or the user's brief is sparse, run a short interview before proposing PRODUCT.md. Do <strong>not</strong> turn a one-sentence request into a complete inferred PRODUCT.md and ask for blanket confirmation.</p>
|
|
<ul>
|
|
<li>Use the harness's structured question tool when one exists. Otherwise, ask directly in chat and stop.</li>
|
|
<li>Ask <strong>2-3 questions per round</strong>, then wait for answers.</li>
|
|
<li>Use inferred answers as hypotheses or options, not as finished facts.</li>
|
|
<li>Complete at least one real user-answer round before drafting PRODUCT.md, unless every required answer is directly discoverable from repo docs.</li>
|
|
<li>Round 1 should establish register, users/purpose, and desired outcome.</li>
|
|
<li>Round 2 should establish brand personality or references, anti-references, and accessibility needs.</li>
|
|
</ul>
|
|
<h3 id="minimum-viable-interview">Minimum viable interview</h3>
|
|
<p>Ask enough to complete PRODUCT.md. At minimum, cover register confirmation, users and purpose, brand personality, anti-references, and accessibility needs unless each answer is directly discoverable from repo context. After at least one interview round, you may propose inferred answers, but the user must confirm them before you write PRODUCT.md. Never synthesize PRODUCT.md from the original task prompt alone.</p>
|
|
<h3 id="register-ask-first-it-shapes-everything-below">Register (ask first — it shapes everything below)</h3>
|
|
<p>Every design task is either <strong>brand</strong> (marketing, landing, campaign, long-form content, portfolio — design IS the product) or <strong>product</strong> (app UI, admin, dashboards, tools — design SERVES the product).</p>
|
|
<p>If Step 2 produced a clear hypothesis, lead with it: <em>"From the codebase, this looks like a [brand / product] surface — does that match your intent, or should we treat it differently?"</em></p>
|
|
<p>If the signal is genuinely split (e.g. a product with a big marketing landing), STOP and call the AskUserQuestion tool to clarify. Ask which register describes the <strong>primary</strong> surface. The register can be overridden per task later, but PRODUCT.md carries one default.</p>
|
|
<h3 id="users-purpose">Users & Purpose</h3>
|
|
<ul>
|
|
<li>Who uses this? What's their context when using it?</li>
|
|
<li>What job are they trying to get done?</li>
|
|
<li>For brand: what emotions should the interface evoke? (confidence, delight, calm, urgency)</li>
|
|
<li>For product: what workflow are they in? What's the primary task on any given screen?</li>
|
|
</ul>
|
|
<h3 id="brand-personality">Brand & Personality</h3>
|
|
<ul>
|
|
<li>How would you describe the brand personality in 3 words?</li>
|
|
<li>Reference sites or apps that capture the right feel? What specifically about them?<ul>
|
|
<li>For brand, push for real-world references in the right lane (tech-minimal, editorial-magazine, consumer-warm, brutalist-grid, etc.) — not generic "modern" adjectives.</li>
|
|
<li>For product, push for category best-tool references (Linear, Figma, Notion, Raycast, Stripe).</li>
|
|
</ul>
|
|
</li>
|
|
<li>What should this explicitly NOT look like? Any anti-references?</li>
|
|
</ul>
|
|
<h3 id="accessibility-inclusion">Accessibility & Inclusion</h3>
|
|
<ul>
|
|
<li>Specific accessibility requirements? (WCAG level, known user needs)</li>
|
|
<li>Considerations for reduced motion, color blindness, or other accommodations?</li>
|
|
</ul>
|
|
<p>Skip questions where the answer is already clear. <strong>Do NOT ask about colors, fonts, radii, or visual styling here</strong> — those belong in DESIGN.md, not PRODUCT.md.</p>
|
|
<h2 id="step-4-write-productmd">Step 4: Write PRODUCT.md</h2>
|
|
<p>Write PRODUCT.md only after the user has confirmed the strategic answers from Step 3. If an inferred answer is uncertain or unconfirmed, ask before writing.</p>
|
|
<p>Synthesize into a strategic document:</p>
|
|
<div class="code-block-wrap"><pre class="code-block code-block--markdown"><code># Product
|
|
|
|
## Register
|
|
|
|
product
|
|
|
|
## Users
|
|
[Who they are, their context, the job to be done]
|
|
|
|
## Product Purpose
|
|
[What this product does, why it exists, what success looks like]
|
|
|
|
## Brand Personality
|
|
[Voice, tone, 3-word personality, emotional goals]
|
|
|
|
## Anti-references
|
|
[What this should NOT look like. Specific bad-example sites or patterns to avoid.]
|
|
|
|
## Design Principles
|
|
[3-5 strategic principles derived from the conversation. Principles like "practice what you preach", "show, don't tell", "expert confidence" — NOT visual rules like "use OKLCH" or "magenta accent".]
|
|
|
|
## Accessibility & Inclusion
|
|
[WCAG level, known user needs, considerations]</code></pre><button class="code-block-copy" type="button" data-copy="# Product
|
|
|
|
## Register
|
|
|
|
product
|
|
|
|
## Users
|
|
[Who they are, their context, the job to be done]
|
|
|
|
## Product Purpose
|
|
[What this product does, why it exists, what success looks like]
|
|
|
|
## Brand Personality
|
|
[Voice, tone, 3-word personality, emotional goals]
|
|
|
|
## Anti-references
|
|
[What this should NOT look like. Specific bad-example sites or patterns to avoid.]
|
|
|
|
## Design Principles
|
|
[3-5 strategic principles derived from the conversation. Principles like "practice what you preach", "show, don't tell", "expert confidence" — NOT visual rules like "use OKLCH" or "magenta accent".]
|
|
|
|
## Accessibility & Inclusion
|
|
[WCAG level, known user needs, considerations]" aria-label="Copy to clipboard"></button></div>
|
|
<p>Register is either <code>brand</code> or <code>product</code> as a bare value. No prose, no commentary.</p>
|
|
<p>Write to <code>PROJECT_ROOT/PRODUCT.md</code>. If <code>.impeccable.md</code> existed, the loader already renamed it — merge into that content rather than starting from scratch.</p>
|
|
<h2 id="step-5-decide-on-designmd">Step 5: Decide on DESIGN.md</h2>
|
|
<p>Offer <code>/impeccable document</code> either way. Two paths:</p>
|
|
<ul>
|
|
<li><strong>Code exists</strong> (CSS tokens, components, a running site): "I can generate a DESIGN.md that captures your visual system (colors, typography, components) so variants stay on-brand. Want to do that now?"</li>
|
|
<li><strong>Pre-implementation</strong> (empty project): "I can seed a starter DESIGN.md from five quick questions about color strategy, type direction, motion energy, and references. You can re-run once there's code, to capture the real tokens. Want to do that now?"</li>
|
|
</ul>
|
|
<p>If the user agrees, delegate to <code>/impeccable document</code> (it auto-detects scan vs seed). Load its reference and follow that flow.</p>
|
|
<p>If the user prefers to skip, mention they can run <code>/impeccable document</code> any time later.</p>
|
|
<h2 id="step-6-confirm-and-wrap-up">Step 6: Confirm and wrap up</h2>
|
|
<p>Summarize:</p>
|
|
<ul>
|
|
<li>Register captured (brand / product)</li>
|
|
<li>What was written (PRODUCT.md, DESIGN.md, or both)</li>
|
|
<li>The 3-5 strategic principles from PRODUCT.md that will guide future work</li>
|
|
<li>If DESIGN.md is pending, remind the user how to generate it later</li>
|
|
</ul>
|
|
<p><strong>Critical: re-run the loader to refresh session context.</strong> After writing PRODUCT.md, run <code>node {{scripts_path}}/load-context.mjs</code> one final time and let its full JSON output land in conversation. This ensures subsequent commands in this session use the freshly-written PRODUCT.md, not a stale earlier version.</p>
|
|
<p>If teach was invoked as a blocker by another impeccable command (e.g. the user ran <code>/impeccable polish</code> with no PRODUCT.md), resume that original task now with the fresh context.</p>
|
|
<p>Optionally STOP and call the AskUserQuestion tool to clarify. Ask whether they'd like a brief summary of PRODUCT.md appended to CLAUDE.md for easier agent reference. If yes, append a short <strong>Design Context</strong> pointer section there.</p>
|
|
|
|
</div>
|
|
</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>
|