Extract design tokens from tailwind config + CSS :root custom properties.
(cwd: string)
| 121 | |
| 122 | /** Extract design tokens from tailwind config + CSS :root custom properties. */ |
| 123 | async function extractDesignTokens(cwd: string): Promise<string | undefined> { |
| 124 | const pieces: string[] = []; |
| 125 | // CSS custom properties from common entry files. |
| 126 | const cssCandidates = ['src/index.css', 'src/App.css', 'src/styles/globals.css', 'styles/globals.css', 'app/globals.css']; |
| 127 | for (const rel of cssCandidates) { |
| 128 | try { |
| 129 | const content = await fs.readFile(path.join(cwd, rel), 'utf-8'); |
| 130 | const rootBlock = content.match(/:root\s*\{([\s\S]*?)\}/); |
| 131 | if (rootBlock) { |
| 132 | const vars = rootBlock[1]!.split('\n').map(l => l.trim()).filter(l => l.startsWith('--')).join('\n'); |
| 133 | if (vars) pieces.push(`/* ${rel} :root */\n${vars}`); |
| 134 | } |
| 135 | } catch { /* missing */ } |
| 136 | } |
| 137 | // Tailwind theme.extend.colors (just the snippet, not the whole config). |
| 138 | for (const rel of ['tailwind.config.js', 'tailwind.config.ts', 'tailwind.config.cjs']) { |
| 139 | try { |
| 140 | const content = await fs.readFile(path.join(cwd, rel), 'utf-8'); |
| 141 | const colors = content.match(/colors\s*:\s*\{[\s\S]*?\n\s{4,6}\}/); |
| 142 | if (colors) pieces.push(`/* ${rel} colors */\n${colors[0]}`); |
| 143 | break; |
| 144 | } catch { /* missing */ } |
| 145 | } |
| 146 | return pieces.length ? pieces.join('\n\n') : undefined; |
| 147 | } |
| 148 | |
| 149 | /** Build QA hooks: a lightweight design audit over staged content (no disk I/O). */ |
| 150 | function buildQaHooks(_cwd: string) { |