1688 lines
79 KiB
XML
1688 lines
79 KiB
XML
<workflow>
|
||
<critical>The workflow execution engine is governed by: {project-root}/_bmad/core/tasks/workflow.xml</critical>
|
||
<critical>You MUST have already loaded and processed: {installed_path}/workflow.yaml</critical>
|
||
<critical>Communicate all responses in {communication_language}, tailored to {user_skill_level}, and generate all documents in {document_output_language}</critical>
|
||
|
||
<critical>CRITICAL MISSION: You are the Design System Generator. You bridge the gap between UX Design and Implementation.</critical>
|
||
<critical>Your purpose is to translate human-readable UX specifications into machine-readable DEV artifacts.</critical>
|
||
<critical>Strictly follow the Single Source of Truth (SSOT) principle: Design Tokens drive everything.</critical>
|
||
|
||
<step n="1" goal="Load Input Documents">
|
||
<!-- AC #1: File discovery and location - invoke discover_inputs protocol -->
|
||
<invoke-protocol name="discover_inputs" />
|
||
|
||
<!-- Set KB query domain -->
|
||
<action>Initialize missing_list to empty</action>
|
||
<action>Set domain to "ui-ux" for KB queries</action>
|
||
|
||
<!-- AC #2: Required file validation - ux-design-specification.md -->
|
||
<check if="ux_spec_content is empty">
|
||
<output>??UX Design Specification not found.</output>
|
||
<output>Please run the UX design workflow first to generate ux-design-specification.md</output>
|
||
<output>Expected location: {output_folder}/ux-design-specification.md</output>
|
||
<action>HALT</action>
|
||
</check>
|
||
<action if="ux_spec_content exists">Set ux_file_path to discovered path</action>
|
||
<action if="ux_spec_content exists">Set ux_spec to ux_spec_content</action>
|
||
|
||
<!-- AC #2: Optional file handling - architecture.md -->
|
||
<check if="architecture_content is empty">
|
||
<output>Architecture document not found.</output>
|
||
<output>Proceeding with defaults: Tailwind CSS + React</output>
|
||
<action>Set arch_file_path to null</action>
|
||
<action>Set default_framework to "tailwind"</action>
|
||
<action>Set default_component_lib to "react"</action>
|
||
</check>
|
||
<action if="architecture_content exists">Set arch_file_path to discovered path</action>
|
||
<action if="architecture_content exists">Set architecture to architecture_content</action>
|
||
|
||
<!-- AC #1: Validate KB path -->
|
||
<action>Set kb_path to {kb_path}</action>
|
||
<check if="kb_path is empty">
|
||
<action>Set kb_path to "{project-root}/resources/ui-ux-pro-max"</action>
|
||
</check>
|
||
<check if="kb_path directory exists">
|
||
<output>??UI/UX Pro Max Knowledge Base located at {kb_path}</output>
|
||
</check>
|
||
<check if="kb_path directory not exists">
|
||
<output>Knowledge Base not found at {kb_path}</output>
|
||
<output>KB replenishment will be unavailable - manual input may be required for missing tokens.</output>
|
||
</check>
|
||
|
||
<!-- Load Project Context (optional) -->
|
||
<check if="project_context found">
|
||
<action>Load project context from {project_context}</action>
|
||
</check>
|
||
<check if="project_context not found">
|
||
<output>Project context not found; proceeding without project-level constraints.</output>
|
||
</check>
|
||
|
||
<!-- Output discovery summary -->
|
||
<output>?? Input Discovery Complete:</output>
|
||
<output> - UX Specification: {ux_file_path}</output>
|
||
<output> - Architecture: {arch_file_path}</output>
|
||
<output> - Knowledge Base: {kb_path}</output>
|
||
</step>
|
||
|
||
|
||
<step n="2" goal="Parse Design Decisions and Build Token Structure">
|
||
<!--
|
||
Story 2.3: Extract Design Decisions
|
||
AC #1: Visual Foundation extraction
|
||
AC #2: Spacing & Layout extraction
|
||
AC #3: Shadow & Animation extraction
|
||
AC #4: Data structure and validation -->
|
||
|
||
<!-- ===== Initialize extracted_tokens object ===== -->
|
||
<action>Initialize extracted_tokens object with the following W3C Design Token structure:</action>
|
||
<output>
|
||
```json
|
||
{
|
||
"$schema": "https://design-tokens.github.io/community-group/format/",
|
||
"colors": {},
|
||
"typography": { "fontFamily": {}, "fontSize": {}, "fontWeight": {}, "lineHeight": {} },
|
||
"spacing": {},
|
||
"borderRadius": {},
|
||
"shadow": {},
|
||
"animation": { "duration": {}, "easing": {} },
|
||
"breakpoints": {}
|
||
}
|
||
```
|
||
</output>
|
||
|
||
<!-- ===== AC #1: Visual Foundation parsing ===== -->
|
||
<action>?? PARSE VISUAL FOUNDATION from {ux_spec}:</action>
|
||
|
||
<!-- Color extraction - detailed process -->
|
||
<action>Extract Color Palette:</action>
|
||
<action> 1. Search for sections matching: "Color Palette", "Colors", "Color", "Palette"</action>
|
||
<action> 2. Identify parsing format:</action>
|
||
<action> FORMAT A - Markdown table:</action>
|
||
<output>
|
||
| Role | Color | Usage |
|
||
|------|-------|-------|
|
||
| Primary | #0ea5e9 | Primary button |
|
||
</output>
|
||
<action> ??Parse: role=first column, value=second column</action>
|
||
<action> FORMAT B - List format:</action>
|
||
<output>
|
||
- Primary: #0ea5e9
|
||
- Secondary: hsl(217, 91%, 60%)
|
||
</output>
|
||
<action> ??Parse: regex pattern `^[-*]\s*(\w+):\s*(#[0-9a-fA-F]{3,8}|hsl\([^)]+\)|rgb\([^)]+\))`</action>
|
||
<action> FORMAT C - Inline definition:</action>
|
||
<output>Primary color is #0ea5e9 (Sky Blue)</output>
|
||
<action> ??Parse: regex pattern `(primary|secondary|accent|neutral|success|warning|error)\s*(color)?\s*(is|:)?\s*(#[0-9a-fA-F]{3,8}|hsl\([^)]+\))`</action>
|
||
<action> 3. For each color role found (primary, secondary, accent, neutral, success, warning, error):</action>
|
||
<action> - Normalize value to HEX format if HSL/RGB provided</action>
|
||
<action> - If scale provided (50-900), store all values</action>
|
||
<action> - If only base color, store as "500" and mark 50/100/900 as MISSING</action>
|
||
<action> - Store with structure: { "value": "#...", "type": "color", "source": "ux-spec" }</action>
|
||
<action> 4. EDGE CASE: No color section found ??Add all required colors to missing_list</action>
|
||
|
||
<!-- Typography extraction - detailed process -->
|
||
<action>Extract Typography:</action>
|
||
<action> 1. Search for sections matching: "Typography", "Font", "Typeface"</action>
|
||
<action> 2. Parse font families:</action>
|
||
<action> - Look for: "Heading font:", "Heading typeface:", "heading:", "h1-h6"</action>
|
||
<action> - Look for: "Body font:", "Body typeface:", "body:", "paragraph"</action>
|
||
<action> - Look for: "Mono font:", "Code font:", "Monospace font:", "mono:"</action>
|
||
<action> - Extract font-stack string (e.g., "'Inter', sans-serif")</action>
|
||
<action> - Store: { "value": "'Inter', sans-serif", "type": "fontFamily", "source": "ux-spec" }</action>
|
||
<action> 3. Parse font sizes (if explicitly defined):</action>
|
||
<action> - Look for scale definitions: xs, sm, base, lg, xl, 2xl, 3xl, 4xl</action>
|
||
<action> - Accept rem, em, or px values (normalize to rem)</action>
|
||
<action> 4. Parse font weights and line heights if found</action>
|
||
<action> 5. EDGE CASE: Only "Inter" mentioned without stack ??Use "'Inter', system-ui, sans-serif"</action>
|
||
<action> 6. EDGE CASE: No typography section ??Add fontFamily.heading, fontFamily.body to missing_list</action>
|
||
|
||
<!-- ===== AC #2: Spacing & Layout parsing ===== -->
|
||
<action>?? PARSE SPACING AND LAYOUT from {ux_spec}:</action>
|
||
|
||
<!-- Spacing system -->
|
||
<action>?? Extract Spacing Scale:</action>
|
||
<action> 1. Search for: "Spacing", "Space scale", "Layout"</action>
|
||
<action> 2. Identify base unit:</action>
|
||
<action> - Look for patterns: "4px base", "base unit: 4px", "0.25rem", "4pt grid"</action>
|
||
<action> - Default base: 4px (0.25rem) if not specified</action>
|
||
<action> 3. Parse scale values:</action>
|
||
<action> - Standard scale: 0, 1, 2, 3, 4, 6, 8, 12, 16</action>
|
||
<action> - Calculate: value * base_unit (e.g., 4 * 4px = 16px = 1rem)</action>
|
||
<action> - Store: { "value": "1rem", "type": "spacing", "source": "ux-spec" }</action>
|
||
<action> 4. EDGE CASE: No spacing defined ??Use default scale with base=4px, mark source as "default"</action>
|
||
|
||
<!-- Breakpoint definitions -->
|
||
<action>Extract Breakpoints:</action>
|
||
<action> 1. Search for: "Breakpoints", "Responsive", "Media queries"</action>
|
||
<action> 2. Parse breakpoint values:</action>
|
||
<action> - sm: usually 640px</action>
|
||
<action> - md: usually 768px</action>
|
||
<action> - lg: usually 1024px</action>
|
||
<action> - xl: usually 1280px</action>
|
||
<action> - 2xl: usually 1536px</action>
|
||
<action> 3. Store: { "value": "768px", "type": "dimension", "source": "ux-spec" }</action>
|
||
<action> 4. EDGE CASE: No breakpoints ??Use Tailwind defaults, mark source as "default"</action>
|
||
|
||
<!-- Border radius definitions -->
|
||
<action>Extract Border Radius:</action>
|
||
<action> 1. Search for: "Border Radius", "Radius", "Corners"</action>
|
||
<action> 2. Parse values: none, sm, md, lg, xl, full</action>
|
||
<action> 3. Accept rem, px values (normalize to rem)</action>
|
||
<action> 4. Store: { "value": "0.375rem", "type": "borderRadius", "source": "ux-spec" }</action>
|
||
<action> 5. EDGE CASE: No radius defined ??Add sm, md, lg to missing_list</action>
|
||
|
||
<!-- ===== AC #3: Shadow & Animation parsing ===== -->
|
||
<action>PARSE EFFECTS AND ANIMATION from {ux_spec}:</action>
|
||
|
||
<!-- Shadow definitions -->
|
||
<action>?? Extract Shadow Definitions:</action>
|
||
<action> 1. Search for: "Shadow", "Elevation", "Box shadow"</action>
|
||
<action> 2. Parse shadow levels: sm, md, lg, xl</action>
|
||
<action> 3. Extract full box-shadow syntax if provided:</action>
|
||
<action> Example: "0 4px 6px -1px rgba(0, 0, 0, 0.1)"</action>
|
||
<action> 4. Store: { "value": "0 4px 6px -1px rgba(0, 0, 0, 0.1)", "type": "boxShadow", "source": "ux-spec" }</action>
|
||
<action> 5. EDGE CASE: No shadow defined ??Add sm, md to missing_list</action>
|
||
|
||
<!-- Animation settings -->
|
||
<action>??Extract Animation Settings:</action>
|
||
<action> 1. Search for: "Animation", "Motion", "Transitions"</action>
|
||
<action> 2. Parse duration values:</action>
|
||
<action> - fast: typically 150ms</action>
|
||
<action> - normal: typically 300ms</action>
|
||
<action> - slow: typically 500ms</action>
|
||
<action> 3. Parse easing functions:</action>
|
||
<action> - default: cubic-bezier(0.4, 0, 0.2, 1)</action>
|
||
<action> - in, out, bounce variations</action>
|
||
<action> 4. Store duration: { "value": "150ms", "type": "duration", "source": "ux-spec" }</action>
|
||
<action> 5. Store easing: { "value": "cubic-bezier(0.4, 0, 0.2, 1)", "type": "cubicBezier", "source": "ux-spec" }</action>
|
||
<action> 6. EDGE CASE: No animation defined ??Add duration.fast, duration.normal, easing.default to missing_list</action>
|
||
|
||
<!-- ===== Architecture tech stack parsing ===== -->
|
||
<check if="architecture exists">
|
||
<action>Extract Tech Stack from {architecture}:</action>
|
||
<action> 1. UI Framework: search for "React", "Vue", "Next.js", "Nuxt", "Svelte", "Angular"</action>
|
||
<action> 2. CSS Framework: search for "Tailwind", "Bootstrap", "Bulma", "Foundation"</action>
|
||
<action> 3. Preprocessor: search for "SCSS", "Sass", "Less", "Stylus"</action>
|
||
<action> 4. Component Library: search for "shadcn/ui", "Chakra", "MUI", "Radix", "Headless UI"</action>
|
||
<action> 5. Store tech_stack object with keys: uiFramework, cssFramework, preprocessor, componentLib, source: "architecture"</action>
|
||
<action> 6. Normalize tech_stack values:</action>
|
||
<action> - Lowercase uiFramework, cssFramework, preprocessor, componentLib</action>
|
||
<action> - If any value missing/empty, set to "none"</action>
|
||
<output>Normalized tech_stack: {tech_stack}</output>
|
||
</check>
|
||
<check if="architecture not found">
|
||
<output>Architecture document not found; using defaults:</output>
|
||
<output> - UI Framework: React</output>
|
||
<output> - CSS Framework: Tailwind CSS</output>
|
||
<output> - Preprocessor: none</output>
|
||
<output> - Component Library: shadcn/ui</output>
|
||
<action>Set tech_stack = { uiFramework: "react", cssFramework: "tailwind", preprocessor: "none", componentLib: "shadcn", source: "default" }</action>
|
||
</check>
|
||
|
||
<!-- ===== AC #4: Output extracted_tokens structure ===== -->
|
||
<action>FINALIZE extracted_tokens structure:</action>
|
||
<output>
|
||
Complete extracted_tokens object should follow this W3C Design Token format:
|
||
```json
|
||
{
|
||
"$schema": "https://design-tokens.github.io/community-group/format/",
|
||
"colors": {
|
||
"primary": {
|
||
"50": { "value": "#f0f9ff", "type": "color", "source": "ux-spec" },
|
||
"500": { "value": "#0ea5e9", "type": "color", "source": "ux-spec" },
|
||
"900": { "value": "#0c4a6e", "type": "color", "source": "ux-spec" }
|
||
},
|
||
"secondary": { "...": "..." },
|
||
"neutral": { "...": "..." }
|
||
},
|
||
"typography": {
|
||
"fontFamily": {
|
||
"heading": { "value": "'Inter', sans-serif", "type": "fontFamily", "source": "ux-spec" },
|
||
"body": { "value": "'Inter', sans-serif", "type": "fontFamily", "source": "ux-spec" }
|
||
},
|
||
"fontSize": {
|
||
"base": { "value": "1rem", "type": "fontSize", "source": "ux-spec" }
|
||
}
|
||
},
|
||
"spacing": {
|
||
"0": { "value": "0", "type": "spacing", "source": "ux-spec" },
|
||
"1": { "value": "0.25rem", "type": "spacing", "source": "ux-spec" },
|
||
"4": { "value": "1rem", "type": "spacing", "source": "ux-spec" }
|
||
},
|
||
"borderRadius": {
|
||
"sm": { "value": "0.125rem", "type": "borderRadius", "source": "ux-spec" },
|
||
"md": { "value": "0.375rem", "type": "borderRadius", "source": "ux-spec" }
|
||
},
|
||
"shadow": {
|
||
"sm": { "value": "0 1px 2px 0 rgba(0,0,0,0.05)", "type": "boxShadow", "source": "ux-spec" },
|
||
"md": { "value": "0 4px 6px -1px rgba(0,0,0,0.1)", "type": "boxShadow", "source": "ux-spec" }
|
||
},
|
||
"animation": {
|
||
"duration": {
|
||
"fast": { "value": "150ms", "type": "duration", "source": "ux-spec" },
|
||
"normal": { "value": "300ms", "type": "duration", "source": "ux-spec" }
|
||
},
|
||
"easing": {
|
||
"default": { "value": "cubic-bezier(0.4, 0, 0.2, 1)", "type": "cubicBezier", "source": "ux-spec" }
|
||
}
|
||
},
|
||
"breakpoints": {
|
||
"sm": { "value": "640px", "type": "dimension", "source": "ux-spec" },
|
||
"md": { "value": "768px", "type": "dimension", "source": "ux-spec" },
|
||
"lg": { "value": "1024px", "type": "dimension", "source": "ux-spec" }
|
||
}
|
||
}
|
||
```
|
||
</output>
|
||
|
||
<output>?? Token Extraction Summary:</output>
|
||
<output> - Colors: {count extracted_tokens.colors} role(s) found</output>
|
||
<output> - Typography: {count extracted_tokens.typography} setting(s) found</output>
|
||
<output> - Spacing: {count extracted_tokens.spacing} value(s) found</output>
|
||
<output> - Border Radius: {count extracted_tokens.borderRadius} value(s) found</output>
|
||
<output> - Shadows: {count extracted_tokens.shadow} level(s) found</output>
|
||
<output> - Animation: {count extracted_tokens.animation} setting(s) found</output>
|
||
<output> - Breakpoints: {count extracted_tokens.breakpoints} breakpoint(s) found</output>
|
||
</step>
|
||
|
||
<step n="3" goal="Validate Completeness and Build Missing List">
|
||
<!--
|
||
Story 2.3: AC #4 - Data structure and validation; based on design-system-generator-spec.md Section 4.2 required token list
|
||
-->
|
||
|
||
<action>?? VALIDATE extracted_tokens against required token checklist:</action>
|
||
<action>Initialize validation_errors = [] and missing_list = []</action>
|
||
|
||
<!-- ===== Validation helper definition ===== -->
|
||
<action>Define validation helper: isValidToken(token) =</action>
|
||
<action> - token !== null</action>
|
||
<action> - token !== undefined</action>
|
||
<action> - token.value !== null && token.value !== ""</action>
|
||
<action> - token.type exists</action>
|
||
<action> - Return true if all conditions pass</action>
|
||
|
||
<!-- ===== Color validation (REQUIRED) - per spec 4.2 ===== -->
|
||
<action>VALIDATE Colors (REQUIRED per spec 4.2):</action>
|
||
<action> CHECK: extracted_tokens.colors exists and is not empty object</action>
|
||
|
||
<!-- Primary: requires 50, 100, 500, 600, 700, 900 -->
|
||
<action> For role = "primary":</action>
|
||
<action> - CHECK: colors.primary exists as object</action>
|
||
<action> - For each required scale in [50, 100, 500, 600, 700, 900]:</action>
|
||
<action> - CHECK: colors.primary.{scale} exists and isValidToken()</action>
|
||
<action> - IF MISSING: Add { category: "colors", key: "primary.{scale}", priority: "required" }</action>
|
||
|
||
<!-- Secondary, Accent, Neutral, Success, Warning, Error: each requires 500, 600, 700 -->
|
||
<action> For each required role in [secondary, accent, neutral, success, warning, error]:</action>
|
||
<action> - CHECK: colors.{role} exists as object</action>
|
||
<action> - For each required scale in [500, 600, 700]:</action>
|
||
<action> - CHECK: colors.{role}.{scale} exists and isValidToken()</action>
|
||
<action> - IF MISSING: Add { category: "colors", key: "{role}.{scale}", priority: "required" }</action>
|
||
|
||
<!-- ===== Typography validation (REQUIRED) ===== -->
|
||
<action>VALIDATE Typography (REQUIRED):</action>
|
||
<action> CHECK: extracted_tokens.typography exists</action>
|
||
<action> Required checks:</action>
|
||
<action> 1. typography.fontFamily.heading:</action>
|
||
<action> - IF null/undefined/empty: Add to missing_list</action>
|
||
<output>
|
||
{ "category": "typography", "key": "fontFamily.heading", "priority": "required" }
|
||
</output>
|
||
<action> 2. typography.fontFamily.body:</action>
|
||
<action> - IF null/undefined/empty: Add to missing_list</action>
|
||
<output>
|
||
{ "category": "typography", "key": "fontFamily.body", "priority": "required" }
|
||
</output>
|
||
<action> 3. typography.fontSize.base:</action>
|
||
<action> - IF null/undefined/empty: Add to missing_list</action>
|
||
<output>
|
||
{ "category": "typography", "key": "fontSize.base", "priority": "required" }
|
||
</output>
|
||
|
||
<!-- ===== Spacing validation (REQUIRED) - per spec 4.2 ===== -->
|
||
<action>VALIDATE Spacing (REQUIRED per spec 4.2):</action>
|
||
<action> CHECK: extracted_tokens.spacing exists</action>
|
||
<action> For each required key in [0, 1, 2, 3, 4, 6, 8, 12, 16]:</action>
|
||
<action> - CHECK: spacing[key] exists and isValidToken()</action>
|
||
<action> - IF MISSING: Add { category: "spacing", key: "{key}", priority: "required" }</action>
|
||
|
||
<!-- ===== Border Radius validation (REQUIRED) - per spec 4.2 ===== -->
|
||
<action>VALIDATE Border Radius (REQUIRED per spec 4.2):</action>
|
||
<action> CHECK: extracted_tokens.borderRadius exists</action>
|
||
<action> For each required key in [none, sm, md, lg, xl, full]:</action>
|
||
<action> - CHECK: borderRadius[key] exists and isValidToken()</action>
|
||
<action> - IF MISSING: Add { category: "borderRadius", key: "{key}", priority: "required" }</action>
|
||
|
||
<!-- ===== Shadow validation (REQUIRED) - per spec 4.2 ===== -->
|
||
<action>VALIDATE Shadow (REQUIRED per spec 4.2):</action>
|
||
<action> CHECK: extracted_tokens.shadow exists</action>
|
||
<action> For each required key in [sm, md, lg, xl]:</action>
|
||
<action> - CHECK: shadow[key] exists and isValidToken()</action>
|
||
<action> - CHECK: shadow[key].value contains valid box-shadow syntax</action>
|
||
<action> - IF MISSING: Add { category: "shadow", key: "{key}", priority: "required" }</action>
|
||
|
||
<!-- ===== Animation validation (REQUIRED) ===== -->
|
||
<action>??VALIDATE Animation (REQUIRED):</action>
|
||
<action> CHECK: extracted_tokens.animation exists</action>
|
||
<action> Required checks:</action>
|
||
<action> 1. animation.duration.fast:</action>
|
||
<action> - CHECK: exists and value matches pattern /^\d+m?s$/</action>
|
||
<action> - IF MISSING: Add { category: "animation", key: "duration.fast", priority: "required" }</action>
|
||
<action> 2. animation.duration.normal:</action>
|
||
<action> - CHECK: exists and value matches pattern /^\d+m?s$/</action>
|
||
<action> - IF MISSING: Add { category: "animation", key: "duration.normal", priority: "required" }</action>
|
||
<action> 3. animation.easing.default:</action>
|
||
<action> - CHECK: exists and value contains "cubic-bezier" or valid keyword</action>
|
||
<action> - IF MISSING: Add { category: "animation", key: "easing.default", priority: "required" }</action>
|
||
|
||
<!-- ===== Breakpoint validation (REQUIRED) - per spec 4.2 ===== -->
|
||
<action>VALIDATE Breakpoints (REQUIRED per spec 4.2):</action>
|
||
<action> CHECK: extracted_tokens.breakpoints exists</action>
|
||
<action> For each required key in [sm, md, lg, xl, 2xl]:</action>
|
||
<action> - CHECK: breakpoints[key] exists and value matches /^\d+px$/</action>
|
||
<action> - IF MISSING: Add { category: "breakpoints", key: "{key}", priority: "required" }</action>
|
||
|
||
<!-- ===== Validation result output ===== -->
|
||
<action>?? Generate Validation Report:</action>
|
||
<action>Set required_missing = missing_list.filter(item => item.priority === "required")</action>
|
||
<action>Set recommended_missing = missing_list.filter(item => item.priority === "recommended")</action>
|
||
|
||
<check if="required_missing.length === 0">
|
||
<output>??All REQUIRED tokens validated successfully!</output>
|
||
<action>Set validation_status = "complete"</action>
|
||
</check>
|
||
<check if="required_missing.length > 0">
|
||
<output>??VALIDATION FAILED: {required_missing.length} required token(s) missing:</output>
|
||
<output>
|
||
```
|
||
Required Missing Tokens:
|
||
{required_missing.map(m => ` - [${m.category}] ${m.key}`).join('\n')}
|
||
```
|
||
</output>
|
||
<action>Set validation_status = "incomplete"</action>
|
||
</check>
|
||
|
||
<check if="recommended_missing.length > 0">
|
||
<output>{recommended_missing.length} recommended token(s) missing (will use defaults):</output>
|
||
<output>
|
||
```
|
||
Recommended Missing:
|
||
{recommended_missing.map(m => ` - [${m.category}] ${m.key}`).join('\n')}
|
||
```
|
||
</output>
|
||
</check>
|
||
|
||
<action>Store missing_list for Step 4 KB replenishment</action>
|
||
</step>
|
||
|
||
<step n="4" goal="Replenish Missing Info with KB and User Input">
|
||
<!--
|
||
Story 2.4: Fill Missing with KB
|
||
AC #1: KB auto-replenishment
|
||
AC #2: User input replenishment
|
||
AC #3: Replenishment summary confirmation
|
||
-->
|
||
|
||
<action>Initialize replenished_items = []</action>
|
||
<action>Initialize kb_query_executed = false</action>
|
||
<action>Initialize kb_query_failed = false</action>
|
||
|
||
<!-- ===== Step 4.1: Extract style keywords (AC #1 prerequisite) ===== -->
|
||
<action>EXTRACT STYLE KEYWORDS from {ux_spec} for KB queries:</action>
|
||
<action> 1. Search for sections: "Design Philosophy", "Design Principles", "Style", "Visual Direction"</action>
|
||
<action> 2. Extract style keywords using patterns:</action>
|
||
<action> - Look for adjectives: modern, minimalist, playful, professional, bold, elegant, etc.</action>
|
||
<action> - Look for style references: Bauhaus, Material, Apple-like, etc.</action>
|
||
<action> - Look for industry hints: SaaS, e-commerce, portfolio, dashboard, etc.</action>
|
||
<action> 3. Build {style} variable from found keywords (fallback: "modern professional")</action>
|
||
<action> 4. Store {industry} from project context or infer from ux_spec content</action>
|
||
<action> 5. Store {product_type} from project context or infer from ux_spec content</action>
|
||
|
||
<check if="style keywords not found">
|
||
<action>Set {style} = "modern professional"</action>
|
||
<output>No explicit style keywords found in UX Spec; using fallback: "modern professional"</output>
|
||
</check>
|
||
<check if="industry not found">
|
||
<action>Set {industry} = "general"</action>
|
||
<output>No industry context found; using fallback: "general"</output>
|
||
</check>
|
||
<output>?? Style Context for KB Queries:</output>
|
||
<output> - Style: {style}</output>
|
||
<output> - Industry: {industry}</output>
|
||
<output> - Product Type: {product_type}</output>
|
||
|
||
<!-- ===== MED-04 fix: define getDefaultSuggestion helper ===== -->
|
||
<action>Define helper function getDefaultSuggestion(item):</action>
|
||
<action> switch(item.category):</action>
|
||
<action> case "colors": return "#0ea5e9" (or appropriate scale value for role)</action>
|
||
<action> case "typography": return "'Inter', system-ui, sans-serif"</action>
|
||
<action> case "spacing": return "{item.key * 0.25}rem" (4px base grid)</action>
|
||
<action> case "borderRadius": return sm=0.125rem, md=0.375rem, lg=0.5rem</action>
|
||
<action> case "shadow": return Tailwind default shadows</action>
|
||
<action> case "animation": return fast=150ms, normal=300ms, default easing</action>
|
||
<action> case "breakpoints": return Tailwind defaults (sm=640px, md=768px, etc.)</action>
|
||
<action> End function definition</action>
|
||
|
||
<!-- ===== HIGH-02 fix: define setNestedToken helper for dot-path keys ===== -->
|
||
<action>Define helper function setNestedToken(obj, keyPath, tokenValue):</action>
|
||
<action> 1. Split keyPath by "." (e.g., "fontFamily.heading" ??["fontFamily", "heading"])</action>
|
||
<action> 2. Traverse obj, creating nested objects as needed</action>
|
||
<action> 3. Set final key to tokenValue</action>
|
||
<action> Example: setNestedToken(typography, "fontFamily.heading", {value, type, source})</action>
|
||
<action> ??typography.fontFamily.heading = {value, type, source}</action>
|
||
<action> End function definition</action>
|
||
|
||
<!-- ===== Step 4.2: Categorized KB queries (AC #1) ===== -->
|
||
<check if="missing_list not empty">
|
||
<action>CATEGORIZE missing_list by token type:</action>
|
||
<action> - color_missing = missing_list.filter(m => m.category === "colors")</action>
|
||
<action> - typography_missing = missing_list.filter(m => m.category === "typography")</action>
|
||
<action> - spacing_missing = missing_list.filter(m => m.category === "spacing" || m.category === "borderRadius")</action>
|
||
<action> - shadow_missing = missing_list.filter(m => m.category === "shadow")</action>
|
||
<action> - animation_missing = missing_list.filter(m => m.category === "animation")</action>
|
||
<action> - breakpoints_missing = missing_list.filter(m => m.category === "breakpoints")</action>
|
||
|
||
<!-- ===== Color KB queries (MED-01, MED-02 fixes) ===== -->
|
||
<check if="color_missing.length > 0 AND kb_path directory exists">
|
||
<output>Searching KB for colors...</output>
|
||
<action>Set kb_query_executed = true</action>
|
||
<action>Execute query: python {kb_path}/scripts/search.py "{style} {industry} color palette" --domain color --json</action>
|
||
|
||
<!-- MED-01 fix: error handling - remove reference to undefined remaining_missing -->
|
||
<check if="script execution fails OR returns non-zero exit code">
|
||
<action>Set kb_query_failed = true</action>
|
||
<output>KB search script failed - colors will remain in color_missing for CSV fallback</output>
|
||
<action>Log error details for debugging</action>
|
||
<action>Skip KB color processing, proceed to next category</action>
|
||
</check>
|
||
<check if="JSON parse fails OR results is empty">
|
||
<output>No color suggestions found in KB for "{style}" style</output>
|
||
<action>Continue without KB color suggestions</action>
|
||
</check>
|
||
|
||
<action>Parse JSON result with structure:</action>
|
||
<output>
|
||
```json
|
||
{
|
||
"results": [
|
||
{ "role": "primary", "value": "#0ea5e9", "scale": { "50": "...", "500": "...", "900": "..." } },
|
||
{ "role": "secondary", "value": "#6366f1" }
|
||
]
|
||
}
|
||
```
|
||
</output>
|
||
<action>For each color found in KB results:</action>
|
||
<!-- Color format conversion -->
|
||
<action> - Normalize value to HEX format:</action>
|
||
<action> If HSL: hsl(h, s%, l%) ??Convert using: R,G,B = hslToRgb(h/360, s/100, l/100), then #RRGGBB</action>
|
||
<action> If RGB: rgb(r, g, b) ??Convert to #RRGGBB using hex encoding</action>
|
||
<action> If already HEX: validate format #RGB or #RRGGBB or #RRGGBBAA</action>
|
||
<!-- HIGH-01 fix: write color tokens into nested structure -->
|
||
<action> - Store with proper nesting:</action>
|
||
<action> - Ensure extracted_tokens.colors[role] exists as an object</action>
|
||
<action> If scale provided: extracted_tokens.colors[role][scaleKey] = { value, type: "color", source: "kb-suggestion" }</action>
|
||
<action> If single value: extracted_tokens.colors[role]["500"] = { value, type: "color", source: "kb-suggestion" }</action>
|
||
<action> - Remove from color_missing list</action>
|
||
<action> - Append to replenished_items with source: "kb-suggestion"</action>
|
||
<output> ??Filled {count} color tokens from KB</output>
|
||
</check>
|
||
|
||
<!-- ===== Typography KB queries (LOW-01 fix: standardize query string) ===== -->
|
||
<check if="typography_missing.length > 0 AND kb_path directory exists">
|
||
<output>Searching KB for typography...</output>
|
||
<!-- LOW-01 fix: standardize Execute query format -->
|
||
<action>Set kb_query_executed = true</action>
|
||
<action>Execute: python {kb_path}/scripts/search.py "{style} typography" --domain typography --json</action>
|
||
<check if="script execution fails OR JSON parse fails">
|
||
<action>Set kb_query_failed = true</action>
|
||
<output>KB search script failed - typography will remain in typography_missing for CSV fallback</output>
|
||
</check>
|
||
<action>Parse JSON result:</action>
|
||
<output>
|
||
```json
|
||
{
|
||
"results": [
|
||
{ "role": "heading", "fontFamily": "'Inter', sans-serif" },
|
||
{ "role": "body", "fontFamily": "'Inter', sans-serif" }
|
||
]
|
||
}
|
||
```
|
||
</output>
|
||
<action>For each typography setting found:</action>
|
||
<action> - Store: extracted_tokens.typography.fontFamily[role] = { value, type: "fontFamily", source: "kb-suggestion" }</action>
|
||
<action> - Remove from typography_missing list</action>
|
||
<action> - Append to replenished_items with source: "kb-suggestion"</action>
|
||
<output> ??Filled {count} typography tokens from KB</output>
|
||
</check>
|
||
|
||
<!-- ===== Spacing/Radius KB queries ===== -->
|
||
<check if="spacing_missing.length > 0 AND kb_path directory exists">
|
||
<output>?? Searching KB for spacing and radius...</output>
|
||
<action>Set kb_query_executed = true</action>
|
||
<action>Execute: python {kb_path}/scripts/search.py "{style} spacing system" --domain spacing --json</action>
|
||
<check if="script execution fails OR JSON parse fails">
|
||
<action>Set kb_query_failed = true</action>
|
||
<output>KB search script failed - spacing/radius will remain in spacing_missing for CSV fallback</output>
|
||
</check>
|
||
<action>Parse JSON result and apply Tailwind-compatible scale if found</action>
|
||
<action>For spacing values: store with type: "spacing", source: "kb-suggestion"</action>
|
||
<action>For borderRadius values: store with type: "borderRadius", source: "kb-suggestion"</action>
|
||
<action>Remove filled items from spacing_missing list</action>
|
||
<action>Append filled spacing/radius items to replenished_items with source: "kb-suggestion"</action>
|
||
<output> ??Filled {count} spacing/radius tokens from KB</output>
|
||
</check>
|
||
|
||
<!-- ===== Shadow KB queries ===== -->
|
||
<check if="shadow_missing.length > 0 AND kb_path directory exists">
|
||
<output>?? Searching KB for shadows...</output>
|
||
<action>Set kb_query_executed = true</action>
|
||
<action>Execute: python {kb_path}/scripts/search.py "{style} elevation shadow" --domain elevation --json</action>
|
||
<check if="script execution fails OR JSON parse fails">
|
||
<action>Set kb_query_failed = true</action>
|
||
<output>KB search script failed - shadows will remain in shadow_missing for CSV fallback</output>
|
||
</check>
|
||
<action>Parse JSON result with box-shadow syntax</action>
|
||
<action>For each shadow found:</action>
|
||
<action> - Store: extracted_tokens.shadow[level] = { value: "box-shadow-syntax", type: "boxShadow", source: "kb-suggestion" }</action>
|
||
<action> - Remove from shadow_missing list</action>
|
||
<action> - Append to replenished_items with source: "kb-suggestion"</action>
|
||
<output> ??Filled {count} shadow tokens from KB</output>
|
||
</check>
|
||
|
||
<!-- ===== Animation KB queries ===== -->
|
||
<check if="animation_missing.length > 0 AND kb_path directory exists">
|
||
<output>??Searching KB for animation settings...</output>
|
||
<action>Set kb_query_executed = true</action>
|
||
<action>Execute: python {kb_path}/scripts/search.py "{style} motion animation" --domain animation --json</action>
|
||
<check if="script execution fails OR JSON parse fails">
|
||
<action>Set kb_query_failed = true</action>
|
||
<output>KB search script failed - animation will remain in animation_missing for CSV fallback</output>
|
||
</check>
|
||
<action>Parse JSON result for duration and easing values</action>
|
||
<action>For duration values: store with type: "duration", source: "kb-suggestion"</action>
|
||
<action>For easing values: store with type: "cubicBezier", source: "kb-suggestion"</action>
|
||
<action>Remove filled items from animation_missing list</action>
|
||
<action>Append filled animation items to replenished_items with source: "kb-suggestion"</action>
|
||
<output> ??Filled {count} animation tokens from KB</output>
|
||
</check>
|
||
|
||
<!-- ===== MED-03 fix: Breakpoints KB queries ===== -->
|
||
<check if="breakpoints_missing.length > 0 AND kb_path directory exists">
|
||
<output>Searching KB for breakpoints...</output>
|
||
<action>Set kb_query_executed = true</action>
|
||
<action>Execute: python {kb_path}/scripts/search.py "{style} responsive breakpoints" --domain layout --json</action>
|
||
|
||
<check if="script execution fails OR JSON parse fails">
|
||
<action>Set kb_query_failed = true</action>
|
||
<output>No breakpoint suggestions found in KB - using Tailwind defaults</output>
|
||
<action>Apply Tailwind default breakpoints: sm=640px, md=768px, lg=1024px, xl=1280px, 2xl=1536px</action>
|
||
<action>Mark source as "default" for these values</action>
|
||
</check>
|
||
|
||
<action>Parse JSON result for breakpoint values</action>
|
||
<action>For each breakpoint found:</action>
|
||
<action> - Validate format matches /^\d+px$/</action>
|
||
<action> - Store: extracted_tokens.breakpoints[key] = { value, type: "dimension", source: "kb-suggestion" }</action>
|
||
<action> - Remove from breakpoints_missing list</action>
|
||
<action> - Append to replenished_items with source: "kb-suggestion"</action>
|
||
<output> ??Filled {count} breakpoint tokens from KB</output>
|
||
</check>
|
||
|
||
<!-- ===== If KB queries are unavailable, use user input and defaults ===== -->
|
||
<check if="kb_path directory not exists">
|
||
<output>Knowledge Base not found at {kb_path}</output>
|
||
<output> KB replenishment unavailable - will proceed with user input and defaults.</output>
|
||
</check>
|
||
</check>
|
||
|
||
<check if="kb_query_failed == true">
|
||
<output>KB search failed. Choose a recovery option before continuing:</output>
|
||
<output> [1] Retry KB search</output>
|
||
<output> [2] Update kb_path and retry</output>
|
||
<output> [3] Proceed with direct CSV lookup (requires explicit approval)</output>
|
||
<output>CSV fallback note (minimal):</output>
|
||
<output> - Reason: KB error summary</output>
|
||
<output> - CSV used: file + keyword</output>
|
||
<output> - Hit: row id + fields used</output>
|
||
<output> - Tokens: affected token list</output>
|
||
<action>HALT</action>
|
||
</check>
|
||
|
||
<check if="missing_list not empty AND kb_path directory exists AND kb_query_executed != true">
|
||
<output>KB search was not executed even though missing tokens exist and KB path is available.</output>
|
||
<output>Do not proceed with manual CSV lookup. Execute search.py for KB replenishment.</output>
|
||
<action>HALT</action>
|
||
</check>
|
||
|
||
<!-- ===== Step 4.3: User input replenishment (AC #2) ===== -->
|
||
<!-- MED-02 fix: initialize remaining_missing explicitly -->
|
||
<action>Initialize remaining_missing = []</action>
|
||
<action>Rebuild remaining_missing by concatenating all category lists after KB queries:</action>
|
||
<action> remaining_missing = [...color_missing, ...typography_missing, ...spacing_missing, ...shadow_missing, ...animation_missing, ...breakpoints_missing]</action>
|
||
<action>Filter remaining_missing by priority: required items first, then recommended</action>
|
||
|
||
<check if="remaining_missing.length > 0">
|
||
<output>The following design decisions require your input:</output>
|
||
<output>
|
||
| # | Category | Item | Suggested Value | Priority |
|
||
|---|------|------|--------|--------|
|
||
{remaining_missing.map((m, i) => `| ${i+1} | ${m.category} | ${m.key} | ${getDefaultSuggestion(m)} | ${m.priority} |`).join('\n')}
|
||
</output>
|
||
<output>
|
||
Input instructions:
|
||
- Provide values in order (comma or newline separated); enter "d" for default, "s" to skip (not recommended for required items), "d-all" for defaults. </output>
|
||
|
||
<ask>Please enter values or commands:</ask>
|
||
|
||
<!-- Validate user input -->
|
||
<action>For EACH user input value, validate format:</action>
|
||
<action> COLOR validation: /^#[0-9a-fA-F]{3,8}$/ OR /^hsl\([^)]+\)$/ OR /^rgb\([^)]+\)$/</action>
|
||
<action> FONT validation: non-empty string (any font family name accepted)</action>
|
||
<action> DIMENSION validation: /^\d+(\.\d+)?(px|rem|em)$/</action>
|
||
<action> DURATION validation: /^\d+(ms|s)$/</action>
|
||
<action> EASING validation: /^cubic-bezier\([^)]+\)$/ OR keywords (ease, ease-in, ease-out, linear)</action>
|
||
|
||
<check if="validation fails for any input">
|
||
<output>Format error:</output>
|
||
<output> Item: {item.key}</output>
|
||
<output> Input: {user_input}</output>
|
||
<output> Expected format: {expected_format}</output>
|
||
<output>
|
||
Options:
|
||
[r] Re-enter this item [d] Use default [s] Skip this item </output>
|
||
<ask>Please choose [r/d/s]:</ask>
|
||
<action>Handle user choice accordingly</action>
|
||
</check>
|
||
|
||
<check if="validation passes">
|
||
<!-- HIGH-02 fix: use setNestedToken to handle dot-path keys -->
|
||
<action>Use setNestedToken(extracted_tokens[category], key, { value: user_input, type: token_type, source: "user-input" })</action>
|
||
<action> This handles keys like "fontFamily.heading" ??typography.fontFamily.heading</action>
|
||
<action>Remove item from remaining_missing</action>
|
||
<action>Append to replenished_items with source: "user-input"</action>
|
||
</check>
|
||
|
||
<check if="user chooses 'd' or 'd-all'">
|
||
<action>Apply default values with source: "default"</action>
|
||
<action>Default values reference:</action>
|
||
<action> - colors.primary.500: "#0ea5e9" (Sky Blue)</action>
|
||
<action> - colors.secondary.500: "#6366f1" (Indigo)</action>
|
||
<action> - colors.neutral.500: "#64748b" (Slate)</action>
|
||
<action> - typography.fontFamily.heading: "'Inter', system-ui, sans-serif"</action>
|
||
<action> - typography.fontFamily.body: "'Inter', system-ui, sans-serif"</action>
|
||
<action> - typography.fontSize.base: "1rem"</action>
|
||
<action> - spacing.{n}: "{n * 0.25}rem" (4px base)</action>
|
||
<action> - borderRadius.sm: "0.125rem", md: "0.375rem", lg: "0.5rem"</action>
|
||
<action> - shadow.sm: "0 1px 2px 0 rgba(0, 0, 0, 0.05)"</action>
|
||
<action> - shadow.md: "0 4px 6px -1px rgba(0, 0, 0, 0.1)"</action>
|
||
<action> - animation.duration.fast: "150ms", normal: "300ms", slow: "500ms"</action>
|
||
<action> - animation.easing.default: "cubic-bezier(0.4, 0, 0.2, 1)"</action>
|
||
<action> - breakpoints: sm=640px, md=768px, lg=1024px, xl=1280px</action>
|
||
<action>Append default-filled items to replenished_items with source: "default"</action>
|
||
</check>
|
||
|
||
<check if="user chooses 's' (skip)">
|
||
<check if="item.priority === 'required'">
|
||
<output>Warning: skipping required item [{item.key}] may lead to incomplete output</output>
|
||
<ask>Confirm skip? [y/n]</ask>
|
||
</check>
|
||
<action>If confirmed skip: mark item as skipped, proceed without value</action>
|
||
</check>
|
||
</check>
|
||
|
||
<!-- ===== Step 4.4: Source statistics summary (AC #3) ===== -->
|
||
<action>CALCULATE source statistics:</action>
|
||
<action> - Count tokens where source === "ux-spec" ??store as ux_count</action>
|
||
<action> - Count tokens where source === "architecture" ??store as arch_count</action>
|
||
<action> - Count tokens where source === "kb-suggestion" ??store as kb_count</action>
|
||
<action> - Count tokens where source === "user-input" ??store as user_count</action>
|
||
<action> - Count tokens where source === "default" ??store as default_count</action>
|
||
<action> - Calculate total_count = ux_count + arch_count + kb_count + user_count + default_count</action>
|
||
<output>
|
||
**Token Source Statistics:**
|
||
|
||
| Source | Count | Percentage |
|
||
|------|------|--------|
|
||
| UX Spec | {ux_count} | {(ux_count/total_count*100).toFixed(1)}% |
|
||
| Architecture | {arch_count} | {(arch_count/total_count*100).toFixed(1)}% |
|
||
| KB Suggestion | {kb_count} | {(kb_count/total_count*100).toFixed(1)}% |
|
||
| User Input | {user_count} | {(user_count/total_count*100).toFixed(1)}% |
|
||
| Default | {default_count} | {(default_count/total_count*100).toFixed(1)}% |
|
||
| **Total** | **{total_count}** | **100%** |
|
||
</output>
|
||
|
||
<!-- Category summary -->
|
||
<output>
|
||
**Token Category Summary:**
|
||
|
||
| Category | Filled | Source Distribution |
|
||
|------|--------|----------|
|
||
| Colors | {colors_count} | {colors_sources} |
|
||
| Typography | {typography_count} | {typography_sources} |
|
||
| Spacing | {spacing_count} | {spacing_sources} |
|
||
| Border Radius | {radius_count} | {radius_sources} |
|
||
| Shadow | {shadow_count} | {shadow_sources} |
|
||
| Animation | {animation_count} | {animation_sources} |
|
||
| Breakpoints | {breakpoints_count} | {breakpoints_sources} |
|
||
</output>
|
||
<check if="replenished_items.length > 0">
|
||
<output>
|
||
**Replenished Item List:**
|
||
{replenished_items.map(i => `- [${i.source}] ${i.category}.${i.key}`).join('\n')}
|
||
</output>
|
||
</check>
|
||
|
||
<check if="kb_count > 0">
|
||
<output>
|
||
**KB Suggestion Notes:**
|
||
{kb_count} token values were filled from KB suggestions based on "{style}" style search results. You can adjust these values during Step 5 confirmation. </output>
|
||
</check>
|
||
|
||
<check if="default_count > total_count * 0.5">
|
||
<output>
|
||
**Note:** More than 50% of tokens use default values. Consider improving the UX design spec for more precise design system output. </output>
|
||
</check>
|
||
|
||
<!-- ===== H-02/H-03 Fix: KB tracking variables for Step 7 validation ===== -->
|
||
<action>FINALIZE KB TRACKING STATE for Step 7 validation:</action>
|
||
<action> Set kb_query_executed = true if ANY KB query was attempted in Step 4</action>
|
||
<action> Set kb_hits_found = true if ANY KB query returned non-empty results</action>
|
||
<action> Build kb_hits_for_item = {} tracking per-item KB hit status:</action>
|
||
<action> For each item in original missing_list:</action>
|
||
<action> kb_hits_for_item[item.category + "." + item.key] = (KB had hit for this item)</action>
|
||
<action> Calculate missing_with_kb_hits = count of items where kb_hits_for_item[key] === true</action>
|
||
<action> Store these variables for Step 7 KB reference rate calculation</action>
|
||
<output>
|
||
KB Tracking State:
|
||
- kb_query_executed: {kb_query_executed}
|
||
- kb_hits_found: {kb_hits_found}
|
||
- missing_with_kb_hits: {missing_with_kb_hits}
|
||
</output>
|
||
</step>
|
||
|
||
<step n="5" goal="Confirm Design Decisions">
|
||
<check if="missing_list not empty">
|
||
<output>Missing foundational design inputs detected: {missing_list}</output>
|
||
<output>These gaps can limit output quality. Please consider adding the following web design foundations to help DEV succeed:</output>
|
||
<output>- Color palette (primary/secondary/neutral scales)</output>
|
||
<output>- Typography (font families, sizes, line heights, weights)</output>
|
||
<output>- Spacing system (base unit and scale)</output>
|
||
<output>- Layout rules (grid, breakpoints, container widths)</output>
|
||
<output>- Component states (hover/active/disabled/focus)</output>
|
||
</check>
|
||
<output>?? Design System Summary</output>
|
||
<output>Provide a concise summary of extracted tokens and component specs.</output>
|
||
<ask>Proceed with file generation? [Y/n]</ask>
|
||
<check if="User says no">
|
||
<action>HALT</action>
|
||
</check>
|
||
</step>
|
||
|
||
<step n="6" goal="Generate Output Files (Universal Adapter Strategy)">
|
||
<!--
|
||
Universal Adapter Strategy:
|
||
This step dynamically adapts output based on tech_stack detected in Step 2.
|
||
- 6.1 & 6.2: Framework-agnostic SSOT (always generated)
|
||
- 6.3: Framework Configuration Adapter (Tailwind / SCSS / Generic based on tech_stack)
|
||
- 6.4: Dynamic Global Styles (Tailwind directives / SCSS reset / vanilla CSS reset)
|
||
- 6.5: Component Specs (framework-aware)
|
||
-->
|
||
|
||
<!-- ===== 6.1 design-tokens.json (SSOT - Framework Agnostic) ===== -->
|
||
<action>Build design_tokens_json from extracted_tokens using W3C Design Token format</action>
|
||
<action>Ensure "$schema" is set to "https://design-tokens.github.io/community-group/format/"</action>
|
||
<action>Preserve source field on every token (ux-spec/architecture/kb-suggestion/user-input/default)</action>
|
||
<action>Write file: {output_folder}/design-tokens.json</action>
|
||
<output>Created design-tokens.json (SSOT)</output>
|
||
|
||
<!-- ===== 6.2 theme.css (SSOT - Framework Agnostic CSS Variables) ===== -->
|
||
<action>Generate theme.css from design_tokens_json</action>
|
||
<action>Map tokens to CSS Custom Properties using naming rules:</action>
|
||
<action> - colors: --color-{role}-{scale}</action>
|
||
<action> - typography: --font-family-*, --font-size-*, --font-weight-*, --line-height-*</action>
|
||
<action> - spacing: --spacing-{key}</action>
|
||
<action> - borderRadius: --radius-{key}</action>
|
||
<action> - shadow: --shadow-{key}</action>
|
||
<action> - animation: --duration-*, --easing-*</action>
|
||
<action> - breakpoints: --breakpoint-{key}</action>
|
||
<action>Group variables by section with readable comments</action>
|
||
<action>Add [data-theme="dark"] overrides for background/surface/text/text-muted if available</action>
|
||
<action>Write file: {output_folder}/theme.css</action>
|
||
<output>Created theme.css (CSS Custom Properties)</output>
|
||
|
||
<!-- ===== 6.3 Framework Configuration Adapter ===== -->
|
||
<action>DETECT framework strategy from tech_stack (mutually exclusive):</action>
|
||
<action> If tech_stack.cssFramework includes "tailwind": set strategy = "tailwind"</action>
|
||
<action> Else if tech_stack.preprocessor includes "scss" OR "sass": set strategy = "scss"</action>
|
||
<action> Else: set strategy = "generic"</action>
|
||
<action> Set is_tailwind = (strategy === "tailwind")</action>
|
||
<action> Set is_scss = (strategy === "scss")</action>
|
||
<action> Set is_vanilla = (strategy === "generic")</action>
|
||
<output>Framework Detection: Strategy={strategy}, Tailwind={is_tailwind}, SCSS={is_scss}, Vanilla={is_vanilla}</output>
|
||
|
||
<!-- Strategy A: Tailwind Configuration -->
|
||
<check if="is_tailwind === true">
|
||
<output>Generating Tailwind configuration...</output>
|
||
<action>Generate tailwind.config.ts with theme.extend mapping:</action>
|
||
<action> 1. Import type { Config } from 'tailwindcss'</action>
|
||
<action> 2. Map colors using hsl(var(--color-{role}-{scale})) syntax for opacity modifier support:</action>
|
||
<output>
|
||
```typescript
|
||
// tailwind.config.ts - Auto-generated by Design System Generator
|
||
import type { Config } from 'tailwindcss'
|
||
|
||
const config: Config = {
|
||
content: [
|
||
'./src/**/*.{js,ts,jsx,tsx,mdx}',
|
||
'./app/**/*.{js,ts,jsx,tsx,mdx}',
|
||
'./components/**/*.{js,ts,jsx,tsx,mdx}',
|
||
],
|
||
theme: {
|
||
extend: {
|
||
colors: {
|
||
primary: {
|
||
50: 'hsl(var(--color-primary-50))',
|
||
100: 'hsl(var(--color-primary-100))',
|
||
500: 'hsl(var(--color-primary-500))',
|
||
600: 'hsl(var(--color-primary-600))',
|
||
700: 'hsl(var(--color-primary-700))',
|
||
900: 'hsl(var(--color-primary-900))',
|
||
DEFAULT: 'hsl(var(--color-primary-500))',
|
||
},
|
||
// ... map all color roles from design-tokens.json
|
||
},
|
||
fontFamily: {
|
||
heading: 'var(--font-family-heading)',
|
||
body: 'var(--font-family-body)',
|
||
mono: 'var(--font-family-mono)',
|
||
},
|
||
fontSize: {
|
||
// Map from --font-size-* variables
|
||
},
|
||
spacing: {
|
||
// Map from --spacing-* variables
|
||
},
|
||
borderRadius: {
|
||
// Map from --radius-* variables
|
||
},
|
||
boxShadow: {
|
||
// Map from --shadow-* variables
|
||
},
|
||
transitionDuration: {
|
||
fast: 'var(--duration-fast)',
|
||
normal: 'var(--duration-normal)',
|
||
slow: 'var(--duration-slow)',
|
||
},
|
||
transitionTimingFunction: {
|
||
default: 'var(--easing-default)',
|
||
},
|
||
},
|
||
},
|
||
plugins: [],
|
||
}
|
||
|
||
export default config
|
||
```
|
||
</output>
|
||
<action> CRITICAL: Use hsl(var(--color-name)) syntax to enable Tailwind opacity modifiers like bg-primary/50</action>
|
||
<action> For this to work, theme.css must store colors as HSL values without the hsl() wrapper:</action>
|
||
<action> --color-primary-500: 199 89% 48%; /* HSL values only, no hsl() */</action>
|
||
<action> If theme.css uses hex values, convert to HSL space-separated format in tailwind.config.ts generation</action>
|
||
<action>Write file: {output_folder}/tailwind.config.ts</action>
|
||
<output>Created tailwind.config.ts (Tailwind theme extension)</output>
|
||
</check>
|
||
|
||
<!-- Strategy B: SCSS/Sass Configuration -->
|
||
<check if="is_scss === true">
|
||
<output>Generating SCSS tokens...</output>
|
||
<action>Generate _tokens.scss with SCSS variable mappings:</action>
|
||
<output>
|
||
```scss
|
||
// _tokens.scss - Auto-generated by Design System Generator
|
||
// Import this file in your main SCSS file: @use 'tokens' as *;
|
||
|
||
// ===== Colors =====
|
||
$color-primary-50: var(--color-primary-50);
|
||
$color-primary-500: var(--color-primary-500);
|
||
// ... map all color scales
|
||
|
||
// ===== Typography =====
|
||
$font-family-heading: var(--font-family-heading);
|
||
$font-family-body: var(--font-family-body);
|
||
$font-family-mono: var(--font-family-mono);
|
||
|
||
// ===== Spacing =====
|
||
$spacing-0: var(--spacing-0);
|
||
$spacing-1: var(--spacing-1);
|
||
// ... map all spacing values
|
||
|
||
// ===== Border Radius =====
|
||
$radius-sm: var(--radius-sm);
|
||
$radius-md: var(--radius-md);
|
||
$radius-lg: var(--radius-lg);
|
||
|
||
// ===== Shadows =====
|
||
$shadow-sm: var(--shadow-sm);
|
||
$shadow-md: var(--shadow-md);
|
||
$shadow-lg: var(--shadow-lg);
|
||
|
||
// ===== Animation =====
|
||
$duration-fast: var(--duration-fast);
|
||
$duration-normal: var(--duration-normal);
|
||
$easing-default: var(--easing-default);
|
||
|
||
// ===== Mixins =====
|
||
@mixin respond-to($breakpoint) {
|
||
@if $breakpoint == sm { @media (min-width: 640px) { @content; } }
|
||
@else if $breakpoint == md { @media (min-width: 768px) { @content; } }
|
||
@else if $breakpoint == lg { @media (min-width: 1024px) { @content; } }
|
||
@else if $breakpoint == xl { @media (min-width: 1280px) { @content; } }
|
||
}
|
||
```
|
||
</output>
|
||
<action>Write file: {output_folder}/_tokens.scss</action>
|
||
<output>Created _tokens.scss (SCSS variables and mixins)</output>
|
||
</check>
|
||
|
||
<!-- Strategy C: Vanilla CSS (no additional framework config needed) -->
|
||
<check if="is_vanilla === true">
|
||
<output>Vanilla CSS detected - no framework configuration file needed</output>
|
||
<output>theme.css provides all CSS Custom Properties for direct use</output>
|
||
</check>
|
||
|
||
<!-- ===== 6.4 Dynamic Global Styles ===== -->
|
||
<action>Generate global styles with framework-appropriate content:</action>
|
||
|
||
<check if="is_tailwind === true">
|
||
<output>Generating Tailwind-compatible globals.css...</output>
|
||
<action>Generate globals.css with Tailwind directives:</action>
|
||
<output>
|
||
```css
|
||
/* globals.css - Tailwind Integration */
|
||
@tailwind base;
|
||
@tailwind components;
|
||
@tailwind utilities;
|
||
|
||
/* Import theme variables */
|
||
@import './theme.css';
|
||
|
||
/* ===== Base Layer Customizations ===== */
|
||
@layer base {
|
||
html {
|
||
font-family: var(--font-family-body);
|
||
line-height: var(--line-height-normal);
|
||
}
|
||
|
||
body {
|
||
background-color: var(--color-background);
|
||
color: var(--color-text);
|
||
}
|
||
|
||
:focus-visible {
|
||
outline: 2px solid var(--color-primary-500);
|
||
outline-offset: 2px;
|
||
}
|
||
}
|
||
|
||
/* ===== Reduced Motion ===== */
|
||
@media (prefers-reduced-motion: reduce) {
|
||
*, *::before, *::after {
|
||
animation-duration: 0.01ms !important;
|
||
transition-duration: 0.01ms !important;
|
||
}
|
||
}
|
||
```
|
||
</output>
|
||
</check>
|
||
|
||
<check if="is_scss === true">
|
||
<output>Generating SCSS globals.scss with reset...</output>
|
||
<action>Generate globals.scss with Modern CSS Reset and base typography:</action>
|
||
<output>
|
||
```scss
|
||
/* globals.scss - SCSS with Modern Reset */
|
||
@import './theme.css';
|
||
|
||
/* ===== Modern CSS Reset ===== */
|
||
*, *::before, *::after { box-sizing: border-box; }
|
||
* { margin: 0; padding: 0; }
|
||
|
||
html {
|
||
font-family: var(--font-family-body);
|
||
line-height: var(--line-height-normal);
|
||
}
|
||
|
||
body {
|
||
min-height: 100vh;
|
||
background-color: var(--color-background);
|
||
color: var(--color-text);
|
||
}
|
||
|
||
img, picture, video, canvas, svg {
|
||
display: block;
|
||
max-width: 100%;
|
||
}
|
||
|
||
input, button, textarea, select { font: inherit; }
|
||
p, h1, h2, h3, h4, h5, h6 { overflow-wrap: break-word; }
|
||
|
||
/* ===== Typography ===== */
|
||
h1, h2, h3, h4, h5, h6 {
|
||
font-family: var(--font-family-heading);
|
||
font-weight: var(--font-weight-semibold);
|
||
}
|
||
|
||
/* ===== Focus Styles (Accessibility) ===== */
|
||
:focus-visible {
|
||
outline: 2px solid var(--color-primary-500);
|
||
outline-offset: 2px;
|
||
}
|
||
|
||
/* ===== Reduced Motion ===== */
|
||
@media (prefers-reduced-motion: reduce) {
|
||
*, *::before, *::after {
|
||
animation-duration: 0.01ms !important;
|
||
transition-duration: 0.01ms !important;
|
||
}
|
||
}
|
||
```
|
||
</output>
|
||
</check>
|
||
|
||
<check if="is_vanilla === true">
|
||
<output>Generating vanilla CSS globals.css with reset...</output>
|
||
<action>Generate globals.css with Modern CSS Reset and base typography:</action>
|
||
<output>
|
||
```css
|
||
/* globals.css - Vanilla CSS with Modern Reset */
|
||
@import './theme.css';
|
||
|
||
/* ===== Modern CSS Reset ===== */
|
||
*, *::before, *::after { box-sizing: border-box; }
|
||
* { margin: 0; padding: 0; }
|
||
|
||
html {
|
||
font-family: var(--font-family-body);
|
||
line-height: var(--line-height-normal);
|
||
}
|
||
|
||
body {
|
||
min-height: 100vh;
|
||
background-color: var(--color-background);
|
||
color: var(--color-text);
|
||
}
|
||
|
||
img, picture, video, canvas, svg {
|
||
display: block;
|
||
max-width: 100%;
|
||
}
|
||
|
||
input, button, textarea, select { font: inherit; }
|
||
p, h1, h2, h3, h4, h5, h6 { overflow-wrap: break-word; }
|
||
|
||
/* ===== Typography ===== */
|
||
h1, h2, h3, h4, h5, h6 {
|
||
font-family: var(--font-family-heading);
|
||
font-weight: var(--font-weight-semibold);
|
||
}
|
||
|
||
/* ===== Focus Styles (Accessibility) ===== */
|
||
:focus-visible {
|
||
outline: 2px solid var(--color-primary-500);
|
||
outline-offset: 2px;
|
||
}
|
||
|
||
/* ===== Reduced Motion ===== */
|
||
@media (prefers-reduced-motion: reduce) {
|
||
*, *::before, *::after {
|
||
animation-duration: 0.01ms !important;
|
||
transition-duration: 0.01ms !important;
|
||
}
|
||
}
|
||
```
|
||
</output>
|
||
</check>
|
||
|
||
<action>Ensure all values reference CSS variables from theme.css (SSOT)</action>
|
||
<check if="is_scss === true">
|
||
<action>Write file: {output_folder}/globals.scss</action>
|
||
<output>Created globals.scss (Global Styles)</output>
|
||
</check>
|
||
<check if="is_scss === false">
|
||
<action>Write file: {output_folder}/globals.css</action>
|
||
<output>Created globals.css (Global Styles)</output>
|
||
</check>
|
||
|
||
<!-- ===== 6.5 component-specs.json ===== -->
|
||
<action>Determine componentLibrary from tech_stack.componentLib (default: "shadcn")</action>
|
||
<action>Determine effectsLibrary (default: "framer-motion", or "none" if not used)</action>
|
||
<action>Include "$schema" only if component-specs.schema.json is available (optional)</action>
|
||
<action>Build component-specs.json with meta and component definitions:</action>
|
||
<action> - Button: variants (primary/secondary/outline/ghost), sizes (sm/md/lg), states (disabled/loading), animation (hover/tap)</action>
|
||
<action> - Card: base + variants (elevated/outlined)</action>
|
||
<action> - Input: base + states (focus/error/disabled)</action>
|
||
<action>Write file: {output_folder}/component-specs.json</action>
|
||
<output>Created component-specs.json (Component Definitions)</output>
|
||
|
||
<!-- ===== 6.6 Generation Summary ===== -->
|
||
<output>
|
||
=== Step 6 Generation Summary ===
|
||
Framework Strategy: {strategy}
|
||
Generated Files: {strategy === 'tailwind' ? 'design-tokens.json, theme.css, tailwind.config.ts, globals.css, component-specs.json' : (strategy === 'scss' ? 'design-tokens.json, theme.css, _tokens.scss, globals.scss, component-specs.json' : 'design-tokens.json, theme.css, globals.css, component-specs.json')}
|
||
</output>
|
||
</step>
|
||
|
||
<step n="7" goal="Validate Outputs">
|
||
<!--
|
||
Story 2.7: Validation Mechanism
|
||
AC #1: Format Validation (JSON/CSS)
|
||
AC #2: Completeness Validation (spec 4.2)
|
||
AC #3: KB Reference Validation (conditional)
|
||
AC #4: Fallback Allowance
|
||
AC #5: Error Reporting
|
||
AC #6: Content Consistency
|
||
|
||
Risk Control Mapping:
|
||
- P0: KB not referenced @ AC #3, #4
|
||
- P1: Incomplete output @ AC #2
|
||
- P3: Invalid format @ AC #1
|
||
-->
|
||
|
||
<action>Initialize validation_report = { errors: [], warnings: [], metrics: {} }</action>
|
||
<action>Initialize format_errors = [], completeness_errors = [], kb_errors = [], consistency_errors = []</action>
|
||
|
||
<!-- ===== 7.1 JSON Syntax Validation (AC #1) ===== -->
|
||
<action>📋 VALIDATE JSON SYNTAX:</action>
|
||
|
||
<!-- design-tokens.json validation -->
|
||
<action>Attempt to parse design-tokens.json as JSON:</action>
|
||
<check if="JSON.parse(design_tokens_content) throws error">
|
||
<action>Extract error line number and message from parse error</action>
|
||
<action>Add to format_errors:</action>
|
||
<output>
|
||
{
|
||
"type": "SYNTAX",
|
||
"file": "design-tokens.json",
|
||
"location": "line {error_line}",
|
||
"error": "{error_message}",
|
||
"suggestion": "Check for missing commas, brackets, or invalid characters at the specified location"
|
||
}
|
||
</output>
|
||
</check>
|
||
<check if="JSON.parse succeeds">
|
||
<action>Set design_tokens_json_valid = true</action>
|
||
<output>✅ design-tokens.json: JSON syntax valid</output>
|
||
</check>
|
||
|
||
<!-- component-specs.json validation -->
|
||
<action>Attempt to parse component-specs.json as JSON:</action>
|
||
<check if="JSON.parse(component_specs_content) throws error">
|
||
<action>Extract error line number and message from parse error</action>
|
||
<action>Add to format_errors:</action>
|
||
<output>
|
||
{
|
||
"type": "SYNTAX",
|
||
"file": "component-specs.json",
|
||
"location": "line {error_line}",
|
||
"error": "{error_message}",
|
||
"suggestion": "Check for missing commas, brackets, or invalid characters at the specified location"
|
||
}
|
||
</output>
|
||
</check>
|
||
<check if="JSON.parse succeeds">
|
||
<action>Set component_specs_json_valid = true</action>
|
||
<output>✅ component-specs.json: JSON syntax valid</output>
|
||
</check>
|
||
|
||
<!-- ===== 7.2 CSS Syntax Validation (AC #1) ===== -->
|
||
<action>📋 VALIDATE CSS SYNTAX:</action>
|
||
|
||
<!-- theme.css validation -->
|
||
<action>Validate theme.css CSS syntax:</action>
|
||
<action> 1. Check bracket balance: count of '{' must equal count of '}'</action>
|
||
<action> 2. Check property format: each line inside {} should match pattern /^\s*[\w-]+:\s*.+;?\s*$/ or be a comment</action>
|
||
<action> 3. Check for unclosed comments: /* must have matching */</action>
|
||
<action> 4. Check for invalid selectors: selectors should not contain unescaped special characters</action>
|
||
<check if="bracket count mismatch">
|
||
<action>Add to format_errors:</action>
|
||
<output>
|
||
{
|
||
"type": "SYNTAX",
|
||
"file": "theme.css",
|
||
"location": "file-level",
|
||
"error": "Unbalanced brackets: {open_count} opening vs {close_count} closing",
|
||
"suggestion": "Check for missing or extra braces"
|
||
}
|
||
</output>
|
||
</check>
|
||
<check if="invalid property format detected">
|
||
<action>Add to format_errors:</action>
|
||
<output>
|
||
{
|
||
"type": "SYNTAX",
|
||
"file": "theme.css",
|
||
"location": "line {line_number}",
|
||
"error": "Invalid CSS property format: '{invalid_line}'",
|
||
"suggestion": "Ensure format is 'property: value;'"
|
||
}
|
||
</output>
|
||
</check>
|
||
<check if="theme.css syntax valid">
|
||
<action>Set theme_css_valid = true</action>
|
||
<output>✅ theme.css: CSS syntax valid</output>
|
||
</check>
|
||
|
||
<!-- global styles validation (M-02 fix: detailed error reporting with line numbers) -->
|
||
<action>Validate global styles syntax with detailed location tracking:</action>
|
||
<action> 1. Initialize globals_line_number = 0</action>
|
||
<action> 2. For each line in {strategy === 'scss' ? 'globals.scss' : 'globals.css'}:</action>
|
||
<action> globals_line_number++</action>
|
||
<action> Track bracket_depth: '{' increases, '}' decreases</action>
|
||
<action> 3. Bracket balance check: if final bracket_depth != 0, report unbalanced</action>
|
||
<action> 4. Comment closure check: verify /* has matching */</action>
|
||
<action> 5. If strategy === "scss": skip strict property format checks (allow SCSS variables/nesting)</action>
|
||
<action> 6. If strategy !== "scss": validate each property line matches /^\s*[\w-]+:\s*.+;?\s*$/</action>
|
||
<check if="globals file bracket imbalance">
|
||
<action>Add to format_errors:</action>
|
||
<output>
|
||
{
|
||
"type": "SYNTAX",
|
||
"file": "{strategy === 'scss' ? 'globals.scss' : 'globals.css'}",
|
||
"location": "file-level",
|
||
"error": "Unbalanced brackets: {open_count} opening vs {close_count} closing",
|
||
"suggestion": "Check for missing or extra braces"
|
||
}
|
||
</output>
|
||
</check>
|
||
<check if="globals file invalid property format detected">
|
||
<action>Add to format_errors:</action>
|
||
<output>
|
||
{
|
||
"type": "SYNTAX",
|
||
"file": "{strategy === 'scss' ? 'globals.scss' : 'globals.css'}",
|
||
"location": "line {globals_line_number}",
|
||
"error": "Invalid CSS property format: '{invalid_line}'",
|
||
"suggestion": "Ensure format is 'property: value;'"
|
||
}
|
||
</output>
|
||
</check>
|
||
<check if="globals file syntax valid">
|
||
<action>Set globals_css_valid = true</action>
|
||
<output>{strategy === 'scss' ? 'globals.scss' : 'globals.css'}: CSS syntax valid</output>
|
||
</check>
|
||
|
||
<!-- ===== 7.3 Completeness Validation (AC #2) ===== -->
|
||
<action>📋 VALIDATE COMPLETENESS (per spec 4.2):</action>
|
||
<action>Define required_tokens checklist from design-system-generator-spec.md Section 4.2:</action>
|
||
<output>
|
||
Required Token Categories:
|
||
- colors: primary (50/100/500/600/700/900), secondary/accent/neutral/success/warning/error (each 500/600/700)
|
||
- typography: fontFamily (heading/body/mono), fontSize (xs~4xl), fontWeight (normal/medium/semibold/bold), lineHeight (tight/normal/relaxed)
|
||
- spacing: 0/1/2/3/4/6/8/12/16
|
||
- borderRadius: none/sm/md/lg/xl/full
|
||
- shadow: sm/md/lg/xl
|
||
- animation: duration (fast/normal/slow), easing (default/in/out/bounce)
|
||
- breakpoints: sm/md/lg/xl/2xl
|
||
</output>
|
||
|
||
<!-- Color completeness check -->
|
||
<action>CHECK colors completeness:</action>
|
||
<action> For primary: verify 50, 100, 500, 600, 700, 900 exist</action>
|
||
<action> For secondary, accent, neutral, success, warning, error: verify 500, 600, 700 exist</action>
|
||
<check if="any required color scale missing">
|
||
<action>Add to completeness_errors:</action>
|
||
<output>
|
||
{
|
||
"type": "COMPLETENESS",
|
||
"category": "colors",
|
||
"missing": ["{list of missing color scales}"],
|
||
"suggestion": "Add missing color values or run KB replenishment"
|
||
}
|
||
</output>
|
||
</check>
|
||
|
||
<!-- Typography completeness check -->
|
||
<action>CHECK typography completeness:</action>
|
||
<action> fontFamily: heading, body, mono</action>
|
||
<action> fontSize: xs, sm, base, lg, xl, 2xl, 3xl, 4xl</action>
|
||
<action> fontWeight: normal, medium, semibold, bold</action>
|
||
<action> lineHeight: tight, normal, relaxed</action>
|
||
<check if="any required typography token missing">
|
||
<action>Add missing items to completeness_errors with category: "typography"</action>
|
||
</check>
|
||
|
||
<!-- Spacing completeness check -->
|
||
<action>CHECK spacing completeness: 0, 1, 2, 3, 4, 6, 8, 12, 16</action>
|
||
<check if="any required spacing value missing">
|
||
<action>Add missing items to completeness_errors with category: "spacing"</action>
|
||
</check>
|
||
|
||
<!-- Border radius completeness check -->
|
||
<action>CHECK borderRadius completeness: none, sm, md, lg, xl, full</action>
|
||
<check if="any required borderRadius value missing">
|
||
<action>Add missing items to completeness_errors with category: "borderRadius"</action>
|
||
</check>
|
||
|
||
<!-- Shadow completeness check -->
|
||
<action>CHECK shadow completeness: sm, md, lg, xl</action>
|
||
<check if="any required shadow value missing">
|
||
<action>Add missing items to completeness_errors with category: "shadow"</action>
|
||
</check>
|
||
|
||
<!-- Animation completeness check -->
|
||
<action>CHECK animation completeness:</action>
|
||
<action> duration: fast, normal, slow</action>
|
||
<action> easing: default, in, out, bounce</action>
|
||
<check if="any required animation value missing">
|
||
<action>Add missing items to completeness_errors with category: "animation"</action>
|
||
</check>
|
||
|
||
<!-- Breakpoints completeness check -->
|
||
<action>CHECK breakpoints completeness: sm, md, lg, xl, 2xl</action>
|
||
<check if="any required breakpoint missing">
|
||
<action>Add missing items to completeness_errors with category: "breakpoints"</action>
|
||
</check>
|
||
|
||
<!-- Calculate completeness metrics (M-01 fix: token-level calculation) -->
|
||
<action>CALCULATE completeness metrics at TOKEN-LEVEL:</action>
|
||
<action> Define total_required_tokens = 73 per spec 4.2:</action>
|
||
<action> - colors: 6 (primary) + 3*6 (secondary~error) = 24</action>
|
||
<action> - typography: 3 (fontFamily) + 8 (fontSize) + 4 (fontWeight) + 3 (lineHeight) = 18</action>
|
||
<action> - spacing: 9 (0,1,2,3,4,6,8,12,16)</action>
|
||
<action> - borderRadius: 6 (none,sm,md,lg,xl,full)</action>
|
||
<action> - shadow: 4 (sm,md,lg,xl)</action>
|
||
<action> - animation: 3 (duration) + 4 (easing) = 7</action>
|
||
<action> - breakpoints: 5 (sm,md,lg,xl,2xl)</action>
|
||
<action> Count missing_tokens_count by iterating all required token paths and checking existence</action>
|
||
<action> found_tokens = 73 - missing_tokens_count</action>
|
||
<action> completeness_rate = (found_tokens / 73) * 100</action>
|
||
<action> Store in validation_report.metrics.completeness_rate</action>
|
||
<output>Completeness Rate: {completeness_rate}% ({found_tokens}/73 tokens)</output>
|
||
<check if="completeness_rate < 100">
|
||
<output>?? COMPLETENESS WARNING: {missing_tokens_count} required tokens missing</output>
|
||
<output>Missing tokens by category:</output>
|
||
<output>{completeness_errors.map(e => ` - [${e.category}] ${e.missing.join(', ')}`).join('\n')}</output>
|
||
<output>Suggestion: Add missing token paths with source (ux-spec/kb-suggestion/user-input/default)</output>
|
||
</check>
|
||
|
||
<!-- ===== 7.4 KB Reference Validation (AC #3, #4) ===== -->
|
||
<action>📋 VALIDATE KB REFERENCE (P0 Risk Control):</action>
|
||
|
||
<!-- Track KB query execution state -->
|
||
<action>Retrieve kb_query_executed and kb_hits_found from Step 4 execution state</action>
|
||
<action>Count tokens with source === "kb-suggestion" as kb_sourced_count</action>
|
||
<action>Count tokens from missing_list that had KB hits as missing_with_kb_hits</action>
|
||
|
||
<!-- Conditional validation logic (AC #3) -->
|
||
<check if="kb_hits_found === true AND kb_sourced_count === 0">
|
||
<action>Add to kb_errors:</action>
|
||
<output>
|
||
{
|
||
"type": "KB_REFERENCE",
|
||
"error": "KB had hits but no tokens use source: kb-suggestion",
|
||
"detail": "KB query returned results for style '{style}' but output tokens do not reflect KB suggestions",
|
||
"suggestion": "Review Step 4 KB replenishment logic - KB hits should be applied to extracted_tokens"
|
||
}
|
||
</output>
|
||
<output>❌ KB REFERENCE ERROR: KB had hits but no tokens use source: kb-suggestion</output>
|
||
</check>
|
||
|
||
<!-- Fallback allowance (AC #4) -->
|
||
<check if="kb_hits_found === false">
|
||
<output>ℹ️ KB Query: No hits found for style '{style}' - fallback sources allowed</output>
|
||
<action>No KB reference error - user-input and default sources are permitted</action>
|
||
</check>
|
||
|
||
<!-- Calculate KB reference rate -->
|
||
<action>CALCULATE KB reference rate:</action>
|
||
<check if="missing_with_kb_hits > 0">
|
||
<action>kb_reference_rate = (kb_sourced_count / missing_with_kb_hits) * 100</action>
|
||
<action>Store in validation_report.metrics.kb_reference_rate</action>
|
||
<output>📊 KB Reference Rate: {kb_reference_rate}% ({kb_sourced_count}/{missing_with_kb_hits} tokens from KB)</output>
|
||
|
||
<check if="kb_reference_rate < 70">
|
||
<output>⚠️ KB Reference Rate below target (70%)</output>
|
||
<action>Add warning to validation_report.warnings</action>
|
||
</check>
|
||
</check>
|
||
<check if="missing_with_kb_hits === 0">
|
||
<output>ℹ️ KB Reference Rate: N/A (no tokens required KB replenishment or KB had no hits)</output>
|
||
<action>Set kb_reference_rate = "N/A" in validation_report.metrics</action>
|
||
</check>
|
||
|
||
<!-- ===== 7.5 Content Consistency Validation (AC #6) ===== -->
|
||
<action>📋 VALIDATE CONTENT CONSISTENCY:</action>
|
||
|
||
<!-- theme.css vs design-tokens.json consistency -->
|
||
<action>CHECK theme.css custom properties match design-tokens.json:</action>
|
||
<action> 1. Extract all CSS custom properties from theme.css (--color-*, --font-*, --spacing-*, etc.)</action>
|
||
<action> 2. For each custom property, verify corresponding token exists in design-tokens.json</action>
|
||
<action> 3. Verify values are consistent (allow for format differences like #hex vs rgb)</action>
|
||
<check if="custom property not found in tokens">
|
||
<action>Add to consistency_errors:</action>
|
||
<output>
|
||
{
|
||
"type": "CONTENT_CONSISTENCY",
|
||
"file": "theme.css",
|
||
"error": "Custom property '{property_name}' not found in design-tokens.json",
|
||
"suggestion": "Add corresponding token or remove orphan custom property"
|
||
}
|
||
</output>
|
||
</check>
|
||
|
||
<!-- M-03 fix: Reverse check - tokens must have corresponding CSS custom properties -->
|
||
<action>CHECK design-tokens.json tokens have corresponding CSS custom properties (reverse check):</action>
|
||
<action> 1. For each token in design-tokens.json:</action>
|
||
<action> - Build expected CSS property name using naming convention:</action>
|
||
<action> * colors.{role}.{scale} -> --color-{role}-{scale}</action>
|
||
<action> * typography.fontFamily.{key} -> --font-family-{key}</action>
|
||
<action> * typography.fontSize.{key} -> --font-size-{key}</action>
|
||
<action> * spacing.{key} -> --spacing-{key}</action>
|
||
<action> * borderRadius.{key} -> --radius-{key}</action>
|
||
<action> * shadow.{key} -> --shadow-{key}</action>
|
||
<action> * animation.duration.{key} -> --duration-{key}</action>
|
||
<action> * animation.easing.{key} -> --easing-{key}</action>
|
||
<action> * breakpoints.{key} -> --breakpoint-{key}</action>
|
||
<action> 2. Verify the expected CSS property exists in theme.css</action>
|
||
<check if="token has no corresponding CSS custom property">
|
||
<action>Add to consistency_errors:</action>
|
||
<output>
|
||
{
|
||
"type": "CONTENT_CONSISTENCY",
|
||
"file": "theme.css",
|
||
"error": "Token '{token_path}' has no corresponding CSS custom property '{expected_css_property}'",
|
||
"suggestion": "Add CSS custom property '{expected_css_property}' to theme.css :root selector"
|
||
}
|
||
</output>
|
||
</check>
|
||
|
||
<!-- Dark theme variant check -->
|
||
<action>CHECK theme.css contains dark theme variant:</action>
|
||
<action> Search for [data-theme="dark"] or .dark or @media (prefers-color-scheme: dark)</action>
|
||
<check if="no dark theme variant found">
|
||
<action>Add to consistency_errors:</action>
|
||
<output>
|
||
{
|
||
"type": "CONTENT_CONSISTENCY",
|
||
"file": "theme.css",
|
||
"error": "Dark theme variant not found",
|
||
"suggestion": "Add [data-theme=\"dark\"] section with color overrides"
|
||
}
|
||
</output>
|
||
</check>
|
||
<check if="dark theme variant found">
|
||
<output>✅ theme.css: Dark theme variant present</output>
|
||
</check>
|
||
|
||
<!-- global styles required content check -->
|
||
<action>CHECK global styles file contains required content:</action>
|
||
|
||
<!-- CSS Reset check -->
|
||
<action> 1. CSS Reset: search for "box-sizing: border-box" and "margin: 0"</action>
|
||
<check if="CSS reset not found">
|
||
<action>Add to consistency_errors:</action>
|
||
<output>
|
||
{
|
||
"type": "CONTENT_CONSISTENCY",
|
||
"file": "{strategy === 'scss' ? 'globals.scss' : 'globals.css'}",
|
||
"error": "CSS reset not found",
|
||
"suggestion": "Add modern CSS reset (box-sizing, margin reset)"
|
||
}
|
||
</output>
|
||
</check>
|
||
|
||
<!-- Focus styles check -->
|
||
<action> 2. Focus styles: search for ":focus-visible" or ":focus"</action>
|
||
<check if="focus styles not found">
|
||
<action>Add to consistency_errors:</action>
|
||
<output>
|
||
{
|
||
"type": "CONTENT_CONSISTENCY",
|
||
"file": "{strategy === 'scss' ? 'globals.scss' : 'globals.css'}",
|
||
"error": "Focus styles for accessibility not found",
|
||
"suggestion": "Add :focus-visible styles for keyboard navigation accessibility"
|
||
}
|
||
</output>
|
||
</check>
|
||
|
||
<!-- Reduced motion check -->
|
||
<action> 3. Reduced motion: search for "prefers-reduced-motion"</action>
|
||
<check if="reduced motion styles not found">
|
||
<action>Add to consistency_errors:</action>
|
||
<output>
|
||
{
|
||
"type": "CONTENT_CONSISTENCY",
|
||
"file": "{strategy === 'scss' ? 'globals.scss' : 'globals.css'}",
|
||
"error": "Reduced motion media query not found",
|
||
"suggestion": "Add @media (prefers-reduced-motion: reduce) for accessibility"
|
||
}
|
||
</output>
|
||
</check>
|
||
<check if="all accessibility styles present">
|
||
<output>{strategy === 'scss' ? 'globals.scss' : 'globals.css'}: CSS reset and accessibility styles present</output>
|
||
</check>
|
||
|
||
<!-- component-specs.json coverage check -->
|
||
<action>CHECK component-specs.json covers core components:</action>
|
||
<action> Required components: Button, Card, Input</action>
|
||
<check if="any required component missing from component-specs.json">
|
||
<action>Add to consistency_errors:</action>
|
||
<output>
|
||
{
|
||
"type": "CONTENT_CONSISTENCY",
|
||
"file": "component-specs.json",
|
||
"error": "Required component '{component_name}' not found",
|
||
"suggestion": "Add {component_name} component specification with base styles and variants"
|
||
}
|
||
</output>
|
||
</check>
|
||
<check if="all required components present">
|
||
<output>✅ component-specs.json: All core components (Button, Card, Input) present</output>
|
||
</check>
|
||
|
||
<!-- ===== 7.6 Error Report and Success Criteria Summary (AC #5) ===== -->
|
||
<action>📋 GENERATE VALIDATION REPORT:</action>
|
||
|
||
<!-- Aggregate all errors -->
|
||
<action>Combine all error arrays: all_errors = [...format_errors, ...completeness_errors, ...kb_errors, ...consistency_errors]</action>
|
||
|
||
<!-- Calculate format correctness rate -->
|
||
<action>format_correct_count = (design_tokens_json_valid ? 1 : 0) + (component_specs_json_valid ? 1 : 0) + (theme_css_valid ? 1 : 0) + (globals_css_valid ? 1 : 0)</action>
|
||
<action>format_correctness_rate = (format_correct_count / 4) * 100</action>
|
||
<action>Store in validation_report.metrics.format_correctness_rate</action>
|
||
|
||
<!-- Output error details if any -->
|
||
<check if="all_errors.length > 0">
|
||
<output>
|
||
❌ VALIDATION ERRORS FOUND: {all_errors.length} issue(s)
|
||
|
||
Error Details:
|
||
</output>
|
||
<action>For each error in all_errors:</action>
|
||
<output>
|
||
---
|
||
Type: {error.type}
|
||
File: {error.file || 'N/A'}
|
||
Location: {error.location || 'N/A'}
|
||
Error: {error.error}
|
||
Suggestion: {error.suggestion}
|
||
---
|
||
</output>
|
||
</check>
|
||
|
||
<!-- Success Criteria Summary Table -->
|
||
<output>
|
||
===============================================================
|
||
VALIDATION REPORT SUMMARY ===============================================================
|
||
|
||
| Metric | Target | Actual | Status |
|
||
|---------------------|---------|---------------------|--------|
|
||
| KB Reference Rate | > 70% | {kb_reference_rate}%| {kb_reference_rate === 'N/A' ? '⬜ N/A' : (kb_reference_rate >= 70 ? '✅ PASS' : '⚠️ BELOW')} |
|
||
| Completeness | 100% | {completeness_rate}%| {completeness_rate === 100 ? '✅ PASS' : '❌ FAIL'} |
|
||
| Format Correctness | 100% | {format_correctness_rate}%| {format_correctness_rate === 100 ? '✅ PASS' : '❌ FAIL'} |
|
||
| Content Consistency | 100% | {consistency_errors.length === 0 ? '100%' : 'Issues'}| {consistency_errors.length === 0 ? '✅ PASS' : '⚠️ ISSUES'} | ===============================================================
|
||
</output>
|
||
|
||
<!-- Dev Usability Assessment -->
|
||
<action>ASSESS Dev Usability (composite metric):</action>
|
||
<action> 1. Output file coverage: 4/4 files generated?</action>
|
||
<action> 2. JSON/CSS lint: format_correctness_rate === 100?</action>
|
||
<action> 3. No blocking errors: format_errors.length === 0?</action>
|
||
|
||
<check if="format_correctness_rate === 100 AND completeness_rate === 100 AND format_errors.length === 0">
|
||
<output>
|
||
✅ DEV USABILITY: READY
|
||
All output files are valid and complete. Dev Agent can proceed with implementation.
|
||
</output>
|
||
<action>Set validation_report.dev_usability = "READY"</action>
|
||
</check>
|
||
<check if="format_correctness_rate < 100 OR format_errors.length > 0">
|
||
<output>
|
||
❌ DEV USABILITY: BLOCKED
|
||
Output files have syntax errors that must be fixed before use.
|
||
See error details above for specific issues and suggestions.
|
||
</output>
|
||
<action>Set validation_report.dev_usability = "BLOCKED"</action>
|
||
</check>
|
||
<check if="format_correctness_rate === 100 AND completeness_rate < 100">
|
||
<output>
|
||
⚠️ DEV USABILITY: USABLE WITH GAPS
|
||
Output files are syntactically valid but incomplete.
|
||
Dev Agent may need to add missing tokens during implementation.
|
||
Consider running KB replenishment or providing missing values.
|
||
</output>
|
||
<action>Set validation_report.dev_usability = "USABLE_WITH_GAPS"</action>
|
||
</check>
|
||
|
||
<!-- Invoke checklist validation -->
|
||
<invoke-task>Validate against checklist at {validation} using {project-root}/_bmad/core/tasks/validate-workflow.xml</invoke-task>
|
||
</step>
|
||
|
||
<step n="8" goal="Final Report">
|
||
<output>??Design System Generation Complete!</output>
|
||
<output>Generated files in: {output_folder}</output>
|
||
<check if="strategy === 'tailwind'">
|
||
<output>1. design-tokens.json (SSOT)</output>
|
||
<output>2. theme.css (Variables)</output>
|
||
<output>3. tailwind.config.ts (Tailwind theme extension)</output>
|
||
<output>4. globals.css (Global Styles)</output>
|
||
<output>5. component-specs.json (Component Definitions)</output>
|
||
</check>
|
||
<check if="strategy === 'scss'">
|
||
<output>1. design-tokens.json (SSOT)</output>
|
||
<output>2. theme.css (Variables)</output>
|
||
<output>3. _tokens.scss (SCSS variables and mixins)</output>
|
||
<output>4. globals.scss (Global Styles)</output>
|
||
<output>5. component-specs.json (Component Definitions)</output>
|
||
</check>
|
||
<check if="strategy === 'generic'">
|
||
<output>1. design-tokens.json (SSOT)</output>
|
||
<output>2. theme.css (Variables)</output>
|
||
<output>3. globals.css (Global Styles)</output>
|
||
<output>4. component-specs.json (Component Definitions)</output>
|
||
</check>
|
||
<action>Report completion</action>
|
||
</step>
|
||
|
||
</workflow>
|