The workflow execution engine is governed by: {project-root}/_bmad/core/tasks/workflow.xml
You MUST have already loaded and processed: {installed_path}/workflow.yaml
Communicate all responses in {communication_language}, tailored to {user_skill_level}, and generate all documents in {document_output_language}
CRITICAL MISSION: You are the Design System Generator. You bridge the gap between UX Design and Implementation.
Your purpose is to translate human-readable UX specifications into machine-readable DEV artifacts.
Strictly follow the Single Source of Truth (SSOT) principle: Design Tokens drive everything.
Initialize missing_list to empty
Set domain to "ui-ux" for KB queries
HALT
Set ux_file_path to discovered path
Set ux_spec to ux_spec_content
Set arch_file_path to null
Set default_framework to "tailwind"
Set default_component_lib to "react"
Set arch_file_path to discovered path
Set architecture to architecture_content
Set kb_path to {kb_path}
Set kb_path to "{project-root}/resources/ui-ux-pro-max"
Load project context from {project_context}
Initialize extracted_tokens object with the following W3C Design Token structure:
?? PARSE VISUAL FOUNDATION from {ux_spec}:
Extract Color Palette:
1. Search for sections matching: "Color Palette", "Colors", "Color", "Palette"
2. Identify parsing format:
FORMAT A - Markdown table:
??Parse: role=first column, value=second column
FORMAT B - List format:
??Parse: regex pattern `^[-*]\s*(\w+):\s*(#[0-9a-fA-F]{3,8}|hsl\([^)]+\)|rgb\([^)]+\))`
FORMAT C - Inline definition:
??Parse: regex pattern `(primary|secondary|accent|neutral|success|warning|error)\s*(color)?\s*(is|:)?\s*(#[0-9a-fA-F]{3,8}|hsl\([^)]+\))`
3. For each color role found (primary, secondary, accent, neutral, success, warning, error):
- Normalize value to HEX format if HSL/RGB provided
- If scale provided (50-900), store all values
- If only base color, store as "500" and mark 50/100/900 as MISSING
- Store with structure: { "value": "#...", "type": "color", "source": "ux-spec" }
4. EDGE CASE: No color section found ??Add all required colors to missing_list
Extract Typography:
1. Search for sections matching: "Typography", "Font", "Typeface"
2. Parse font families:
- Look for: "Heading font:", "Heading typeface:", "heading:", "h1-h6"
- Look for: "Body font:", "Body typeface:", "body:", "paragraph"
- Look for: "Mono font:", "Code font:", "Monospace font:", "mono:"
- Extract font-stack string (e.g., "'Inter', sans-serif")
- Store: { "value": "'Inter', sans-serif", "type": "fontFamily", "source": "ux-spec" }
3. Parse font sizes (if explicitly defined):
- Look for scale definitions: xs, sm, base, lg, xl, 2xl, 3xl, 4xl
- Accept rem, em, or px values (normalize to rem)
4. Parse font weights and line heights if found
5. EDGE CASE: Only "Inter" mentioned without stack ??Use "'Inter', system-ui, sans-serif"
6. EDGE CASE: No typography section ??Add fontFamily.heading, fontFamily.body to missing_list
?? PARSE SPACING AND LAYOUT from {ux_spec}:
?? Extract Spacing Scale:
1. Search for: "Spacing", "Space scale", "Layout"
2. Identify base unit:
- Look for patterns: "4px base", "base unit: 4px", "0.25rem", "4pt grid"
- Default base: 4px (0.25rem) if not specified
3. Parse scale values:
- Standard scale: 0, 1, 2, 3, 4, 6, 8, 12, 16
- Calculate: value * base_unit (e.g., 4 * 4px = 16px = 1rem)
- Store: { "value": "1rem", "type": "spacing", "source": "ux-spec" }
4. EDGE CASE: No spacing defined ??Use default scale with base=4px, mark source as "default"
Extract Breakpoints:
1. Search for: "Breakpoints", "Responsive", "Media queries"
2. Parse breakpoint values:
- sm: usually 640px
- md: usually 768px
- lg: usually 1024px
- xl: usually 1280px
- 2xl: usually 1536px
3. Store: { "value": "768px", "type": "dimension", "source": "ux-spec" }
4. EDGE CASE: No breakpoints ??Use Tailwind defaults, mark source as "default"
Extract Border Radius:
1. Search for: "Border Radius", "Radius", "Corners"
2. Parse values: none, sm, md, lg, xl, full
3. Accept rem, px values (normalize to rem)
4. Store: { "value": "0.375rem", "type": "borderRadius", "source": "ux-spec" }
5. EDGE CASE: No radius defined ??Add sm, md, lg to missing_list
PARSE EFFECTS AND ANIMATION from {ux_spec}:
?? Extract Shadow Definitions:
1. Search for: "Shadow", "Elevation", "Box shadow"
2. Parse shadow levels: sm, md, lg, xl
3. Extract full box-shadow syntax if provided:
Example: "0 4px 6px -1px rgba(0, 0, 0, 0.1)"
4. Store: { "value": "0 4px 6px -1px rgba(0, 0, 0, 0.1)", "type": "boxShadow", "source": "ux-spec" }
5. EDGE CASE: No shadow defined ??Add sm, md to missing_list
??Extract Animation Settings:
1. Search for: "Animation", "Motion", "Transitions"
2. Parse duration values:
- fast: typically 150ms
- normal: typically 300ms
- slow: typically 500ms
3. Parse easing functions:
- default: cubic-bezier(0.4, 0, 0.2, 1)
- in, out, bounce variations
4. Store duration: { "value": "150ms", "type": "duration", "source": "ux-spec" }
5. Store easing: { "value": "cubic-bezier(0.4, 0, 0.2, 1)", "type": "cubicBezier", "source": "ux-spec" }
6. EDGE CASE: No animation defined ??Add duration.fast, duration.normal, easing.default to missing_list
Extract Tech Stack from {architecture}:
1. UI Framework: search for "React", "Vue", "Next.js", "Nuxt", "Svelte", "Angular"
2. CSS Framework: search for "Tailwind", "Bootstrap", "Bulma", "Foundation"
3. Preprocessor: search for "SCSS", "Sass", "Less", "Stylus"
4. Component Library: search for "shadcn/ui", "Chakra", "MUI", "Radix", "Headless UI"
5. Store tech_stack object with keys: uiFramework, cssFramework, preprocessor, componentLib, source: "architecture"
6. Normalize tech_stack values:
- Lowercase uiFramework, cssFramework, preprocessor, componentLib
- If any value missing/empty, set to "none"
Set tech_stack = { uiFramework: "react", cssFramework: "tailwind", preprocessor: "none", componentLib: "shadcn", source: "default" }
FINALIZE extracted_tokens structure:
?? VALIDATE extracted_tokens against required token checklist:
Initialize validation_errors = [] and missing_list = []
Define validation helper: isValidToken(token) =
- token !== null
- token !== undefined
- token.value !== null && token.value !== ""
- token.type exists
- Return true if all conditions pass
VALIDATE Colors (REQUIRED per spec 4.2):
CHECK: extracted_tokens.colors exists and is not empty object
For role = "primary":
- CHECK: colors.primary exists as object
- For each required scale in [50, 100, 500, 600, 700, 900]:
- CHECK: colors.primary.{scale} exists and isValidToken()
- IF MISSING: Add { category: "colors", key: "primary.{scale}", priority: "required" }
For each required role in [secondary, accent, neutral, success, warning, error]:
- CHECK: colors.{role} exists as object
- For each required scale in [500, 600, 700]:
- CHECK: colors.{role}.{scale} exists and isValidToken()
- IF MISSING: Add { category: "colors", key: "{role}.{scale}", priority: "required" }
VALIDATE Typography (REQUIRED):
CHECK: extracted_tokens.typography exists
Required checks:
1. typography.fontFamily.heading:
- IF null/undefined/empty: Add to missing_list
2. typography.fontFamily.body:
- IF null/undefined/empty: Add to missing_list
3. typography.fontSize.base:
- IF null/undefined/empty: Add to missing_list
VALIDATE Spacing (REQUIRED per spec 4.2):
CHECK: extracted_tokens.spacing exists
For each required key in [0, 1, 2, 3, 4, 6, 8, 12, 16]:
- CHECK: spacing[key] exists and isValidToken()
- IF MISSING: Add { category: "spacing", key: "{key}", priority: "required" }
VALIDATE Border Radius (REQUIRED per spec 4.2):
CHECK: extracted_tokens.borderRadius exists
For each required key in [none, sm, md, lg, xl, full]:
- CHECK: borderRadius[key] exists and isValidToken()
- IF MISSING: Add { category: "borderRadius", key: "{key}", priority: "required" }
VALIDATE Shadow (REQUIRED per spec 4.2):
CHECK: extracted_tokens.shadow exists
For each required key in [sm, md, lg, xl]:
- CHECK: shadow[key] exists and isValidToken()
- CHECK: shadow[key].value contains valid box-shadow syntax
- IF MISSING: Add { category: "shadow", key: "{key}", priority: "required" }
??VALIDATE Animation (REQUIRED):
CHECK: extracted_tokens.animation exists
Required checks:
1. animation.duration.fast:
- CHECK: exists and value matches pattern /^\d+m?s$/
- IF MISSING: Add { category: "animation", key: "duration.fast", priority: "required" }
2. animation.duration.normal:
- CHECK: exists and value matches pattern /^\d+m?s$/
- IF MISSING: Add { category: "animation", key: "duration.normal", priority: "required" }
3. animation.easing.default:
- CHECK: exists and value contains "cubic-bezier" or valid keyword
- IF MISSING: Add { category: "animation", key: "easing.default", priority: "required" }
VALIDATE Breakpoints (REQUIRED per spec 4.2):
CHECK: extracted_tokens.breakpoints exists
For each required key in [sm, md, lg, xl, 2xl]:
- CHECK: breakpoints[key] exists and value matches /^\d+px$/
- IF MISSING: Add { category: "breakpoints", key: "{key}", priority: "required" }
?? Generate Validation Report:
Set required_missing = missing_list.filter(item => item.priority === "required")
Set recommended_missing = missing_list.filter(item => item.priority === "recommended")
Set validation_status = "complete"
Set validation_status = "incomplete"
Store missing_list for Step 4 KB replenishment
Initialize replenished_items = []
Initialize kb_query_executed = false
Initialize kb_query_failed = false
EXTRACT STYLE KEYWORDS from {ux_spec} for KB queries:
1. Search for sections: "Design Philosophy", "Design Principles", "Style", "Visual Direction"
2. Extract style keywords using patterns:
- Look for adjectives: modern, minimalist, playful, professional, bold, elegant, etc.
- Look for style references: Bauhaus, Material, Apple-like, etc.
- Look for industry hints: SaaS, e-commerce, portfolio, dashboard, etc.
3. Build {style} variable from found keywords (fallback: "modern professional")
4. Store {industry} from project context or infer from ux_spec content
5. Store {product_type} from project context or infer from ux_spec content
Set {style} = "modern professional"
Set {industry} = "general"
Define helper function getDefaultSuggestion(item):
switch(item.category):
case "colors": return "#0ea5e9" (or appropriate scale value for role)
case "typography": return "'Inter', system-ui, sans-serif"
case "spacing": return "{item.key * 0.25}rem" (4px base grid)
case "borderRadius": return sm=0.125rem, md=0.375rem, lg=0.5rem
case "shadow": return Tailwind default shadows
case "animation": return fast=150ms, normal=300ms, default easing
case "breakpoints": return Tailwind defaults (sm=640px, md=768px, etc.)
End function definition
Define helper function setNestedToken(obj, keyPath, tokenValue):
1. Split keyPath by "." (e.g., "fontFamily.heading" ??["fontFamily", "heading"])
2. Traverse obj, creating nested objects as needed
3. Set final key to tokenValue
Example: setNestedToken(typography, "fontFamily.heading", {value, type, source})
??typography.fontFamily.heading = {value, type, source}
End function definition
CATEGORIZE missing_list by token type:
- color_missing = missing_list.filter(m => m.category === "colors")
- typography_missing = missing_list.filter(m => m.category === "typography")
- spacing_missing = missing_list.filter(m => m.category === "spacing" || m.category === "borderRadius")
- shadow_missing = missing_list.filter(m => m.category === "shadow")
- animation_missing = missing_list.filter(m => m.category === "animation")
- breakpoints_missing = missing_list.filter(m => m.category === "breakpoints")
Set kb_query_executed = true
Execute query: python {kb_path}/scripts/search.py "{style} {industry} color palette" --domain color --json
Set kb_query_failed = true
Log error details for debugging
Skip KB color processing, proceed to next category
Continue without KB color suggestions
Parse JSON result with structure:
For each color found in KB results:
- Normalize value to HEX format:
If HSL: hsl(h, s%, l%) ??Convert using: R,G,B = hslToRgb(h/360, s/100, l/100), then #RRGGBB
If RGB: rgb(r, g, b) ??Convert to #RRGGBB using hex encoding
If already HEX: validate format #RGB or #RRGGBB or #RRGGBBAA
- Store with proper nesting:
- Ensure extracted_tokens.colors[role] exists as an object
If scale provided: extracted_tokens.colors[role][scaleKey] = { value, type: "color", source: "kb-suggestion" }
If single value: extracted_tokens.colors[role]["500"] = { value, type: "color", source: "kb-suggestion" }
- Remove from color_missing list
- Append to replenished_items with source: "kb-suggestion"
Set kb_query_executed = true
Execute: python {kb_path}/scripts/search.py "{style} typography" --domain typography --json
Set kb_query_failed = true
Parse JSON result:
For each typography setting found:
- Store: extracted_tokens.typography.fontFamily[role] = { value, type: "fontFamily", source: "kb-suggestion" }
- Remove from typography_missing list
- Append to replenished_items with source: "kb-suggestion"
Set kb_query_executed = true
Execute: python {kb_path}/scripts/search.py "{style} spacing system" --domain spacing --json
Set kb_query_failed = true
Parse JSON result and apply Tailwind-compatible scale if found
For spacing values: store with type: "spacing", source: "kb-suggestion"
For borderRadius values: store with type: "borderRadius", source: "kb-suggestion"
Remove filled items from spacing_missing list
Append filled spacing/radius items to replenished_items with source: "kb-suggestion"
Set kb_query_executed = true
Execute: python {kb_path}/scripts/search.py "{style} elevation shadow" --domain elevation --json
Set kb_query_failed = true
Parse JSON result with box-shadow syntax
For each shadow found:
- Store: extracted_tokens.shadow[level] = { value: "box-shadow-syntax", type: "boxShadow", source: "kb-suggestion" }
- Remove from shadow_missing list
- Append to replenished_items with source: "kb-suggestion"
Set kb_query_executed = true
Execute: python {kb_path}/scripts/search.py "{style} motion animation" --domain animation --json
Set kb_query_failed = true
Parse JSON result for duration and easing values
For duration values: store with type: "duration", source: "kb-suggestion"
For easing values: store with type: "cubicBezier", source: "kb-suggestion"
Remove filled items from animation_missing list
Append filled animation items to replenished_items with source: "kb-suggestion"
Set kb_query_executed = true
Execute: python {kb_path}/scripts/search.py "{style} responsive breakpoints" --domain layout --json
Set kb_query_failed = true
Apply Tailwind default breakpoints: sm=640px, md=768px, lg=1024px, xl=1280px, 2xl=1536px
Mark source as "default" for these values
Parse JSON result for breakpoint values
For each breakpoint found:
- Validate format matches /^\d+px$/
- Store: extracted_tokens.breakpoints[key] = { value, type: "dimension", source: "kb-suggestion" }
- Remove from breakpoints_missing list
- Append to replenished_items with source: "kb-suggestion"
HALT
HALT
Initialize remaining_missing = []
Rebuild remaining_missing by concatenating all category lists after KB queries:
remaining_missing = [...color_missing, ...typography_missing, ...spacing_missing, ...shadow_missing, ...animation_missing, ...breakpoints_missing]
Filter remaining_missing by priority: required items first, then recommended
Please enter values or commands:
For EACH user input value, validate format:
COLOR validation: /^#[0-9a-fA-F]{3,8}$/ OR /^hsl\([^)]+\)$/ OR /^rgb\([^)]+\)$/
FONT validation: non-empty string (any font family name accepted)
DIMENSION validation: /^\d+(\.\d+)?(px|rem|em)$/
DURATION validation: /^\d+(ms|s)$/
EASING validation: /^cubic-bezier\([^)]+\)$/ OR keywords (ease, ease-in, ease-out, linear)
Please choose [r/d/s]:
Handle user choice accordingly
Use setNestedToken(extracted_tokens[category], key, { value: user_input, type: token_type, source: "user-input" })
This handles keys like "fontFamily.heading" ??typography.fontFamily.heading
Remove item from remaining_missing
Append to replenished_items with source: "user-input"
Apply default values with source: "default"
Default values reference:
- colors.primary.500: "#0ea5e9" (Sky Blue)
- colors.secondary.500: "#6366f1" (Indigo)
- colors.neutral.500: "#64748b" (Slate)
- typography.fontFamily.heading: "'Inter', system-ui, sans-serif"
- typography.fontFamily.body: "'Inter', system-ui, sans-serif"
- typography.fontSize.base: "1rem"
- spacing.{n}: "{n * 0.25}rem" (4px base)
- borderRadius.sm: "0.125rem", md: "0.375rem", lg: "0.5rem"
- shadow.sm: "0 1px 2px 0 rgba(0, 0, 0, 0.05)"
- shadow.md: "0 4px 6px -1px rgba(0, 0, 0, 0.1)"
- animation.duration.fast: "150ms", normal: "300ms", slow: "500ms"
- animation.easing.default: "cubic-bezier(0.4, 0, 0.2, 1)"
- breakpoints: sm=640px, md=768px, lg=1024px, xl=1280px
Append default-filled items to replenished_items with source: "default"
Confirm skip? [y/n]
If confirmed skip: mark item as skipped, proceed without value
CALCULATE source statistics:
- Count tokens where source === "ux-spec" ??store as ux_count
- Count tokens where source === "architecture" ??store as arch_count
- Count tokens where source === "kb-suggestion" ??store as kb_count
- Count tokens where source === "user-input" ??store as user_count
- Count tokens where source === "default" ??store as default_count
- Calculate total_count = ux_count + arch_count + kb_count + user_count + default_count
FINALIZE KB TRACKING STATE for Step 7 validation:
Set kb_query_executed = true if ANY KB query was attempted in Step 4
Set kb_hits_found = true if ANY KB query returned non-empty results
Build kb_hits_for_item = {} tracking per-item KB hit status:
For each item in original missing_list:
kb_hits_for_item[item.category + "." + item.key] = (KB had hit for this item)
Calculate missing_with_kb_hits = count of items where kb_hits_for_item[key] === true
Store these variables for Step 7 KB reference rate calculation
Proceed with file generation? [Y/n]
HALT
Build design_tokens_json from extracted_tokens using W3C Design Token format
Ensure "$schema" is set to "https://design-tokens.github.io/community-group/format/"
Preserve source field on every token (ux-spec/architecture/kb-suggestion/user-input/default)
Write file: {output_folder}/design-tokens.json
Generate theme.css from design_tokens_json
Map tokens to CSS Custom Properties using naming rules:
- colors: --color-{role}-{scale}
- typography: --font-family-*, --font-size-*, --font-weight-*, --line-height-*
- spacing: --spacing-{key}
- borderRadius: --radius-{key}
- shadow: --shadow-{key}
- animation: --duration-*, --easing-*
- breakpoints: --breakpoint-{key}
Group variables by section with readable comments
Add [data-theme="dark"] overrides for background/surface/text/text-muted if available
Write file: {output_folder}/theme.css
DETECT framework strategy from tech_stack (mutually exclusive):
If tech_stack.cssFramework includes "tailwind": set strategy = "tailwind"
Else if tech_stack.preprocessor includes "scss" OR "sass": set strategy = "scss"
Else: set strategy = "generic"
Set is_tailwind = (strategy === "tailwind")
Set is_scss = (strategy === "scss")
Set is_vanilla = (strategy === "generic")
Generate tailwind.config.ts with theme.extend mapping:
1. Import type { Config } from 'tailwindcss'
2. Map colors using hsl(var(--color-{role}-{scale})) syntax for opacity modifier support:
CRITICAL: Use hsl(var(--color-name)) syntax to enable Tailwind opacity modifiers like bg-primary/50
For this to work, theme.css must store colors as HSL values without the hsl() wrapper:
--color-primary-500: 199 89% 48%; /* HSL values only, no hsl() */
If theme.css uses hex values, convert to HSL space-separated format in tailwind.config.ts generation
Write file: {output_folder}/tailwind.config.ts
Generate _tokens.scss with SCSS variable mappings:
Write file: {output_folder}/_tokens.scss
Generate global styles with framework-appropriate content:
Generate globals.css with Tailwind directives:
Generate globals.scss with Modern CSS Reset and base typography:
Generate globals.css with Modern CSS Reset and base typography:
Ensure all values reference CSS variables from theme.css (SSOT)
Write file: {output_folder}/globals.scss
Write file: {output_folder}/globals.css
Determine componentLibrary from tech_stack.componentLib (default: "shadcn")
Determine effectsLibrary (default: "framer-motion", or "none" if not used)
Include "$schema" only if component-specs.schema.json is available (optional)
Build component-specs.json with meta and component definitions:
- Button: variants (primary/secondary/outline/ghost), sizes (sm/md/lg), states (disabled/loading), animation (hover/tap)
- Card: base + variants (elevated/outlined)
- Input: base + states (focus/error/disabled)
Write file: {output_folder}/component-specs.json
Initialize validation_report = { errors: [], warnings: [], metrics: {} }
Initialize format_errors = [], completeness_errors = [], kb_errors = [], consistency_errors = []
đ VALIDATE JSON SYNTAX:
Attempt to parse design-tokens.json as JSON:
Extract error line number and message from parse error
Add to format_errors:
Set design_tokens_json_valid = true
Attempt to parse component-specs.json as JSON:
Extract error line number and message from parse error
Add to format_errors:
Set component_specs_json_valid = true
đ VALIDATE CSS SYNTAX:
Validate theme.css CSS syntax:
1. Check bracket balance: count of '{' must equal count of '}'
2. Check property format: each line inside {} should match pattern /^\s*[\w-]+:\s*.+;?\s*$/ or be a comment
3. Check for unclosed comments: /* must have matching */
4. Check for invalid selectors: selectors should not contain unescaped special characters
Add to format_errors:
Add to format_errors:
Set theme_css_valid = true
Validate global styles syntax with detailed location tracking:
1. Initialize globals_line_number = 0
2. For each line in {strategy === 'scss' ? 'globals.scss' : 'globals.css'}:
globals_line_number++
Track bracket_depth: '{' increases, '}' decreases
3. Bracket balance check: if final bracket_depth != 0, report unbalanced
4. Comment closure check: verify /* has matching */
5. If strategy === "scss": skip strict property format checks (allow SCSS variables/nesting)
6. If strategy !== "scss": validate each property line matches /^\s*[\w-]+:\s*.+;?\s*$/
Add to format_errors:
Add to format_errors:
Set globals_css_valid = true
đ VALIDATE COMPLETENESS (per spec 4.2):
Define required_tokens checklist from design-system-generator-spec.md Section 4.2:
CHECK colors completeness:
For primary: verify 50, 100, 500, 600, 700, 900 exist
For secondary, accent, neutral, success, warning, error: verify 500, 600, 700 exist
Add to completeness_errors:
CHECK typography completeness:
fontFamily: heading, body, mono
fontSize: xs, sm, base, lg, xl, 2xl, 3xl, 4xl
fontWeight: normal, medium, semibold, bold
lineHeight: tight, normal, relaxed
Add missing items to completeness_errors with category: "typography"
CHECK spacing completeness: 0, 1, 2, 3, 4, 6, 8, 12, 16
Add missing items to completeness_errors with category: "spacing"
CHECK borderRadius completeness: none, sm, md, lg, xl, full
Add missing items to completeness_errors with category: "borderRadius"
CHECK shadow completeness: sm, md, lg, xl
Add missing items to completeness_errors with category: "shadow"
CHECK animation completeness:
duration: fast, normal, slow
easing: default, in, out, bounce
Add missing items to completeness_errors with category: "animation"
CHECK breakpoints completeness: sm, md, lg, xl, 2xl
Add missing items to completeness_errors with category: "breakpoints"
CALCULATE completeness metrics at TOKEN-LEVEL:
Define total_required_tokens = 73 per spec 4.2:
- colors: 6 (primary) + 3*6 (secondary~error) = 24
- typography: 3 (fontFamily) + 8 (fontSize) + 4 (fontWeight) + 3 (lineHeight) = 18
- spacing: 9 (0,1,2,3,4,6,8,12,16)
- borderRadius: 6 (none,sm,md,lg,xl,full)
- shadow: 4 (sm,md,lg,xl)
- animation: 3 (duration) + 4 (easing) = 7
- breakpoints: 5 (sm,md,lg,xl,2xl)
Count missing_tokens_count by iterating all required token paths and checking existence
found_tokens = 73 - missing_tokens_count
completeness_rate = (found_tokens / 73) * 100
Store in validation_report.metrics.completeness_rate
đ VALIDATE KB REFERENCE (P0 Risk Control):
Retrieve kb_query_executed and kb_hits_found from Step 4 execution state
Count tokens with source === "kb-suggestion" as kb_sourced_count
Count tokens from missing_list that had KB hits as missing_with_kb_hits
Add to kb_errors:
No KB reference error - user-input and default sources are permitted
CALCULATE KB reference rate:
kb_reference_rate = (kb_sourced_count / missing_with_kb_hits) * 100
Store in validation_report.metrics.kb_reference_rate
Add warning to validation_report.warnings
Set kb_reference_rate = "N/A" in validation_report.metrics
đ VALIDATE CONTENT CONSISTENCY:
CHECK theme.css custom properties match design-tokens.json:
1. Extract all CSS custom properties from theme.css (--color-*, --font-*, --spacing-*, etc.)
2. For each custom property, verify corresponding token exists in design-tokens.json
3. Verify values are consistent (allow for format differences like #hex vs rgb)
Add to consistency_errors:
CHECK design-tokens.json tokens have corresponding CSS custom properties (reverse check):
1. For each token in design-tokens.json:
- Build expected CSS property name using naming convention:
* colors.{role}.{scale} -> --color-{role}-{scale}
* typography.fontFamily.{key} -> --font-family-{key}
* typography.fontSize.{key} -> --font-size-{key}
* spacing.{key} -> --spacing-{key}
* borderRadius.{key} -> --radius-{key}
* shadow.{key} -> --shadow-{key}
* animation.duration.{key} -> --duration-{key}
* animation.easing.{key} -> --easing-{key}
* breakpoints.{key} -> --breakpoint-{key}
2. Verify the expected CSS property exists in theme.css
Add to consistency_errors:
CHECK theme.css contains dark theme variant:
Search for [data-theme="dark"] or .dark or @media (prefers-color-scheme: dark)
Add to consistency_errors:
CHECK global styles file contains required content:
1. CSS Reset: search for "box-sizing: border-box" and "margin: 0"
Add to consistency_errors:
2. Focus styles: search for ":focus-visible" or ":focus"
Add to consistency_errors:
3. Reduced motion: search for "prefers-reduced-motion"
Add to consistency_errors:
CHECK component-specs.json covers core components:
Required components: Button, Card, Input
Add to consistency_errors:
đ GENERATE VALIDATION REPORT:
Combine all error arrays: all_errors = [...format_errors, ...completeness_errors, ...kb_errors, ...consistency_errors]
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)
format_correctness_rate = (format_correct_count / 4) * 100
Store in validation_report.metrics.format_correctness_rate
For each error in all_errors:
ASSESS Dev Usability (composite metric):
1. Output file coverage: 4/4 files generated?
2. JSON/CSS lint: format_correctness_rate === 100?
3. No blocking errors: format_errors.length === 0?
Set validation_report.dev_usability = "READY"
Set validation_report.dev_usability = "BLOCKED"
Set validation_report.dev_usability = "USABLE_WITH_GAPS"
Validate against checklist at {validation} using {project-root}/_bmad/core/tasks/validate-workflow.xml
Report completion