Skip to content

Commit b4d8aee

Browse files
authored
feat: add fix suggestions for dark mode theme issues
feat: add fix suggestions for dark mode theme issues
2 parents c1e5f29 + fc95c1c commit b4d8aee

File tree

4 files changed

+73
-1
lines changed

4 files changed

+73
-1
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'helixir': patch
3+
---
4+
5+
Add auto-fix suggestions for mixed-token-hardcoded and dark-mode-shadow-invisible theme issues

packages/core/src/handlers/suggest-fix.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,39 @@ function fixThemeCompat(input: SuggestFixInput): FixSuggestion {
289289
return {
290290
original,
291291
suggestion: `background: var(${bgToken}); color: var(${fgToken});`,
292-
explanation: `Light-on-light or dark-on-dark color pairs create contrast issues. Use semantic token pairs (surface + on-surface) that maintain readable contrast across themes.`,
292+
explanation:
293+
`Light-on-light or dark-on-dark color pairs create contrast issues. ` +
294+
`Use semantic token pairs (surface + on-surface) that maintain readable contrast across themes.`,
295+
severity: 'warning',
296+
};
297+
}
298+
299+
if (input.issue === 'mixed-token-hardcoded') {
300+
const propMatch = original.match(/([a-z-]+)\s*:\s*([^;]+)/i);
301+
if (propMatch) {
302+
const [, prop, value] = propMatch;
303+
const token = suggestTokenForProperty(prop ?? 'color', tokenPrefix);
304+
return {
305+
original,
306+
suggestion: `${prop}: var(${token}, ${value?.trim()});`,
307+
explanation:
308+
`One property uses a design token while the paired property is hardcoded. ` +
309+
`When the theme switches, the token adapts but the hardcoded value stays — ` +
310+
`breaking the color pairing. Use tokens for both.`,
311+
severity: 'warning',
312+
};
313+
}
314+
}
315+
316+
if (input.issue === 'dark-mode-shadow-invisible') {
317+
const shadowToken = tokenPrefix ? `${tokenPrefix}-shadow-md` : '--shadow-md';
318+
return {
319+
original,
320+
suggestion: `box-shadow: var(${shadowToken});`,
321+
explanation:
322+
`Very low opacity shadows (alpha <= 0.1) are invisible on dark backgrounds. ` +
323+
`Use a design token that adapts shadow intensity per theme, ` +
324+
`or increase the opacity for dark mode.`,
293325
severity: 'warning',
294326
};
295327
}

packages/core/src/handlers/validation-summary.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,14 @@ export function summarizeValidation(result: ValidateComponentCodeResult): Valida
136136

137137
if (result.themeCompat) {
138138
for (const issue of result.themeCompat.issues) {
139+
const original = `${issue.property}: ${issue.value};`;
140+
const fix = tryAutoFix('theme-compat', issue.rule, original, issue.property);
139141
issues.push({
140142
severity: 'warning',
141143
category: 'themeCompat',
142144
message: issue.message,
143145
line: issue.line,
146+
...(fix ? { fix } : {}),
144147
});
145148
}
146149
}

tests/handlers/suggest-fix.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,38 @@ describe('suggestFix — library-agnostic tokens', () => {
358358
expect(result.suggestion).not.toContain('--sl-');
359359
});
360360

361+
it('fixes mixed-token-hardcoded by wrapping in var()', () => {
362+
const result = suggestFix({
363+
type: 'theme-compat',
364+
issue: 'mixed-token-hardcoded',
365+
original: 'color: #333333;',
366+
});
367+
expect(result.suggestion).toContain('var(');
368+
expect(result.explanation).toContain('token');
369+
expect(result.severity).toBe('warning');
370+
});
371+
372+
it('fixes dark-mode-shadow-invisible with token', () => {
373+
const result = suggestFix({
374+
type: 'theme-compat',
375+
issue: 'dark-mode-shadow-invisible',
376+
original: 'box-shadow: 0 2px 8px rgba(0,0,0,0.05);',
377+
});
378+
expect(result.suggestion).toContain('var(');
379+
expect(result.suggestion).toContain('shadow');
380+
expect(result.severity).toBe('warning');
381+
});
382+
383+
it('uses tokenPrefix in dark-mode-shadow-invisible fix', () => {
384+
const result = suggestFix({
385+
type: 'theme-compat',
386+
issue: 'dark-mode-shadow-invisible',
387+
original: 'box-shadow: 0 2px 8px rgba(0,0,0,0.05);',
388+
tokenPrefix: '--hx-',
389+
});
390+
expect(result.suggestion).toContain('--hx-');
391+
});
392+
361393
it('generates property-appropriate token names from prefix', () => {
362394
const result = suggestFix({
363395
type: 'token-fallback',

0 commit comments

Comments
 (0)