mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-12 14:16:28 +03:00
510 lines
42 KiB
Markdown
510 lines
42 KiB
Markdown
Generate a `DESIGN.md` file at the project root that captures the current visual design system, so AI agents generating new screens stay on-brand.
|
||
|
||
DESIGN.md follows the [official DESIGN.md format spec](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md): optional YAML frontmatter carrying machine-readable design tokens, followed by up to eight markdown sections in a fixed order. **Tokens are normative; prose provides context for how to apply them.** Sections may be omitted when not relevant, but those present stay in the specified order. Use the canonical headings below so the file remains portable across DESIGN.md-aware tools.
|
||
|
||
## The frontmatter: token schema
|
||
|
||
The YAML frontmatter is the machine-readable layer. It's what Stitch's linter validates and what the live panel renders tiles from. Keep it tight; every entry should correspond to a token the project actually uses.
|
||
|
||
```yaml
|
||
---
|
||
name: <project title>
|
||
description: <one-line tagline>
|
||
colors:
|
||
primary: "#b8422e"
|
||
neutral-bg: "#faf7f2"
|
||
# ...one entry per extracted color; key = descriptive slug
|
||
typography:
|
||
display:
|
||
fontFamily: "Cormorant Garamond, Georgia, serif"
|
||
fontSize: "clamp(2.5rem, 7vw, 4.5rem)"
|
||
fontWeight: 300
|
||
lineHeight: 1
|
||
letterSpacing: "normal"
|
||
body:
|
||
# ...
|
||
rounded:
|
||
sm: "4px"
|
||
md: "8px"
|
||
spacing:
|
||
sm: "8px"
|
||
md: "16px"
|
||
components:
|
||
button-primary:
|
||
backgroundColor: "{colors.primary}"
|
||
textColor: "{colors.neutral-bg}"
|
||
rounded: "{rounded.sm}"
|
||
padding: "16px 48px"
|
||
button-primary-hover:
|
||
backgroundColor: "{colors.primary-deep}"
|
||
---
|
||
```
|
||
|
||
Rules that matter:
|
||
|
||
- **Token refs** use `{path.to.token}` (e.g. `{colors.primary}`, `{rounded.md}`). Components may reference primitives; primitives may not reference each other.
|
||
- **Colors accept any valid CSS color string.** Hex is the recommended default for portability, but preserve an incumbent `rgb()`, `hsl()`, `oklch()`, wide-gamut, or mixed-color value when it is the project's normative source. Never split the source of truth without explicit reason.
|
||
- **Component sub-tokens** are limited to 8 props: `backgroundColor`, `textColor`, `typography`, `rounded`, `padding`, `size`, `height`, `width`. Shadows, motion, focus rings, backdrop-filter: none of those fit. Carry them in the sidecar (Step 4b).
|
||
- **Scale keys are open-ended.** Use whatever names the project already uses (`oxblood-deep`, `surface-container-low`). Don't rename to Material defaults.
|
||
- **Variants are naming convention, not schema.** `button-primary` / `button-primary-hover` / `button-primary-active` as sibling keys.
|
||
|
||
## The markdown body: eight sections (canonical order)
|
||
|
||
1. `## Overview`
|
||
2. `## Colors`
|
||
3. `## Typography`
|
||
4. `## Layout`
|
||
5. `## Elevation & Depth`
|
||
6. `## Shapes`
|
||
7. `## Components`
|
||
8. `## Do's and Don'ts`
|
||
|
||
Omit irrelevant sections rather than filling them with invented rules. Put responsive layout in Layout, depth in Elevation & Depth, radius and form language in Shapes, and per-component behavior in Components. Unknown sections are preserved by the format, but new visual guidance should use the canonical structure whenever it fits.
|
||
|
||
## When to run
|
||
|
||
- New-work found a coherent incumbent visual system but no `DESIGN.md`.
|
||
- New-work paused before its direction roll on a project with no `DESIGN.md` and the user accepted the seed questionnaire recommendation; run seed mode.
|
||
- The first implementation of a new world is complete and its provisional decisions need to be carbonized.
|
||
- An existing `DESIGN.md` is stale (the design has drifted).
|
||
- Before a large redesign, to capture the current state as a reference.
|
||
|
||
If a `DESIGN.md` already exists, **do not silently overwrite it**. Show the user the existing file first. {{ask_instruction}} The choice is refresh, overwrite, or merge.
|
||
|
||
## Two paths
|
||
|
||
- **Scan mode** (default): the project has design tokens, components, or rendered output. Extract, then confirm descriptive language. Use when there's code to analyze.
|
||
- **Seed mode**: the project is pre-implementation (fresh init, nothing built yet). Decide first whether the browser questionnaire can run, gather any existing brand assets, interview in chat (three named references and one anti-reference when the questionnaire will run; five high-level answers when it will not), then write a seed DESIGN.md marked `<!-- SEED -->` that carries every decision the interview and the questionnaire made. Re-run in scan mode once there's code.
|
||
|
||
Decide by scanning first (Scan mode Step 1). If the scan finds no tokens, no component files, and no rendered site, offer seed mode; don't silently switch. `/impeccable document --seed` forces seed mode on a pre-implementation project, but it does not authorize replacing coherent code: when an incumbent system exists, offer scan mode or route an explicit identity-replacement request through new-work.
|
||
|
||
## Scan mode (approach C: auto-extract, then confirm descriptive language)
|
||
|
||
### Step 1: Find the design assets
|
||
|
||
Search the codebase in priority order:
|
||
|
||
1. **CSS custom properties**: grep for `--color-`, `--font-`, `--spacing-`, `--radius-`, `--shadow-`, `--ease-`, `--duration-` declarations in CSS files (usually `src/styles/`, `public/css/`, `app/globals.css`, etc.). Record name, value, and the file it's defined in.
|
||
2. **Tailwind config**: if `tailwind.config.{js,ts,mjs}` exists, read the `theme.extend` block for colors, fontFamily, spacing, borderRadius, boxShadow.
|
||
3. **CSS-in-JS theme files**: styled-components, emotion, vanilla-extract, stitches; look for `theme.ts`, `tokens.ts`, or equivalent.
|
||
4. **Design token files**: `tokens.json`, `design-tokens.json`, Style Dictionary output, W3C token community group format.
|
||
5. **Component library**: scan the main button, card, input, navigation, dialog components. Note their variant APIs and default styles.
|
||
6. **Global stylesheet**: the root CSS file usually has the base typography and color assignments.
|
||
7. **Visible rendered output**: if browser automation tools are available, load the live site and sample computed styles from key elements (body, h1, a, button, .card). This catches values that tokens miss.
|
||
|
||
### Step 2: Auto-extract what can be auto-extracted
|
||
|
||
Build a structured draft from the discovered tokens. For each token class:
|
||
|
||
- **Colors**: Group into Primary / Secondary / Tertiary / Neutral (the Material-derived roles Stitch uses). If the project only has one accent, express it as Primary + Neutral; omit Secondary and Tertiary rather than inventing them.
|
||
- **Typography**: Map observed sizes and weights to the Material hierarchy (display / headline / title / body / label). Note font-family stacks and the scale ratio.
|
||
- **Elevation**: Catalogue the shadow vocabulary. If the project is flat and uses tonal layering instead, that's a valid answer; state it explicitly.
|
||
- **Components**: For each common component (button, card, input, chip, list item, tooltip, nav), extract shape (radius), color assignment, hover/focus treatment, internal padding.
|
||
- **Layout + spacing**: Extract grid, container, breakpoint, rhythm, and density behavior into Layout.
|
||
- **Shapes**: Extract radius, corner, border, clipping, and recurring form behavior into Shapes.
|
||
|
||
### Step 2b: Stage the frontmatter
|
||
|
||
From the auto-extracted tokens, draft the YAML frontmatter now (you'll write it at the top of DESIGN.md in Step 4). This is the machine-readable layer: what the live panel and Stitch's linter consume.
|
||
|
||
- **Colors**: one entry per extracted color. Key = descriptive slug (`oxblood-deep`, `editorial-magenta`, not `blue-800`). Value = whichever format the project treats as canonical (OKLCH or hex; see the frontmatter rules above). Don't split the source of truth: one format in the frontmatter, don't redefine the same token in prose with a different value.
|
||
- **Typography**: one entry per role (`display`, `headline`, `title`, `body`, `label`). Typography is an object; include only the props that are real for the project (`fontFamily`, `fontSize`, `fontWeight`, `lineHeight`, `letterSpacing`, `fontFeature`, `fontVariation`).
|
||
- **Rounded / Spacing**: whatever scale steps the project actually uses, keyed by whatever scale name the project uses (`sm` / `md` / `lg`, or `surface-sm`, or numeric steps).
|
||
- **Components**: one entry per variant (`button-primary`, `button-primary-hover`, `button-ghost`). Reference primitives via `{colors.X}`, `{rounded.Y}`. If a variant needs a property Stitch's 8-prop set doesn't cover (shadow, focus ring, backdrop-filter), carry the full snippet in the sidecar instead.
|
||
|
||
Skip anything the project doesn't have. Empty scale keys or fabricated tokens pollute the spec.
|
||
|
||
### Step 3: Ask the user for qualitative language
|
||
|
||
The following require creative input that cannot be auto-extracted. Ask them in two structured rounds of no more than three questions each (or the harness's lower limit), waiting between rounds:
|
||
|
||
- **Creative North Star**: a single named metaphor for the whole system ("The Editorial Sanctuary", "The Golden State Curator", "The Lab Notebook"). Offer 2-3 options that honor PRODUCT.md's brand personality.
|
||
- **Overview voice**: mood adjectives, aesthetic philosophy in 2-3 sentences, and any confirmed visual anti-reference.
|
||
- **Color character** (for auto-extracted colors): descriptive names ("Deep Muted Teal-Navy", not "blue-800"). Suggest 2-3 options per key color based on hue/saturation.
|
||
- **Elevation philosophy**: flat/layered/lifted. If shadows exist, is their role ambient or structural?
|
||
- **Component philosophy**: the feel of buttons, cards, inputs in one phrase ("tactile and confident" vs. "refined and restrained").
|
||
|
||
Carry a line from PRODUCT.md only when it is a durable brand commitment that actually constrains the visual system. Page strategy and surface concepts do not belong here.
|
||
|
||
### Step 4: Write DESIGN.md
|
||
|
||
The file opens with the YAML frontmatter staged in Step 2b (schema documented at the top of this reference), then the markdown body using the canonical structure below.
|
||
|
||
```markdown
|
||
---
|
||
name: [Project Title]
|
||
description: [one-line tagline]
|
||
colors:
|
||
# ... staged frontmatter from Step 2b
|
||
---
|
||
|
||
# Design System: [Project Title]
|
||
|
||
## Overview
|
||
|
||
**Creative North Star: "[Named metaphor in quotes]"**
|
||
|
||
[2-3 paragraph holistic description: personality, density, and aesthetic philosophy. Start from the North Star and work outward. State only confirmed visual rejections. End with a short **Key Characteristics:** bullet list.]
|
||
|
||
## Colors
|
||
|
||
[Describe the palette character in one sentence.]
|
||
|
||
### Primary
|
||
- **[Descriptive Name]** (#HEX / oklch(...)): [Where and why this color is used. Be specific about context, not just role.]
|
||
|
||
### Secondary (optional; omit if the project has only one accent)
|
||
- **[Descriptive Name]** (#HEX): [Role.]
|
||
|
||
### Tertiary (optional)
|
||
- **[Descriptive Name]** (#HEX): [Role.]
|
||
|
||
### Neutral
|
||
- **[Descriptive Name]** (#HEX): [Text / background / border / divider role.]
|
||
- [...]
|
||
|
||
### Named Rules (optional, powerful)
|
||
**The [Rule Name] Rule.** [Short, forceful prohibition or doctrine, e.g. "The One Voice Rule. The primary accent is used on ≤10% of any given screen. Its rarity is the point."]
|
||
|
||
## Typography
|
||
|
||
**Display Font:** [Family] (with [fallback])
|
||
**Body Font:** [Family] (with [fallback])
|
||
**Label/Mono Font:** [Family, if distinct]
|
||
|
||
**Character:** [1-2 sentence personality description of the pairing.]
|
||
|
||
### Hierarchy
|
||
- **Display** ([weight], [size/clamp], [line-height]): [Purpose; where it appears.]
|
||
- **Headline** ([weight], [size], [line-height]): [Purpose.]
|
||
- **Title** ([weight], [size], [line-height]): [Purpose.]
|
||
- **Body** ([weight], [size], [line-height]): [Purpose. Include max line length like 65–75ch if relevant.]
|
||
- **Label** ([weight], [size], [letter-spacing], [case if uppercase]): [Purpose.]
|
||
|
||
### Named Rules (optional)
|
||
**The [Rule Name] Rule.** [Short doctrine about type use.]
|
||
|
||
## Layout
|
||
|
||
[Describe the grid or spatial model, container behavior, density, responsive changes, and the spacing rhythm. Include exact values only when observed.]
|
||
|
||
## Elevation & Depth
|
||
|
||
[One paragraph: does this system use shadows, tonal layering, or a hybrid? If "no shadows", say so explicitly and describe how depth is conveyed instead.]
|
||
|
||
### Shadow Vocabulary (if applicable)
|
||
- **[Role name]** (`box-shadow: [exact value]`): [When to use it.]
|
||
- [...]
|
||
|
||
### Named Rules (optional)
|
||
**The [Rule Name] Rule.** [e.g. "The Flat-By-Default Rule. Surfaces are flat at rest. Shadows appear only as a response to state (hover, elevation, focus)."]
|
||
|
||
## Shapes
|
||
|
||
[Describe the form language: corner/radius strategy, borders, clipping, and any recurring silhouette or geometry.]
|
||
|
||
## Components
|
||
|
||
For each component, lead with a short character line, then specify shape, color assignment, states, and any distinctive behavior.
|
||
|
||
### Buttons
|
||
- **Shape:** [radius described, exact value in parens]
|
||
- **Primary:** [color assignment + padding, in semantic + exact terms]
|
||
- **Hover / Focus:** [transitions, treatments]
|
||
- **Secondary / Ghost / Tertiary (if applicable):** [brief description]
|
||
|
||
### Chips (if used)
|
||
- **Style:** [background, text color, border treatment]
|
||
- **State:** [selected / unselected, filter / action variants]
|
||
|
||
### Cards / Containers
|
||
- **Corner Style:** [radius]
|
||
- **Background:** [colors used]
|
||
- **Shadow Strategy:** [reference Elevation section]
|
||
- **Border:** [if any]
|
||
- **Internal Padding:** [scale]
|
||
|
||
### Inputs / Fields
|
||
- **Style:** [stroke, background, radius]
|
||
- **Focus:** [treatment, e.g. glow, border shift, etc.]
|
||
- **Error / Disabled:** [if applicable]
|
||
|
||
### Navigation
|
||
- **Style, typography, default/hover/active states, mobile treatment.**
|
||
|
||
### [Signature Component] (optional; if the project has a distinctive custom component worth documenting)
|
||
[Description.]
|
||
|
||
## Do's and Don'ts
|
||
|
||
Concrete visual guardrails grounded in the incumbent implementation or the user's chosen world. Lead each with "Do" or "Don't" and include exact values only when established. Do not turn a task-specific concept or surface strategy into a system-wide prohibition.
|
||
|
||
### Do:
|
||
- **Do** [specific prescription with exact values / named rule].
|
||
- **Do** [...]
|
||
|
||
### Don't:
|
||
- **Don't** [specific prohibition confirmed by the incumbent system or the user].
|
||
- **Don't** [...]
|
||
- **Don't** [...]
|
||
```
|
||
|
||
### Step 4b: Write .impeccable/design.json sidecar (extensions only)
|
||
|
||
The frontmatter owns token primitives (colors, typography, rounded, spacing, components). The sidecar at `.impeccable/design.json` carries **what Stitch's schema can't hold**: tonal ramps per color, shadow/elevation tokens, motion tokens, breakpoints, full component HTML/CSS snippets (the panel renders these into a shadow DOM), and narrative (north star, rules, do's/don'ts). It extends the frontmatter, it doesn't duplicate it.
|
||
|
||
Regenerate the sidecar whenever you regenerate root `DESIGN.md`. If the user only asks to refresh the sidecar (e.g., from the live panel's stale-hint), preserve `DESIGN.md` and write only `.impeccable/design.json`.
|
||
|
||
#### Schema
|
||
|
||
```json
|
||
{
|
||
"schemaVersion": 2,
|
||
"generatedAt": "ISO-8601 string",
|
||
"title": "Design System: [Project Title]",
|
||
"extensions": {
|
||
"colorMeta": {
|
||
"primary": { "role": "primary", "displayName": "Editorial Magenta", "canonical": "oklch(60% 0.25 350)", "tonalRamp": ["...", "...", "..."] },
|
||
"cool-paper": { "role": "neutral", "displayName": "Cool Paper", "canonical": "oklch(96% 0.005 230)", "tonalRamp": ["...", "...", "..."] }
|
||
},
|
||
"typographyMeta": {
|
||
"display": { "displayName": "Display", "purpose": "Hero headlines only." }
|
||
},
|
||
"shadows": [
|
||
{ "name": "ambient-low", "value": "0 4px 24px rgba(0,0,0,0.12)", "purpose": "Diffuse hover glow under accent elements." }
|
||
],
|
||
"motion": [
|
||
{ "name": "ease-standard", "value": "cubic-bezier(0.4, 0, 0.2, 1)", "purpose": "Default easing for state transitions." }
|
||
],
|
||
"breakpoints": [
|
||
{ "name": "sm", "value": "640px" }
|
||
]
|
||
},
|
||
"components": [
|
||
{
|
||
"name": "Primary Button",
|
||
"kind": "button | input | nav | chip | card | custom",
|
||
"refersTo": "button-primary",
|
||
"description": "One-line what and when.",
|
||
"html": "<button class=\"ds-btn-primary\">SAVE CHANGES</button>",
|
||
"css": ".ds-btn-primary { background: #191c1d; color: #fff; padding: 16px 48px; letter-spacing: 0.05em; text-transform: uppercase; font-weight: 500; border: none; border-radius: 0; transition: background 0.2s, transform 0.2s; } .ds-btn-primary:hover { background: oklch(60% 0.25 350); transform: translateY(-2px); }"
|
||
}
|
||
],
|
||
"narrative": {
|
||
"northStar": "The Editorial Sanctuary",
|
||
"overview": "2-3 paragraphs of the philosophy, pulled from DESIGN.md Overview section.",
|
||
"keyCharacteristics": ["...", "..."],
|
||
"rules": [{ "name": "The One Voice Rule", "body": "...", "section": "colors|typography|elevation" }],
|
||
"dos": ["Do use ..."],
|
||
"donts": ["Don't use ..."]
|
||
}
|
||
}
|
||
```
|
||
|
||
**What changed from schemaVersion 1.** The old sidecar carried token primitive arrays (`tokens.colors[]`, `tokens.typography[]`, etc.). Those values now live in the frontmatter. The sidecar only carries metadata that can't live in the frontmatter (tonal ramps, canonical OKLCH when the hex is an approximation, display names, role hints), keyed by the frontmatter token name (`colorMeta.<token-name>`, `typographyMeta.<token-name>`). Components still carry full HTML/CSS because Stitch's 8-prop set can't hold them.
|
||
|
||
#### Component translation rules
|
||
|
||
The `html` and `css` fields must be **self-contained, drop-in snippets** that render correctly when injected into a shadow DOM. The panel applies them directly: no post-processing, no framework runtime.
|
||
|
||
1. **Tailwind expansion.** If the source uses Tailwind (className="bg-primary text-white rounded-lg px-6 py-3"), expand every utility to literal CSS properties in the `css` string. Do **not** reference Tailwind classes; do **not** assume a Tailwind CSS bundle is loaded. Each component is self-contained.
|
||
2. **Token resolution.** If the project exposes tokens as CSS custom properties on `:root` (e.g. `--color-primary`, `--radius-md`), reference them via `var(--color-primary)`; they inherit through the shadow DOM and stay live-bound. If tokens live only in JS theme objects (styled-components, CSS-in-JS), resolve to literal values at generation time.
|
||
3. **Icons.** Inline as SVG. Do not reference Lucide/Heroicons packages, icon fonts, or `<img src="...">`. A typical icon is 16-24px; copy the SVG path data directly. This is about how a snippet ships, not about which family the project draws from: when the design names an icon set, keep using that set's glyphs and paste their path data in.
|
||
4. **States.** Include `:hover`, `:focus-visible`, and (if meaningful) `:active` rules inline. A static default-only snapshot makes the panel feel dead. Hover + focus rules in the CSS make it feel alive.
|
||
5. **Reset bloat.** Extract only the component's *distinctive* CSS (background, color, padding, border-radius, typography, transition). Skip universal resets (`box-sizing: border-box`, `line-height: inherit`, `-webkit-font-smoothing`). The panel already has a neutral canvas; don't re-ship resets.
|
||
6. **Scoped class names.** Prefix every class with `ds-` (e.g. `ds-btn-primary`, `ds-input-search`) so component CSS doesn't collide with other components' CSS in the same shadow DOM.
|
||
|
||
#### What to include
|
||
|
||
Aim for a tight set of **5-10 components** that best represent the visual system:
|
||
|
||
- **Canonical primitives (always include if the project has them):** button (each variant as a separate component entry), input/text field, navigation, chip/tag, card.
|
||
- **Signature components (include if distinctive):** the recurring custom patterns that actually define the implemented system.
|
||
- **Skip the rest.** Utility components, form building blocks, wrapper layouts: not worth documenting unless visually distinctive.
|
||
|
||
If the project has **no component library yet** (bare landing page, new project), synthesize canonical primitives from the tokens using best-practice defaults consistent with the DESIGN.md's rules. Every `.impeccable/design.json` has *something* to render, even on day zero.
|
||
|
||
#### Tonal ramps
|
||
|
||
For each color token, generate an 8-step `tonalRamp` array: dark to light, same hue and chroma, stepped lightness from ~15% to ~95%. The panel renders this as a strip under the swatch. If the project already defines a tonal scale (Material `surface-container-low` family, Tailwind-style `blue-50..blue-900`), use those values. Otherwise synthesize in OKLCH.
|
||
|
||
#### Narrative mapping
|
||
|
||
Pull directly from the DESIGN.md you just wrote:
|
||
|
||
- `narrative.northStar` → the `**Creative North Star: "..."**` line from Overview
|
||
- `narrative.overview` → the philosophy paragraphs from Overview
|
||
- `narrative.keyCharacteristics` → the bulleted `**Key Characteristics:**` list
|
||
- `narrative.rules` → every `**The [Name] Rule.** [body]` across all sections, tagged with `section`
|
||
- `narrative.dos` / `narrative.donts` → the bullet lists from Do's and Don'ts verbatim
|
||
|
||
Do not reword. The panel shows these as secondary collapsible context; the same voice that's in the Markdown carries through.
|
||
|
||
### Step 5: Confirm and refine
|
||
|
||
1. Show the user the full DESIGN.md you wrote. Briefly highlight the non-obvious creative choices (descriptive color names, atmosphere language, named rules).
|
||
2. Mention that `.impeccable/design.json` was also written alongside; the live panel will now render this project's actual button/input/nav primitives instead of generic approximations.
|
||
3. Offer to refine any section: "Want me to revise a section, add component patterns I missed, or adjust the atmosphere language?"
|
||
|
||
Your own write is the freshest source; subsequent commands in this session don't need a reload.
|
||
|
||
## Seed mode
|
||
|
||
For projects with no visual system to extract yet. Produces a minimal, user-chosen scaffold, not a fabricated token spec.
|
||
|
||
PRODUCT.md is the prerequisite. If it is missing, load [init.md](init.md) and complete its product interview first. Do not create a visual identity without durable product context.
|
||
|
||
### Step 1: Decide the path, confirm seed mode, and ask for assets
|
||
|
||
The browser questionnaire asks color strategy and motion per surface and picks concrete typefaces and a type scale by eye, so whether it will run decides what the chat interview may ask. Decide the path **before the first question**, never after the interview:
|
||
|
||
- **The harness has native image generation** (Codex's `image_gen`, an equivalent MCP tool, or similar): the questionnaire path; the cues are generated directly at Step 4, no setup needed. This branch wins even when `.impeccable/.env` already holds an `IMAGE_GEN_API_KEY` or an earlier run in another harness left a wrapper script behind; those are fallbacks for keyless harnesses, not the preferred path. A native tool that **cannot generate** (zero credits, failed auth) counts as absent: fall through to the next branch without asking, and mention the swap in the final report.
|
||
- **No usable native path, key already in `.impeccable/.env`**: the questionnaire path, with no pause and no questions. Load [image-api.md](image-api.md) and use its shipped wrapper; it pre-answers everything this path has ever stopped to ask, including which provider the key belongs to.
|
||
- **No usable native path, no key**: pause. {{ask_instruction}} Ask whether the user wants generated visual cues to pick a palette by eye. *"I can generate a few small palette-and-mood images so you choose a direction visually instead of from descriptions. That needs an image-generation API key (FLUX and Google Nano Banana are supported out of the box; other providers work too), stored as `IMAGE_GEN_API_KEY` in `.impeccable/.env`. Add one, or skip straight to the chat interview?"* If a key arrives, write it to `.impeccable/.env` together with `IMAGE_GEN_PROVIDER` (`bfl` for FLUX, `gemini` for Nano Banana, the provider's own name for anything else; when the user does not say, let the wrapper infer it from the key). Confirm that file is listed in the project's `.gitignore` (add it if missing; a committed key is a leak), then load [image-api.md](image-api.md). Its shipped wrapper is the whole integration for the built-in providers; only a provider it does not know earns the project-local wrapper that file specifies. A key arriving makes this the questionnaire path.
|
||
- **The user opts out, or no key arrives**: the interview-only path. The assets ask below, the five questions in Step 3, then Steps 5-6 from the interview alone.
|
||
|
||
Then confirm seed mode and ask for assets, framed for the path:
|
||
|
||
- **Questionnaire path**: *"There's no existing visual system to scan. You'll pick the visual direction by eye in a browser questionnaire; before I generate its options, three quick things. First: if you have any visual assets (a logo, reference or product images, moodboards), drop them in or point me at the files. They're extra context that makes the first DESIGN.md seed more accurate. You can re-run `/impeccable document` once there's code, to capture the real tokens and components. OK?"*
|
||
- **Interview-only path**: *"There's no existing visual system to scan. I'll ask five quick questions to seed a starter DESIGN.md. First: if you have any visual assets (a logo, reference or product images, moodboards), drop them in or point me at the files. They'll ground the questions in what you already have. You can re-run `/impeccable document` once there's code, to capture the real tokens and components. OK?"*
|
||
|
||
Also glance for assets already in the project (`assets/`, `public/`, `brand/`, image files at the root); name anything found so the user can confirm it's relevant. Assets are optional: one ask, then proceed with whatever arrived.
|
||
|
||
If the user prefers to skip entirely, stop. No file.
|
||
|
||
### Step 2: Read the assets
|
||
|
||
Look at every asset provided (attached in chat or a file path) and record what it tells you, before writing the questions:
|
||
|
||
- **Logo**: sample the exact colors, note letterform character (geometric / humanist / serif) and temperature.
|
||
- **Reference / product images**: density, palette, type feel; what the user is drawn to.
|
||
- **Moodboards**: recurring hues, textures, era, register cues.
|
||
|
||
On the questionnaire path, the files themselves also feed the design context document the picker shows after the last question. When the user provided actual files (a logo, a mood board, a reference image), copy each one into `.impeccable/design-context/assets/`, keeping its filename. Record every staged file for Step 4's context write: it becomes an object entry in `context.json` `context.assets`, `{ "file": "<filename>", "kind": "logo" | "moodboard" | "reference", "note": "<one-line observation>" }`, where the note is what this step read off it. An observation with no file behind it stays a plain string entry, as before. On the interview-only path, stage nothing; the observations feed the questions and the seed alone.
|
||
|
||
These observations exist to sharpen Step 3. **No assets: skip straight to Step 3** with generic options.
|
||
|
||
### Step 3: The interview
|
||
|
||
Group each path's questions into one `AskUserQuestion` interaction. Options must be concrete. Keep skill vocabulary (seed, register, anti-reference) out of question text; ask for the thing in words the user would use. Ask like a magazine editor profiling the brand: curious and narrative, drawing out the feel the surface should carry.
|
||
|
||
**Questionnaire path: two questions, nothing more.** With Step 1's assets ask these are the whole chat interview; the questionnaire asks everything else by eye.
|
||
|
||
1. **Three named references.** Brands, products, printed objects. Not adjectives. When Step 2 produced observations, ground candidate names in them (references drawn from the moodboard's era).
|
||
2. **One anti-reference.** What the product should NOT feel like. Also named.
|
||
|
||
**Do not ask about color, typography, or motion here; the questionnaire owns them.** It asks color strategy and motion per surface and picks concrete typefaces and a type scale, so a chat answer would be asked again by eye and one of the two would be thrown away. Both answered, go straight to Step 4.
|
||
|
||
**Interview-only path: five questions.** When Step 2 produced observations, ground the options in them: offer the logo's sampled color as a hue anchor in Q1, a type direction that matches the letterforms in Q2, candidate named references drawn from the moodboard's era in Q4. The user should recognize their own material in the choices.
|
||
|
||
1. **Color strategy.** Pick one:
|
||
- Restrained: tinted neutrals + one accent ≤10%
|
||
- Committed: one saturated color carries 30–60% of the surface
|
||
- Full palette: 3–4 named color roles, each deliberate
|
||
- Drenched: the surface IS the color
|
||
|
||
Then: one hue family or anchor reference ("deep teal", "mustard", "Klim #ff4500 orange").
|
||
|
||
2. **Typography direction.** Pick one (specific fonts come later):
|
||
- Serif display + sans body
|
||
- Single sans (warm / technical / geometric / humanist; pick a feel)
|
||
- Display + mono
|
||
- Mono-forward
|
||
- Editorial script + sans
|
||
|
||
3. **Motion energy.** Pick one:
|
||
- Restrained: state changes only
|
||
- Responsive: feedback + transitions, no choreography
|
||
- Choreographed: orchestrated entrances, scroll-driven sequences
|
||
|
||
4. **Three named references.** Brands, products, printed objects. Not adjectives.
|
||
|
||
5. **One anti-reference.** What it should NOT feel like. Also named.
|
||
|
||
### Step 4: Launch the questionnaire (questionnaire path only)
|
||
|
||
**Interview-only path: skip this step.** Go to Step 5 and seed from the answers alone. Step 1 already settled the capability question; do not re-open it here.
|
||
|
||
On the questionnaire path, **stop and load [visual-cues.md](visual-cues.md)** and follow its pipeline; it owns everything from the one-line user announcement and the persona palette studio through generation, `cues.json`, and the picker pause. Do not restate its mechanics here or in chat. The picker's exit is the handoff: when the server exits 0 and `.impeccable/design-context/answers.json` lands, come back here and run Steps 5-6 with that file in hand.
|
||
|
||
### Step 5: Write seed DESIGN.md
|
||
|
||
Use the canonical section order from Scan mode. Populate what the interview, the assets, and the questionnaire answer; leave the rest as honest placeholders. The seed is a scaffold, not a fabricated spec, but a decision the user actually made in the picker is real and belongs in the file at full strength.
|
||
|
||
Mark the file as a seed with this comment as the first line of the markdown body, immediately after the frontmatter's closing `---` (the frontmatter must open the file or token parsers will not see it):
|
||
|
||
```markdown
|
||
<!-- SEED: established with the user before implementation; re-run /impeccable document once there's code to capture the actual tokens and components. -->
|
||
```
|
||
|
||
**Two seeds exist**, and which one you write depends on whether Step 4's picker ran:
|
||
|
||
**Interview-only seed** (the user opted out of generation, or no key arrived). Per-section guidance:
|
||
|
||
- **Overview**: Creative North Star and philosophy phrased from the answers (color strategy + motion energy + references). Reference the user's anti-reference directly.
|
||
- **Colors**: Color strategy as a Named Rule (e.g. *"The Drenched Rule. The surface IS the color."*). Hue family or anchor reference. Colors sampled from a provided logo are real; include them with exact values and note the source. Everything else stays `[to be resolved during implementation]`; those sampled anchors are the only hex this seed may carry.
|
||
- **Typography**: the direction the user picked (e.g. "Serif display + sans body"). No font names yet: `[font pairing to be chosen at implementation]`.
|
||
- **Layout** and **Shapes**: omit unless an asset or answer established a spatial or form preference; do not invent grids or corner language pre-implementation.
|
||
- **Elevation & Depth**: inferred from motion energy. Restrained/Responsive → flat by default; Choreographed → layered. One sentence.
|
||
- **Components**: omit entirely; no components exist yet.
|
||
- **Do's and Don'ts**: carry PRODUCT.md's anti-references directly plus the anti-reference named in Q5.
|
||
|
||
This seed writes a minimal frontmatter with `name` and `description` only; no colors, typography, rounded, spacing, or components yet.
|
||
|
||
**Questionnaire seed** (`.impeccable/design-context/answers.json` exists from this run). The user answered every screen by eye, so the seed carries their answers as decisions, not directions. **`_chosen` names the fields they actually set**: it holds a JSON-encoded array of per-surface keys, and a `<key>-<mode>` field missing from that array is a **preset** the picker minted when the surface was switched on, not an answer. Read the answers file plus the picked cue's palette entry in `.impeccable/visual-cues/cues.json` (`palette-source` names it), and map:
|
||
|
||
- **Frontmatter**: `name` and `description`, plus real `colors` (the four `palette-*` hex values under descriptive slugs; these are picked, not sampled) and real `typography` (`font-heading` and `font-body` are exact family names; give each role its family and weight intent, leave sizes for implementation). Derive the two text inks and record them under `colors` too: one near-black and one near-white, the pair the picker's previews already set their text in over these exact surfaces, each holding 4.5:1 against the grounds it will carry copy on, so a builder needing body-text contrast finds ink in the system instead of inventing a fifth color. Still no `rounded`, `spacing`, or `components`: the corner and spacing answers are qualitative, and nothing is built.
|
||
- **Overview**: Creative North Star and philosophy phrased from the questionnaire's color-strategy and motion answers plus the chat references; reference the user's anti-reference directly. Name the chosen surfaces (`surface-modes`) and what each is for. Movement stays here, after the North Star, but the questionnaire asks it of a landing page and a portfolio only, so write what the keys support:
|
||
- `motion-energy-<mode>` keys present, all agreeing: one philosophy sentence for the product, as before.
|
||
- Keys present and disagreeing: one sentence per surface, named (*"The landing page moves on state change only; the portfolio stages entrances and drives sequences on scroll."*). The bare `motion-energy` is the leading one of the two.
|
||
- **No `motion-energy` key at all**: the run has neither of those surfaces, so movement was never asked. Say nothing about it, and do not fill the gap from the register; this path's chat interview never asked about motion, so there is nothing to borrow. The next Scan-mode run reads the real transitions out of the code.
|
||
- **Colors**: the four roles with their picked hex, noting the cue they came from. Name the chosen cue by its slug and name its kept image at `.impeccable/design-context/cue.png`, so a later build opens the picture the palette came from instead of imagining it; note that the unpicked cue images stay in `.impeccable/visual-cues/` for later art direction. Then open the kept image and describe it into the same section, three or four sentences under a **Cue, in words:** lead: the physical material each palette role lives on in the picture (cloth, glass, enamel, paper), the light and its temperature, the surface finish and grain, and the one material move that makes the image itself. Name each color as it appears on its material; `#C92823` as soft matte wrapping cloth instructs an image model where the bare hex only tints. Generation prompts on this world restate this passage (new-work.md and visualize.md say where), so a seed that records only the cue's file path leaves the material world to the model's imagination. The **chosen** strategy becomes the Named Rule. When surfaces differ (`color-strategy-<mode>` keys), state each surface's strategy and which surface leads (the bare key's owner).
|
||
- **Typography**: the real pair by name, the pairing's character, and the type scale as a rule: `type-scale` names it, `type-scale-ratio` is the ratio (e.g. *"Major third: each heading step is 1.25x the last"*). Base size and exact steps stay `[resolved at implementation]`. A `font-heading-source` / `font-body-source` value means a user-provided font file; record where it lives.
|
||
- **Layout**: `boundary-style` (how sections separate) per surface when the `-<mode>` keys differ, plus `layout-structure` (how pages are composed), which the questionnaire asks of a landing page and a portfolio only. No invented grids beyond what the answers state.
|
||
- `layout-structure` present: one bare key and no `-<mode>` keys, so state it as a rule for the whole product rather than per surface.
|
||
- **No `layout-structure` key at all**: the run has neither of those surfaces, so composition was never asked. Say nothing about how strict the grid is, and let `boundary-style` carry the section.
|
||
- **Elevation & Depth**: `depth-style` per surface, stated directly; the questionnaire answered this, so do not re-infer it from motion energy.
|
||
- **Shapes**: `corner-style` per surface.
|
||
- **Components**: still omit; nothing exists yet.
|
||
- **Do's and Don'ts**: the interview-only guidance, plus a Do fixing the icon source: every icon comes from the chosen pack (`icon-pack-name`, license, URL), no mixed sets. When the interview staged brand files (`context.assets` object entries in `.impeccable/design-context/context.json`), add one Do per file naming its path under `.impeccable/design-context/assets/`, its kind, and its note; a staged logo is the product's real mark and the build uses the file itself.
|
||
|
||
Per-surface answers come back for every chosen surface, presets included, and a difference between surfaces is a decision the picker enforced, not an inconsistency to smooth over (the option lists differ per surface, so a pick one surface allows can be unavailable on another and that surface falls to its preset). **Write a preset as provisional**, on the surface's own line: name the value, say it is that surface's default because the surface was never configured, and keep it out of the Named Rules and out of every product-wide sentence. Naming an untouched preset as a rule invents a law the user never chose. Where all surfaces agree **and `_chosen` shows the agreement was picked**, state the answer once for the product. `motion-energy` and `layout-structure` are the two keys that can be missing entirely, since movement and composition are asked of a landing page and a portfolio only; [visual-cues.md](visual-cues.md) has the full contract.
|
||
|
||
Both seeds skip the `.impeccable/design.json` sidecar: nothing to render yet. Real tokens for sizes, spacing, and components land on the next Scan-mode run.
|
||
|
||
### Step 6: Confirm
|
||
|
||
1. Show the seed DESIGN.md. Call out that it is a seed (the marker is the literal commitment).
|
||
2. Tell the user: "Re-run `/impeccable document` once you have some code. That pass will extract real tokens and generate the sidecar."
|
||
3. On the questionnaire path, add one line: the interview is kept, and `{{command_prefix}}impeccable design-context` reopens the document, re-runs the questionnaire over these answers, or writes the context out for another tool. See [design-context.md](design-context.md).
|
||
|
||
Your own write is the freshest source; no reload needed.
|
||
|
||
When the questionnaire ran, the confirm is not the end of the turn: the design context document in the user's tab is live for edits through the session the picker forked. Follow the document edit loop in [visual-cues.md](visual-cues.md): poll, apply `edit_request`s to this same DESIGN.md, reply. A color the user changed in the tab before your seed write is already in `answers.json`; one changed after arrives as a `save_batch` event, its value already in the store and its description in DESIGN.md yours to bring in line.
|
||
|
||
## Style guidelines
|
||
|
||
- **Frontmatter first, prose second.** Tokens go in the YAML frontmatter; prose contextualizes them. Don't redefine a token value in two places; the frontmatter is normative.
|
||
- **Carry only durable product constraints.** A binding logo, identity asset, accessibility need, or brand commitment from PRODUCT.md may constrain DESIGN.md. Surface strategy stays in its surface brief.
|
||
- **Match the spec.** Use its eight canonical sections in order and omit any that are irrelevant. Put motion guidance with the world or component it affects rather than creating a token group the schema does not support.
|
||
- **Descriptive > technical**: "Gently curved edges (8px radius)" > "rounded-lg". Include the technical value in parens, lead with the description.
|
||
- **Functional > decorative**: for each token, explain WHERE and WHY it's used, not just WHAT it is.
|
||
- **Exact values in parens**: hex codes, px/rem values, font weights; always the number in parens alongside the description.
|
||
- **Use Named Rules**: `**The [Name] Rule.** [short doctrine]`. These are memorable, citable, and much stickier for AI consumers than bullet lists. Stitch's own outputs use them heavily ("The No-Line Rule", "The Ghost Border Fallback"). Aim for 1-3 per section.
|
||
- **Be decisive where evidence is decisive.** Use hard language for actual invariants and softer language for provisional guidance.
|
||
- **Use concrete audit tests only when they are grounded in the observed system or a confirmed user decision.** A one-sentence test beats a paragraph of principle.
|
||
- **Reference PRODUCT.md selectively.** Product truth explains why the world fits; it does not supply page composition or a visual don't-list by default.
|
||
- **Group colors by role**, not by hex-order or hue-order. Primary / Secondary / Tertiary / Neutral is the spec ordering.
|
||
|
||
## Pitfalls
|
||
|
||
- Don't paste raw CSS class names. Translate to descriptive language.
|
||
- Don't extract every token. Stop at what's actually reused; one-offs pollute the system.
|
||
- Don't invent components that don't exist. If the project only has buttons and cards, only document those.
|
||
- Don't overwrite an existing DESIGN.md without asking.
|
||
- Don't duplicate content from PRODUCT.md. DESIGN.md is strictly visual.
|
||
- Don't replace canonical sections with near-synonyms. Put layout and responsive behavior in `Layout`; put motion with the affected world or component.
|
||
- Don't rename sections even slightly. "Colors" not "Color Palette & Roles". "Typography" not "Typography Rules". Tooling parsing depends on exact headers.
|
||
- Don't duplicate token values between frontmatter and prose. If a color is in `colors.primary` as hex, the prose can name it and describe its role but should not reassert a different hex. The frontmatter is normative.
|
||
- Don't invent frontmatter token groups outside Stitch's schema (no `motion:`, `breakpoints:`, `shadows:` at the top level). Stitch's Zod schema only accepts `colors`, `typography`, `rounded`, `spacing`, `components`. Anything else belongs in the sidecar's `extensions`.
|