Build QA hooks: a lightweight design audit over staged content (no disk I/O).
(_cwd: string)
| 148 | |
| 149 | /** Build QA hooks: a lightweight design audit over staged content (no disk I/O). */ |
| 150 | function buildQaHooks(_cwd: string) { |
| 151 | return { |
| 152 | designAudit: async (files: Array<{ path: string; content: string }>) => { |
| 153 | const issues: Array<{ severity: 'high' | 'medium' | 'low'; message: string }> = []; |
| 154 | for (const f of files) { |
| 155 | if (!/\.(tsx|jsx|css)$/.test(f.path)) continue; |
| 156 | const lines = f.content.split('\n'); |
| 157 | let hasDark = /\bdark:/.test(f.content); |
| 158 | let usesLightBg = /\b(bg-white|bg-gray-50|bg-gray-100)\b/.test(f.content); |
| 159 | lines.forEach((line, i) => { |
| 160 | // raw hex in className |
| 161 | if (/className=["'][^"']*#[0-9a-fA-F]{3,6}/.test(line)) { |
| 162 | issues.push({ severity: 'medium', message: `${f.path}:${i + 1} raw hex in className — use a token` }); |
| 163 | } |
| 164 | // <img> without alt |
| 165 | if (/<img\b/.test(line) && !/\balt=/.test(line)) { |
| 166 | issues.push({ severity: 'high', message: `${f.path}:${i + 1} <img> missing alt (a11y)` }); |
| 167 | } |
| 168 | }); |
| 169 | // dark-mode coverage: a component using light bg with no dark: variants |
| 170 | if (usesLightBg && !hasDark) { |
| 171 | issues.push({ severity: 'low', message: `${f.path} uses light backgrounds with no dark: variants` }); |
| 172 | } |
| 173 | } |
| 174 | return issues; |
| 175 | }, |
| 176 | }; |
| 177 | } |