Skip to content

fix(dashboard): comprehensive dark mode fixes across all affected components#1110

Merged
aaight merged 1 commit intodevfrom
feature/dark-mode-fixes
Apr 15, 2026
Merged

fix(dashboard): comprehensive dark mode fixes across all affected components#1110
aaight merged 1 commit intodevfrom
feature/dark-mode-fixes

Conversation

@aaight
Copy link
Copy Markdown
Collaborator

@aaight aaight commented Apr 15, 2026

Summary

Fixes 7 categories of dark mode issues across ~25 files in web/src/, as identified in the investigation phase.

  • Native <select> dark backgrounds — Added dark:bg-card dark:text-foreground [&_option]:bg-card to all native select elements in run-filters.tsx, stats-filters.tsx, webhook-logs.tsx, agent-definition-sections.tsx, user-form-dialog.tsx, and wizard-shared.tsx
  • Theme-aware chart color system — New CHART_PALETTE_DARK hex approximations of dark-mode oklch CSS vars; new useChartColors() hook using useTheme() from next-themes to select the correct palette; updated getAgentColor() to accept optional palette parameter for backward compatibility
  • Recharts components updatedwork-item-cost-chart.tsx, work-item-duration-chart.tsx, project-work-duration-chart.tsx, project-work-table.tsx, work-item-duration-bar.tsx now use useChartColors() hook; CartesianGrid uses currentColor stroke, XAxis/YAxis tick fill uses currentColor, Legend text uses color: 'currentColor'
  • Sonner toast CSS vars fixed — Updated variable names from var(--popover) to var(--color-popover) to match Tailwind v4 naming convention
  • Toggle knob visibility — Changed bg-white to bg-white dark:bg-primary-foreground on toggle switch knob in agent-definition-shared.tsx
  • Missing dark: variants — Added dark variants for hardcoded colors: text-blue-600 dark:text-blue-400 in PR runs page, text-red-500 dark:text-red-400 and ring-red-500/60 dark:ring-red-400/60 in duration bar, text-green-500/600 dark:text-green-400 in pm-wizard steps
  • Log viewer — Added bg-muted dark:bg-black/90 for softer light mode while preserving intentional dark terminal look; text-neutral-700 dark:text-neutral-300 for readable text in both themes
  • Destructive button overrides — Replaced inline className="bg-destructive text-white hover:bg-destructive/90" with variant="destructive" in projects-table.tsx, project-general-form.tsx, and users-table.tsx

Test plan

  • All 378 unit tests pass
  • TypeScript type checks pass (both root and web/ project)
  • Biome linting passes (no errors, 3 pre-existing complexity warnings)
  • New tests/unit/web/chart-colors.test.ts validates dual-palette system
  • Manual: Toggle light/dark on runs page — verify chart colors change
  • Manual: Toggle light/dark on project stats page — verify recharts axes are readable
  • Manual: Test native selects in dark mode — verify no white background flash
  • Manual: Test toasts in dark mode — verify they use correct background color
  • Manual: Test toggle knob in dark mode — verify white knob is visible

Key decisions

  • Backward-compatible getAgentColor — The exported function still works without args (uses light palette by default), so all existing call sites and tests continue to work without changes
  • Pure buildDurationSegments/buildDurationChartData — These functions remain pure (no hooks), accepting an optional colorFn parameter. Tests mock the function without needing React context
  • Log viewer terminal style preserved — The bg-black/90 dark mode look is intentional. We added a bg-muted light-mode fallback for softer appearance without changing the terminal aesthetic

Trello card: https://trello.com/c/69df4d553feba59e3fb7dba2

🤖 Generated with Claude Code

🕵️ claude-code · claude-sonnet-4-6 · run details

Copy link
Copy Markdown
Collaborator

@nhopeatall nhopeatall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

LGTM — Well-organized dark mode fix across ~25 files with a sound architectural approach for the theme-aware chart color system.

Notes

Theme-aware chart colors: The dual-palette system (CHART_PALETTE_LIGHT / CHART_PALETTE_DARK) with the useChartColors() hook is a clean solution to Recharts' limitation of requiring hex values rather than CSS variables. The backward-compatible getAgentColor function with optional palette parameter preserves existing call sites and test compatibility. The pure functions (buildDurationSegments, buildDurationChartData) correctly accept optional colorFn to remain testable without React context.

Sonner CSS variable fix: The var(--popover)var(--color-popover) change is correct — the CSS defines --color-popover, matching Tailwind v4's naming convention. The old variable names would have resolved to nothing.

Destructive button pattern: Replacing inline className="bg-destructive text-white hover:bg-destructive/90" with variant="destructive" is the right call — AlertDialogAction already supports the variant prop and delegates to the Button component which handles dark mode correctly through the design system.

Select dark mode fix: The dark:bg-card dark:text-foreground [&_option]:bg-card pattern is consistently applied across all native <select> elements. The [&_option]:bg-card selector targets <option> children which inherit the OS-default white background in dark mode — correct fix for the "white flash" issue.

Variable shadowing in work-item-duration-bar.tsx: The const getAgentColor = useChartColors() at component level intentionally shadows the module-level getAgentColor import. The import is still needed as the default fallback in the buildDurationSegments pure function. This is fine — the shadowing is scoped to the component function body.

All CI checks pass. Tests cover the dual-palette system adequately.

🕵️ claude-code · claude-opus-4-6 · run details

@aaight aaight merged commit 310a6f7 into dev Apr 15, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants