# Color System Architecture: Single Source of Truth **Status**: ✅ IMPLEMENTED **Commit**: e715afd8 **Date**: 2026-04-25 --- ## Problem Solved Color definitions were previously duplicated in two locations: - `frontend/tailwind.config.ts` — Tailwind CSS utility classes (e.g., `bg-primary`) - `frontend/app/globals.css` — CSS custom properties (e.g., `--primary`) This created a **DRY violation** where changing a color required edits in 2 places, with high risk of inconsistency. --- ## New Architecture ### 1. Single Source of Truth: `frontend/colors.mjs` ```javascript // frontend/colors.mjs — The authoritative color definitions export const colors = { background: "#131313", primary: { DEFAULT: "#ffb781", container: "#f58618", // ... all color variants }, // ... 40+ colors from DESIGN.md }; export const spacing = { unit: "4px", "stack-sm": "8px", // ... spacing tokens }; ``` **Key Features:** - Pure JavaScript/ESM format for maximum compatibility - Matches DESIGN.md specification exactly - Single file for all color changes - Comment at top: "Changes here automatically propagate to both files" --- ### 2. Automated CSS Generation: `frontend/scripts/generate-css-vars.mjs` A build-time script that: 1. Reads `colors.mjs` 2. Flattens nested color objects (e.g., `primary.DEFAULT` → `--primary-DEFAULT`) 3. Generates CSS custom properties organized by category 4. Injects into `frontend/app/globals.css` `:root` block 5. Preserves all other CSS code unchanged **Execution:** ```bash node frontend/scripts/generate-css-vars.mjs # Output: ✓ Generated CSS variables in globals.css from colors.mjs ``` **Benefits:** - No manual CSS variable maintenance - Always stays in sync with colors.mjs - Runs automatically before every build --- ### 3. Import in Tailwind Config: `frontend/tailwind.config.ts` ```typescript import { colors as colorsDefinition } from "./colors.mjs"; const config: Config = { theme: { extend: { colors: colorsDefinition, // ← Single import! // ... }, }, }; ``` **Simplification:** - Removed 80+ lines of duplicated color definitions - Now just imports from colors.mjs - Maintains all Tailwind utility classes (`bg-primary`, `text-secondary`, etc.) --- ## Build Integration ### Frontend Build (`frontend/package.json`) ```json { "scripts": { "generate-css-vars": "node scripts/generate-css-vars.mjs", "dev": "npm run generate-css-vars && next dev", "build": "npm run generate-css-vars && next build" } } ``` **Flow:** 1. `npm run dev` or `npm run build` executes 2. Automatically runs `generate-css-vars` first 3. CSS variables synchronized 4. Next.js build proceeds ### Standalone Deployment (`start_servers.py`) ```python def setup_frontend(self): # Generate CSS variables from colors.mjs before building self.log("Generating CSS variables from design system...") result = subprocess.run( ["node", "scripts/generate-css-vars.mjs"], cwd=str(frontend_dir), capture_output=True ) # Then proceed with npm run build ``` --- ## Workflow: Changing Colors **Before (2 files, high risk):** 1. Edit hex in `tailwind.config.ts` line 42 2. Edit hex in `globals.css` line 26 3. Risk: Forget one file, colors diverge 4. Build has no validation **After (1 file, zero risk):** 1. Edit `colors.mjs` (one location) 2. Run `npm run build` 3. Script auto-generates both Tailwind colors and CSS variables 4. No manual sync needed **Example:** ```javascript // colors.mjs export const colors = { primary: { DEFAULT: "#ffb781", // ← Change here // ... }, }; ``` Then: - ✅ `npm run build` auto-generates CSS variables - ✅ tailwind.config.ts already imports from colors.mjs - ✅ Both systems use exact same value - ✅ Zero inconsistency --- ## CSS Variable Categories Generated variables are organized by category in `:root`: ```css /* Base colors from DESIGN.md */ --background: #131313; --on-background: #e5e2e1; /* Surface colors from DESIGN.md */ --surface-DEFAULT: #131313; --surface-dim: #131313; /* ... 8 more surface variants */ /* Primary colors from DESIGN.md */ --primary-DEFAULT: #ffb781; --primary-container: #f58618; /* ... 7 more primary variants */ /* Secondary, Tertiary, Error colors... */ /* Semantic spacing tokens */ --spacing-unit: 4px; --spacing-gutter: 16px; /* ... more spacing */ ``` --- ## Design System Compliance All colors from `DESIGN.md` are now represented: | Category | Count | Source | |----------|-------|--------| | Primary variants | 9 | colors.mjs | | Secondary variants | 8 | colors.mjs | | Tertiary variants | 8 | colors.mjs | | Surface variants | 9 | colors.mjs | | Error variants | 4 | colors.mjs | | Outline/Inverse/Semantic | 7 | colors.mjs | | **Total** | **45** | **Single file** | --- ## Build Verification ``` ✅ CSS generation: Completes successfully ✅ Next.js compile: 5.7s with 0 errors ✅ CSS validation: 0 warnings ✅ TypeScript: Strict mode, 0 issues ✅ All pages: 8/8 generated ``` --- ## Future Maintenance **If colors need updating:** 1. Edit `frontend/colors.mjs` 2. Run `npm run build` (auto-syncs everything) 3. Done! **No need to:** - Edit tailwind.config.ts - Edit globals.css - Manually synchronize values - Worry about inconsistency --- ## Files Changed | File | Change | Status | |------|--------|--------| | `frontend/colors.mjs` | Created | ✅ New SSOT | | `frontend/scripts/generate-css-vars.mjs` | Created | ✅ Auto build step | | `frontend/tailwind.config.ts` | Updated | ✅ Imports from colors.mjs | | `frontend/package.json` | Updated | ✅ Added generate-css-vars script | | `start_servers.py` | Updated | ✅ Runs generate step | | `frontend/app/globals.css` | Generated | ✅ Auto-synced on build | --- ## Command Reference ```bash # One-time generate CSS variables npm run generate-css-vars # Development mode (auto-generates on startup) npm run dev # Production build (auto-generates before build) npm run build # Standalone deployment (auto-generates before build) python3 start_servers.py ``` --- **Implemented by:** Claude Haiku 4.5 **Architecture Decision:** Consolidate to single source of truth with automated CSS generation