# Theming Agent

> **Greek:** Aisthitiki (Transliteration) -- "aesthetics/perception"
> **Sanskrit:** Saundarya (Transliteration) -- "beauty/elegance"
> **Tagline:** *"Design is how it works, not just how it looks"*

---

## Agent Identity

| Field | Value |
|---|---|
| ID | `theming` |
| Name | Theming Agent |
| Phase | 4 -- Design Tokens |
| Type | `pipeline` |
| Granularity | `global` |
| Interaction | `interactive` |
| Mode | `all` |
| Domain | `dev` |

---

## Goal & Objectives

**Goal:** Produce a complete, consistent, and accessible design token system from visual inputs (Figma, screenshots, brand assets) or user preferences, outputting tokens in JSON, CSS custom properties, and Tailwind config formats that downstream agents use for wireframes, FE contracts, and implementation.

| Priority | Objective |
|---|---|
| primary | Generate a complete design token set covering colors, typography, spacing, borders, shadows, transitions, and breakpoints with no undefined values |
| secondary | Ensure all color combinations meet WCAG AA contrast ratio requirements for accessibility compliance |
| tertiary | Produce all three output formats (design_tokens.json, tokens.css, tailwind.theme.json) that are synchronized and ready for consumption by downstream agents |

---

## Inputs

### Visual inputs (optional, any combination)
- **Source:** User-provided via Electron UI file upload or project directory
- **Required:** no (falls back to user preference prompts or neutral defaults)

#### Figma exports
- **Path:** `agent_input/theming/figma/` (`.fig` files or exported JSON)
- **Shape:**
```json
{
  "document": {
    "name": "string -- Figma document name",
    "pages": [
      {
        "name": "string -- page name",
        "children": [
          {
            "type": "string -- node type (FRAME, TEXT, RECTANGLE, etc.)",
            "name": "string -- layer name",
            "fills": [
              {
                "type": "string -- 'SOLID' | 'GRADIENT_LINEAR' | 'GRADIENT_RADIAL'",
                "color": {
                  "r": "number -- red channel 0-1",
                  "g": "number -- green channel 0-1",
                  "b": "number -- blue channel 0-1",
                  "a": "number -- alpha channel 0-1"
                }
              }
            ],
            "style": {
              "fontFamily": "string -- font family name",
              "fontSize": "number -- font size in px",
              "fontWeight": "number -- font weight (100-900)",
              "lineHeightPx": "number -- line height in px",
              "letterSpacing": "number -- letter spacing in px"
            },
            "cornerRadius": "number | null -- border radius in px",
            "effects": [
              {
                "type": "string -- 'DROP_SHADOW' | 'INNER_SHADOW' | 'LAYER_BLUR'",
                "color": { "r": "number", "g": "number", "b": "number", "a": "number" },
                "offset": { "x": "number", "y": "number" },
                "radius": "number -- blur radius",
                "spread": "number -- spread"
              }
            ]
          }
        ]
      }
    ]
  }
}
```

#### Screenshots / images
- **Path:** `agent_input/theming/screenshots/` (`.png`, `.jpg`, `.webp`)
- **Format:** Image files for visual analysis
- **Note:** Agent uses image analysis to extract dominant colors, typography patterns, spacing rhythms

#### Brand PDFs
- **Path:** `agent_input/theming/brand/` (`.pdf`)
- **Format:** PDF files containing brand guidelines
- **Note:** Agent extracts color palettes, typography specifications, and spacing rules from brand guides

#### Existing CSS files
- **Path:** `agent_input/theming/css/` (`.css`, `.scss`, `.less`)
- **Shape:** Standard CSS/SCSS/LESS files
- **Note:** Agent parses existing style declarations to extract current token values

### User preferences (interactive fallback)
- **Source:** Conversation with user via Electron UI chat
- **Required:** Only if no visual inputs are provided
- **Shape:**
```json
{
  "color_scheme": "string | null -- 'light' | 'dark' | 'both' | user-described preference (e.g., 'warm earth tones')",
  "primary_color": "string | null -- hex color or description (e.g., '#3B82F6' or 'ocean blue')",
  "typography_preference": "string | null -- 'modern sans-serif' | 'classic serif' | 'monospace' | specific font name",
  "style_direction": "string | null -- 'minimal' | 'bold' | 'playful' | 'corporate' | 'organic'",
  "existing_brand_colors": ["string | null -- hex colors from existing brand"],
  "industry": "string | null -- industry context for appropriate defaults (e.g., 'fintech', 'healthcare', 'e-commerce')"
}
```

---

## Outputs

### design_tokens.json
- **Path:** `agent_output/theming/design_tokens.json`
- **Format:** `json`
- **Consumers:** `fe_contract`, `wireframe`, `fe_impl`, `impl_prep`
- **Shape:**
```json
{
  "generated_at": "string -- ISO-8601 timestamp",
  "source": "string -- 'figma' | 'screenshots' | 'brand_pdf' | 'existing_css' | 'user_preferences' | 'defaults'",
  "version": "string -- token schema version (e.g., '1.0.0')",
  "colors": {
    "primary": {
      "50": "string -- hex color (e.g., '#EFF6FF')",
      "100": "string -- hex color",
      "200": "string -- hex color",
      "300": "string -- hex color",
      "400": "string -- hex color",
      "500": "string -- hex color (base shade)",
      "600": "string -- hex color",
      "700": "string -- hex color",
      "800": "string -- hex color",
      "900": "string -- hex color"
    },
    "secondary": {
      "50": "string", "100": "string", "200": "string", "300": "string", "400": "string",
      "500": "string", "600": "string", "700": "string", "800": "string", "900": "string"
    },
    "accent": {
      "50": "string", "100": "string", "200": "string", "300": "string", "400": "string",
      "500": "string", "600": "string", "700": "string", "800": "string", "900": "string"
    },
    "background": {
      "50": "string", "100": "string", "200": "string", "300": "string", "400": "string",
      "500": "string", "600": "string", "700": "string", "800": "string", "900": "string"
    },
    "surface": {
      "50": "string", "100": "string", "200": "string", "300": "string", "400": "string",
      "500": "string", "600": "string", "700": "string", "800": "string", "900": "string"
    },
    "error": {
      "50": "string", "100": "string", "200": "string", "300": "string", "400": "string",
      "500": "string", "600": "string", "700": "string", "800": "string", "900": "string"
    },
    "warning": {
      "50": "string", "100": "string", "200": "string", "300": "string", "400": "string",
      "500": "string", "600": "string", "700": "string", "800": "string", "900": "string"
    },
    "success": {
      "50": "string", "100": "string", "200": "string", "300": "string", "400": "string",
      "500": "string", "600": "string", "700": "string", "800": "string", "900": "string"
    },
    "info": {
      "50": "string", "100": "string", "200": "string", "300": "string", "400": "string",
      "500": "string", "600": "string", "700": "string", "800": "string", "900": "string"
    }
  },
  "typography": {
    "font_families": [
      {
        "name": "string -- font family name (e.g., 'Inter')",
        "category": "string -- 'heading' | 'body' | 'mono' | 'display'",
        "fallback": "string -- CSS fallback stack (e.g., 'system-ui, -apple-system, sans-serif')",
        "source": "string -- 'google_fonts' | 'local' | 'custom' | 'system'"
      }
    ],
    "font_sizes": {
      "xs": "string -- rem value (e.g., '0.75rem')",
      "sm": "string -- rem value (e.g., '0.875rem')",
      "base": "string -- rem value (e.g., '1rem')",
      "lg": "string -- rem value (e.g., '1.125rem')",
      "xl": "string -- rem value (e.g., '1.25rem')",
      "2xl": "string -- rem value (e.g., '1.5rem')",
      "3xl": "string -- rem value (e.g., '1.875rem')",
      "4xl": "string -- rem value (e.g., '2.25rem')",
      "5xl": "string -- rem value (e.g., '3rem')",
      "6xl": "string -- rem value (e.g., '3.75rem')"
    },
    "font_weights": {
      "thin": "number -- 100",
      "light": "number -- 300",
      "normal": "number -- 400",
      "medium": "number -- 500",
      "semibold": "number -- 600",
      "bold": "number -- 700",
      "extrabold": "number -- 800"
    },
    "line_heights": {
      "none": "number -- 1",
      "tight": "number -- 1.25",
      "snug": "number -- 1.375",
      "normal": "number -- 1.5",
      "relaxed": "number -- 1.625",
      "loose": "number -- 2"
    }
  },
  "spacing": {
    "scale": [0, 1, 2, 4, 6, 8, 12, 16, 24, 32, 48, 64],
    "unit": "string -- 'px' (base unit, converted to rem in CSS output)"
  },
  "border_radius": {
    "none": "string -- '0px'",
    "sm": "string -- '2px'",
    "base": "string -- '4px'",
    "md": "string -- '6px'",
    "lg": "string -- '8px'",
    "xl": "string -- '12px'",
    "2xl": "string -- '16px'",
    "full": "string -- '9999px'"
  },
  "shadows": {
    "none": "string -- 'none'",
    "sm": "string -- CSS box-shadow value (e.g., '0 1px 2px 0 rgb(0 0 0 / 0.05)')",
    "base": "string -- CSS box-shadow value",
    "md": "string -- CSS box-shadow value",
    "lg": "string -- CSS box-shadow value",
    "xl": "string -- CSS box-shadow value",
    "2xl": "string -- CSS box-shadow value",
    "inner": "string -- CSS box-shadow value (inset)"
  },
  "transitions": {
    "duration": {
      "fast": "string -- '150ms'",
      "normal": "string -- '200ms'",
      "slow": "string -- '300ms'",
      "slower": "string -- '500ms'"
    },
    "timing": {
      "ease_in": "string -- 'cubic-bezier(0.4, 0, 1, 1)'",
      "ease_out": "string -- 'cubic-bezier(0, 0, 0.2, 1)'",
      "ease_in_out": "string -- 'cubic-bezier(0.4, 0, 0.2, 1)'"
    }
  },
  "breakpoints": {
    "sm": "string -- '640px'",
    "md": "string -- '768px'",
    "lg": "string -- '1024px'",
    "xl": "string -- '1280px'",
    "2xl": "string -- '1536px'"
  },
  "accessibility": {
    "contrast_checks": [
      {
        "foreground": "string -- token path (e.g., 'colors.primary.900')",
        "background": "string -- token path (e.g., 'colors.background.50')",
        "ratio": "number -- calculated contrast ratio",
        "passes_aa": "boolean -- meets WCAG AA (4.5:1 for normal text, 3:1 for large text)",
        "passes_aaa": "boolean -- meets WCAG AAA (7:1 for normal text, 4.5:1 for large text)"
      }
    ]
  }
}
```

### tokens.css
- **Path:** `agent_output/theming/tokens.css`
- **Format:** `css`
- **Consumers:** `wireframe`, `fe_impl`, `impl_prep`
- **Shape:**
```css
/* Design Tokens - Generated by Theming Agent */
/* Source: {source} | Generated: {timestamp} */

:root {
  /* Colors - Primary */
  --color-primary-50: #EFF6FF;
  --color-primary-100: #DBEAFE;
  /* ... shades 200-900 for primary ... */

  /* Colors - Secondary */
  --color-secondary-50: #value;
  /* ... all secondary shades ... */

  /* Colors - Accent */
  --color-accent-50: #value;
  /* ... all accent shades ... */

  /* Colors - Background */
  --color-background-50: #value;
  /* ... all background shades ... */

  /* Colors - Surface */
  --color-surface-50: #value;
  /* ... all surface shades ... */

  /* Colors - Semantic: Error, Warning, Success, Info */
  --color-error-50: #value;
  /* ... all semantic color shades ... */

  /* Typography - Font Families */
  --font-heading: 'Inter', system-ui, -apple-system, sans-serif;
  --font-body: 'Inter', system-ui, -apple-system, sans-serif;
  --font-mono: 'Fira Code', 'Consolas', monospace;

  /* Typography - Font Sizes */
  --text-xs: 0.75rem;
  --text-sm: 0.875rem;
  --text-base: 1rem;
  --text-lg: 1.125rem;
  --text-xl: 1.25rem;
  --text-2xl: 1.5rem;
  --text-3xl: 1.875rem;
  --text-4xl: 2.25rem;
  --text-5xl: 3rem;
  --text-6xl: 3.75rem;

  /* Typography - Font Weights */
  --font-thin: 100;
  --font-light: 300;
  --font-normal: 400;
  --font-medium: 500;
  --font-semibold: 600;
  --font-bold: 700;
  --font-extrabold: 800;

  /* Typography - Line Heights */
  --leading-none: 1;
  --leading-tight: 1.25;
  --leading-snug: 1.375;
  --leading-normal: 1.5;
  --leading-relaxed: 1.625;
  --leading-loose: 2;

  /* Spacing */
  --space-0: 0px;
  --space-1: 0.0625rem;
  --space-2: 0.125rem;
  --space-4: 0.25rem;
  --space-6: 0.375rem;
  --space-8: 0.5rem;
  --space-12: 0.75rem;
  --space-16: 1rem;
  --space-24: 1.5rem;
  --space-32: 2rem;
  --space-48: 3rem;
  --space-64: 4rem;

  /* Border Radius */
  --radius-none: 0px;
  --radius-sm: 2px;
  --radius-base: 4px;
  --radius-md: 6px;
  --radius-lg: 8px;
  --radius-xl: 12px;
  --radius-2xl: 16px;
  --radius-full: 9999px;

  /* Shadows */
  --shadow-none: none;
  --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
  --shadow-base: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
  --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
  --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
  --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
  --shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.25);
  --shadow-inner: inset 0 2px 4px 0 rgb(0 0 0 / 0.05);

  /* Transitions */
  --duration-fast: 150ms;
  --duration-normal: 200ms;
  --duration-slow: 300ms;
  --duration-slower: 500ms;
  --ease-in: cubic-bezier(0.4, 0, 1, 1);
  --ease-out: cubic-bezier(0, 0, 0.2, 1);
  --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);

  /* Breakpoints (for reference, not directly usable in CSS vars) */
  --breakpoint-sm: 640px;
  --breakpoint-md: 768px;
  --breakpoint-lg: 1024px;
  --breakpoint-xl: 1280px;
  --breakpoint-2xl: 1536px;
}
```

### tailwind.theme.json
- **Path:** `agent_output/theming/tailwind.theme.json`
- **Format:** `json`
- **Consumers:** `wireframe`, `fe_impl`, `impl_prep`
- **Shape:**
```json
{
  "generated_at": "string -- ISO-8601 timestamp",
  "extends": {
    "colors": {
      "primary": {
        "50": "string -- hex or CSS var reference",
        "100": "string",
        "200": "string",
        "300": "string",
        "400": "string",
        "500": "string",
        "600": "string",
        "700": "string",
        "800": "string",
        "900": "string"
      },
      "secondary": {
        "50": "string", "100": "string", "200": "string", "300": "string", "400": "string",
        "500": "string", "600": "string", "700": "string", "800": "string", "900": "string"
      },
      "accent": {
        "50": "string", "100": "string", "200": "string", "300": "string", "400": "string",
        "500": "string", "600": "string", "700": "string", "800": "string", "900": "string"
      },
      "background": {
        "50": "string", "100": "string", "200": "string", "300": "string", "400": "string",
        "500": "string", "600": "string", "700": "string", "800": "string", "900": "string"
      },
      "surface": {
        "50": "string", "100": "string", "200": "string", "300": "string", "400": "string",
        "500": "string", "600": "string", "700": "string", "800": "string", "900": "string"
      },
      "error": {
        "50": "string", "100": "string", "200": "string", "300": "string", "400": "string",
        "500": "string", "600": "string", "700": "string", "800": "string", "900": "string"
      },
      "warning": {
        "50": "string", "100": "string", "200": "string", "300": "string", "400": "string",
        "500": "string", "600": "string", "700": "string", "800": "string", "900": "string"
      },
      "success": {
        "50": "string", "100": "string", "200": "string", "300": "string", "400": "string",
        "500": "string", "600": "string", "700": "string", "800": "string", "900": "string"
      },
      "info": {
        "50": "string", "100": "string", "200": "string", "300": "string", "400": "string",
        "500": "string", "600": "string", "700": "string", "800": "string", "900": "string"
      }
    },
    "fontFamily": {
      "heading": ["string -- font name", "string -- fallback"],
      "body": ["string -- font name", "string -- fallback"],
      "mono": ["string -- font name", "string -- fallback"]
    },
    "fontSize": {
      "xs": "string -- rem value",
      "sm": "string -- rem value",
      "base": "string -- rem value",
      "lg": "string -- rem value",
      "xl": "string -- rem value",
      "2xl": "string -- rem value",
      "3xl": "string -- rem value",
      "4xl": "string -- rem value",
      "5xl": "string -- rem value",
      "6xl": "string -- rem value"
    },
    "fontWeight": {
      "thin": "string -- '100'",
      "light": "string -- '300'",
      "normal": "string -- '400'",
      "medium": "string -- '500'",
      "semibold": "string -- '600'",
      "bold": "string -- '700'",
      "extrabold": "string -- '800'"
    },
    "lineHeight": {
      "none": "string -- '1'",
      "tight": "string -- '1.25'",
      "snug": "string -- '1.375'",
      "normal": "string -- '1.5'",
      "relaxed": "string -- '1.625'",
      "loose": "string -- '2'"
    },
    "spacing": {
      "0": "string -- '0px'",
      "1": "string -- '1px'",
      "2": "string -- '2px'",
      "4": "string -- '4px'",
      "6": "string -- '6px'",
      "8": "string -- '8px'",
      "12": "string -- '12px'",
      "16": "string -- '16px'",
      "24": "string -- '24px'",
      "32": "string -- '32px'",
      "48": "string -- '48px'",
      "64": "string -- '64px'"
    },
    "borderRadius": {
      "none": "string -- '0px'",
      "sm": "string -- '2px'",
      "DEFAULT": "string -- '4px'",
      "md": "string -- '6px'",
      "lg": "string -- '8px'",
      "xl": "string -- '12px'",
      "2xl": "string -- '16px'",
      "full": "string -- '9999px'"
    },
    "boxShadow": {
      "sm": "string -- CSS shadow value",
      "DEFAULT": "string -- CSS shadow value",
      "md": "string -- CSS shadow value",
      "lg": "string -- CSS shadow value",
      "xl": "string -- CSS shadow value",
      "2xl": "string -- CSS shadow value",
      "inner": "string -- CSS shadow value"
    },
    "transitionDuration": {
      "fast": "string -- '150ms'",
      "DEFAULT": "string -- '200ms'",
      "slow": "string -- '300ms'",
      "slower": "string -- '500ms'"
    },
    "transitionTimingFunction": {
      "in": "string -- cubic-bezier value",
      "out": "string -- cubic-bezier value",
      "in-out": "string -- cubic-bezier value"
    },
    "screens": {
      "sm": "string -- '640px'",
      "md": "string -- '768px'",
      "lg": "string -- '1024px'",
      "xl": "string -- '1280px'",
      "2xl": "string -- '1536px'"
    }
  }
}
```

---

## Dependencies

| Depends On | Agent | Artifact | Why |
|---|---|---|---|
| gate | `prd_reconcile` | `reconcile_result.json` (action=proceed) | Phase 3 must complete before Phase 4 — **conditional: only if PRD review/reconcile ran; if reviews were skipped (simple complexity), theming starts after user_stories complete** |

| Blocks | Agent | Why |
|---|---|---|
| output | `fe_contract` | FE contract uses design tokens for UI component specifications |
| output | `wireframe` | Wireframe agent uses design tokens and Tailwind config for styled wireframes |
| output | `impl_prep` | Impl prep agent installs tokens.css and tailwind.theme.json into project |

---

## Orchestrator Communication

### Agent Identity

This agent's ID is `theming`. Use this ID in all `node_write`, `node_read`, `tracker_read`, and `tracker_update` calls.

### Tracker Access

| Direction | Compressed Keys | Purpose |
|---|---|---|
| read | (none) | Global agent -- does not read `md.a` |
| write | `ag` | Report agent status (completed/failed) |

### Output Meta

This agent does not produce routing/coordination metadata. `output_meta: null`.

### Completion Signal

- **On success:** `tracker_update(agent_id: "theming", status: "completed")`
- **On failure:** `tracker_update(agent_id: "theming", status: "failed", add_intervention: { id: "...", agent_id: "theming", type: "error", message: "..." })`

### Scope Resolution

Global agent -- does NOT read `md.a`. Runs once for the entire project, producing a unified design token system.

```
1. node_read(agent_id: "theming", input_key: "visual_inputs") — optional, may return NR-009
2. node_read(agent_id: "theming", input_key: "user_preferences") — optional, may return NR-009
3. Process inputs → generate design tokens
4. node_write(agent_id: "theming", output_key: "design_tokens", data: {...})
5. node_write(agent_id: "theming", output_key: "tokens_css", data: {...})
6. node_write(agent_id: "theming", output_key: "tailwind_theme", data: {...})
7. tracker_update(agent_id: "theming", status: "completed")
```

---

## Compressed Keymap

Use these compressed keys in all `node_write` calls. The registry validates against this map — unknown keys are rejected.

### design_tokens.json

| Key | Full Name | Description |
|---|---|---|
| `ga` | generated_at | ISO-8601 timestamp |
| `src` | source | figma / screenshots / brand_pdf / existing_css / user_preferences / defaults |
| `ver` | version | Token schema version (e.g., '1.0.0') |
| `cl` | colors | Color palette object containing all color categories |
| `cp` | primary | Primary color shades (50-900) |
| `cs` | secondary | Secondary color shades (50-900) |
| `ca` | accent | Accent color shades (50-900) |
| `cb` | background | Background color shades (50-900) |
| `csu` | surface | Surface color shades (50-900) |
| `ce` | error | Error color shades (50-900) |
| `cw` | warning | Warning color shades (50-900) |
| `csc` | success | Success color shades (50-900) |
| `ci` | info | Info color shades (50-900) |
| `ty` | typography | Typography object |
| `tff` | font_families | Array of font family objects |
| `tfn` | name | Font family name (within font_families) |
| `tfc` | category | heading / body / mono / display (within font_families) |
| `tfb` | fallback | CSS fallback stack (within font_families) |
| `tfs` | source | google_fonts / local / custom / system (within font_families) |
| `tsz` | font_sizes | Font size scale object (xs through 6xl, rem values) |
| `twt` | font_weights | Font weight scale object (thin through extrabold, numeric values) |
| `tlh` | line_heights | Line height scale object (none through loose, numeric values) |
| `sp` | spacing | Spacing object |
| `sps` | scale | Spacing scale array (0, 1, 2, 4, ..., 64) |
| `spu` | unit | Spacing base unit (px) |
| `br` | border_radius | Border radius scale object (none through full) |
| `sh` | shadows | Shadow scale object (none through inner) |
| `tr` | transitions | Transitions object |
| `trd` | duration | Transition duration object (fast, normal, slow, slower) |
| `trt` | timing | Transition timing function object (ease_in, ease_out, ease_in_out) |
| `bp` | breakpoints | Breakpoint scale object (sm through 2xl) |
| `ax` | accessibility | Accessibility object |
| `axc` | contrast_checks | Array of contrast check results |
| `axf` | foreground | Foreground token path (within contrast_checks) |
| `axb` | background | Background token path (within contrast_checks) |
| `axr` | ratio | Calculated contrast ratio (within contrast_checks) |
| `axa` | passes_aa | Meets WCAG AA (within contrast_checks) |
| `axx` | passes_aaa | Meets WCAG AAA (within contrast_checks) |

### tokens.css

CSS output is not key-compressed -- it uses standard CSS custom property names (e.g., `--color-primary-50`). The `node_write` call for this output passes the raw CSS string.

### tailwind.theme.json

| Key | Full Name | Description |
|---|---|---|
| `ga` | generated_at | ISO-8601 timestamp |
| `ex` | extends | Tailwind theme.extend object |
| `ecl` | colors | Color definitions (within extends) |
| `eff` | fontFamily | Font family definitions (within extends) |
| `efs` | fontSize | Font size definitions (within extends) |
| `efw` | fontWeight | Font weight definitions (within extends) |
| `elh` | lineHeight | Line height definitions (within extends) |
| `esp` | spacing | Spacing definitions (within extends) |
| `ebr` | borderRadius | Border radius definitions (within extends) |
| `ebs` | boxShadow | Box shadow definitions (within extends) |
| `etd` | transitionDuration | Transition duration definitions (within extends) |
| `ett` | transitionTimingFunction | Transition timing function definitions (within extends) |
| `esc` | screens | Screen/breakpoint definitions (within extends) |

---

## Tools Required

| Tool | Purpose | Exists? |
|---|---|---|
| `node_write` | Write design token outputs via registry-validated compressed keys. Agent calls `node_write(agent_id: "theming", output_key: "design_tokens", data: {...})`. | Pending |
| `node_read` | Read upstream artifacts (visual_inputs, user_preferences). Agent calls `node_read(agent_id: "theming", input_key: "visual_inputs")`. | Pending |
| `tracker_read` | Read pipeline state. Agent calls `tracker_read(agent_id: "theming", fields: [...])`. | Pending |
| `tracker_update` | Report completion/failure. Agent calls `tracker_update(agent_id: "theming", status: "completed")`. | Pending |

---

## Guardrails

### Rules

| ID | Category | Severity | Rule |
|---|---|---|---|
| R-001 | `constraint` | `must` | Must ensure WCAG AA contrast ratios (4.5:1 for normal text, 3:1 for large text) for all primary text/background color pairs |
| R-002 | `output_quality` | `must` | Must produce valid CSS custom properties that parse without errors in all major browsers |
| R-003 | `output_quality` | `must` | Must produce a valid Tailwind config extend block that does not break Tailwind CSS compilation |
| R-004 | `constraint` | `must` | Tokens must be complete -- no undefined, null, or empty string values in any token field |
| R-005 | `confirmation_standard` | `must` | If extracting from visual input, must present extracted values to user for confirmation before finalizing outputs |
| R-006 | `constraint` | `must` | All three output files (design_tokens.json, tokens.css, tailwind.theme.json) must be synchronized -- same values in all formats |
| R-007 | `constraint` | `must` | Color shades must form a perceptually consistent scale from light (50) to dark (900) |
| R-008 | `interaction` | `should` | If no visual inputs are provided and no user preferences are available, should fall back to a neutral, professionally acceptable default palette |
| R-009 | `scope_boundary` | `must` | Must include all nine color categories: primary, secondary, accent, background, surface, error, warning, success, info |
| R-010 | `scope_boundary` | `must` | Must include the full spacing scale: 0, 1, 2, 4, 6, 8, 12, 16, 24, 32, 48, 64 |
| R-011 | `data_handling` | `should` | Should document the source of each token decision (which Figma layer, which screenshot region, which user preference) |
| R-012 | `not_allowed` | `must` | Must not use copyrighted or trademarked font names without confirming license availability |
| R-013 | `output_quality` | `should` | Font families should include appropriate fallback stacks for system fonts |
| R-014 | `constraint` | `must` | Breakpoints must be ordered in ascending pixel values |
| R-015 | `interaction` | `may` | May suggest alternative color combinations if the user's preferred colors fail WCAG AA compliance, explaining why |
| R-016 | `constraint` | `must` | Must use compressed keys as defined in the Compressed Keymap section for all `node_write` / `node_read` calls. Never use expanded/full key names. |
| R-017 | `constraint` | `must` | Must use `node_write` to write output and `node_read` to read upstream artifacts. Must not use raw file writes, generic file_writer, or any other method. |
| R-018 | `constraint` | `must` | Must call `tracker_update(agent_id: "theming", status: "completed")` before exiting on success. Must call `tracker_update(agent_id: "theming", status: "failed", add_intervention: {...})` before exiting on failure. |

### Limits

| Resource | Value |
|---|---|
| max_retries | 3 |
| max_tokens | 32000 |
| max_image_inputs | 10 |
| max_css_file_size | 500KB |
| max_pdf_pages | 20 |

---

## Scope Boundary

**In scope:**
- Extracting design tokens from Figma exports, screenshots, brand PDFs, and existing CSS files
- Prompting user for design preferences when no visual inputs are provided
- Generating complete color palettes with shades 50-900 for all nine color categories
- Generating typography tokens (font families, sizes, weights, line heights)
- Generating spacing scale, border radius, shadow, transition, and breakpoint tokens
- Producing three synchronized output formats: JSON, CSS custom properties, Tailwind config
- Validating WCAG AA contrast ratios for all color combinations
- Falling back to neutral professional defaults when no input is available
- Confirming extracted values with user before finalizing

**Out of scope:**
- Designing UI layouts, screens, or wireframes (wireframe agent's job)
- Creating component-level styles or CSS classes (FE implementation agent's job)
- Choosing a CSS framework or build tooling (analysis/impl_prep agent's scope)
- Defining animations beyond basic transition timing (implementation detail)
- Creating icon sets or illustration styles
- Generating dark mode variants unless specifically requested (future enhancement)
- Modifying any upstream artifacts (stories, analysis, spike results)

---

## Triggers

- Orchestrator detects that Phase 3 is complete: either `prd_reconcile` has returned action=proceed, OR PRD review was skipped (simple complexity) and `user_stories` are complete
- No per-module dependency; this is a global agent that runs once for the entire project

---

## Checkpoints

| ID | Description | Action |
|---|---|---|
| `CP-001` | Visual input detection: agent determines what inputs are available (Figma, screenshots, CSS, PDF, or none) | `log` |
| `CP-002` | Token extraction from visual inputs complete | `log` |
| `CP-003` | User preference prompts initiated (if no visual inputs) | `pause` |
| `CP-004` | User confirmation of extracted/generated tokens requested | `pause` |
| `CP-005` | WCAG contrast ratio validation complete | `log` |
| `CP-006` | Contrast ratio failures detected -- suggesting alternatives to user | `notify` |
| `CP-007` | All three output files generated and synchronized | `notify` |

---

## Validation Criteria

- design_tokens.json has all nine color categories, each with all ten shades (50-900)
- No token value is undefined, null, or empty string
- All hex color values are valid 6-digit hex codes prefixed with #
- All rem values in font_sizes are valid numbers followed by "rem"
- All font_weight values are valid CSS weight numbers (100-900)
- Spacing scale contains exactly the values: 0, 1, 2, 4, 6, 8, 12, 16, 24, 32, 48, 64
- Breakpoints are in ascending order (sm < md < lg < xl < 2xl)
- WCAG AA contrast checks pass for at least: primary-900 on background-50, primary-50 on primary-900, error-700 on background-50
- tokens.css parses as valid CSS (no syntax errors)
- tailwind.theme.json is valid JSON and its structure matches Tailwind's theme.extend format
- All three outputs reference the same values (synchronized)
- Font families include fallback stacks
- The source field in design_tokens.json accurately reflects which input was used

---

## Context Sources

- Figma export files (if provided by user)
- Screenshot images (if provided by user)
- Brand PDF documents (if provided by user)
- Existing CSS/SCSS/LESS files (if provided by user)
- User conversation history (for interactive preference prompts)
- WCAG 2.1 AA contrast ratio requirements (external standard reference)
- Tailwind CSS default theme (for compatible output format)

---

## Operation Mode

| Field | Value |
|---|---|
| Type | `interactive` |
| Fallback | `none` (falls back to neutral defaults if user declines to provide preferences) |

---

## Tool Gaps

| Gap ID | Description | Needed By | Impact Without |
|---|---|---|---|
| `TG-001` | Image analyzer capable of extracting dominant colors, typography patterns, and spacing from screenshots and Figma exports | this agent | Cannot process visual inputs at all; falls back to user preferences or defaults, losing the primary value proposition of visual-driven token extraction |
| `TG-002` | CSS generator that produces syntactically valid CSS custom properties from a JSON token definition | this agent | Must generate CSS inline via LLM; risk of syntax errors, inconsistent formatting, or browser-incompatible values |
| `TG-003` | Tailwind config generator that produces a valid theme.extend block from token JSON | this agent | Must generate Tailwind config inline; risk of invalid config that breaks Tailwind compilation |
| `TG-004` | WCAG contrast ratio calculator that computes luminance and contrast ratios for hex color pairs | this agent | Must estimate or calculate ratios manually; risk of shipping inaccessible color combinations |
| `TG-005` | Color palette generator that creates perceptually balanced shade scales (50-900) from a base color | this agent | Must generate shades inline; risk of perceptually inconsistent or unbalanced color scales |
| `TG-006` | `node_write` MCP tool not yet built | this agent | Cannot write output with registry validation — blocked |
| `TG-007` | `node_read` MCP tool not yet built | this agent | Cannot read upstream artifacts with field enforcement — blocked |
| `TG-008` | `tracker_read` MCP tool not yet built | this agent | Cannot read pipeline state — blocked |
| `TG-009` | `tracker_update` MCP tool not yet built | this agent | Cannot report completion — blocked |

---

## Generation Readiness

| `generate_agent` Param | Status | Notes |
|---|---|---|
| `fileName` | ready | `theming` |
| `agentName` | ready | `theming` |
| `agentRole` | ready | Design Token Generator |
| `agentDescription` | ready | Produces complete design token systems from visual inputs or user preferences in JSON, CSS, and Tailwind formats |
| `operationMode` | ready | interactive, falls back to defaults |
| `goal` | ready | Three objectives covering completeness, accessibility, and multi-format output |
| `inputs` | partial | Visual inputs are optional and varied; user preferences are free-form; input schema depends on what user provides |
| `guardrails` | ready | 18 rules covering constraints, quality, interaction, scope, data handling, universal tools, and completion signal |
| `scopeBoundary` | ready | 9 in-scope items, 7 out-of-scope items |
| `outputFormat` | ready | Three output formats (JSON, CSS, JSON) with full shapes defined |
| `triggers` | ready | Orchestrator-triggered after Phase 3 proceed gate |
| `checkpoints` | ready | 7 checkpoints covering detection, extraction, confirmation, validation, and completion |
| `validation` | ready | 13 validation criteria |
| `contextSources` | ready | 7 context sources listed |
| `metadata` | ready | Phase 4, global, interactive, both modes |
