fix(ui): add dark mode variants to dashboard, layout, notification and chargeability components

Add missing dark: class variants for backgrounds, borders, and text across
dashboard widgets, AppShell sidebar, notification cards, and the chargeability
report table. Replace hardcoded slate/gray hex values with CSS variable
references. Fix chargeability hover tint and remove ineffective sticky thead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-10 09:20:50 +02:00
parent 2a91257e69
commit 9ba49c9ab8
9 changed files with 46 additions and 42 deletions
@@ -16,14 +16,14 @@ export function AddWidgetModal({ onAdd, onClose }: AddWidgetModalProps) {
if (e.target === e.currentTarget) onClose();
}}
>
<div className="bg-white rounded-xl shadow-2xl w-full max-w-2xl max-h-[80vh] flex flex-col">
<div className="bg-white dark:bg-gray-900 rounded-xl shadow-2xl w-full max-w-2xl max-h-[80vh] flex flex-col border border-transparent dark:border-gray-700/60">
{/* Header */}
<div className="flex items-center justify-between px-6 py-4 border-b border-gray-200">
<h2 className="text-lg font-semibold text-gray-900">Add Widget</h2>
<div className="flex items-center justify-between px-6 py-4 border-b border-gray-200 dark:border-gray-700">
<h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100">Add Widget</h2>
<button
type="button"
onClick={onClose}
className="text-gray-400 hover:text-gray-600 text-2xl leading-none"
className="text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 text-2xl leading-none"
>
×
</button>
@@ -40,13 +40,13 @@ export function AddWidgetModal({ onAdd, onClose }: AddWidgetModalProps) {
onAdd(def.type);
onClose();
}}
className="flex items-start gap-4 p-4 border border-gray-200 rounded-xl hover:border-brand-400 hover:bg-brand-50 transition-colors text-left"
className="flex items-start gap-4 p-4 border border-gray-200 dark:border-gray-700 rounded-xl hover:border-brand-400 hover:bg-brand-50 dark:hover:border-[rgb(var(--accent-500))] dark:hover:bg-[rgb(var(--accent-500)/0.08)] transition-colors text-left"
>
<span className="text-3xl shrink-0">{def.icon}</span>
<div>
<div className="font-semibold text-gray-900 text-sm">{def.label}</div>
<div className="text-xs text-gray-500 mt-1">{def.description}</div>
<div className="text-xs text-gray-400 mt-1">
<div className="font-semibold text-gray-900 dark:text-gray-100 text-sm">{def.label}</div>
<div className="text-xs text-gray-500 dark:text-gray-400 mt-1">{def.description}</div>
<div className="text-xs text-gray-400 dark:text-gray-500 mt-1">
Default: {def.defaultSize.w}×{def.defaultSize.h} grid units
</div>
</div>
@@ -74,7 +74,7 @@ export function WidgetContainer({
className={`rounded-xl border px-3 py-1.5 text-[11px] font-semibold transition ${
showDetails
? "border-brand-200 bg-brand-50 text-brand-700 hover:bg-brand-100 dark:border-brand-500/30 dark:bg-brand-500/10 dark:text-brand-300"
: "border-gray-200 bg-white/80 text-gray-500 hover:border-gray-300 hover:text-gray-700 dark:border-gray-700 dark:bg-gray-900/70 dark:text-gray-400 dark:hover:text-gray-200"
: "border-gray-200 bg-white/80 text-gray-500 hover:border-gray-300 hover:text-gray-700 dark:border-gray-600/60 dark:bg-[rgb(var(--surface-elevated))] dark:text-gray-400 dark:hover:text-gray-200"
}`}
title={showDetails ? "Hide details" : "Show details"}
>
@@ -97,7 +97,7 @@ export function WidgetContainer({
</div>
</div>
<div className="mx-4 border-t border-gray-200/80 dark:border-gray-800" />
<div className="mx-4 border-t border-gray-200/80 dark:border-gray-700/40" />
<div className="flex-1 overflow-auto p-4 pt-3">{children}</div>
</motion.div>
@@ -97,8 +97,8 @@ export default function PeakTimesChart({
}
return (
<div className="flex h-full min-h-[15rem] flex-col rounded-[22px] border border-slate-200/80 bg-[linear-gradient(180deg,rgba(255,255,255,0.98),rgba(248,250,252,0.96))] p-3 shadow-sm dark:border-slate-700/70 dark:bg-[linear-gradient(180deg,rgba(15,23,42,0.96),rgba(15,23,42,0.98))]">
<div className="flex flex-wrap items-center justify-between gap-2 border-b border-slate-200/70 pb-2 dark:border-slate-700/60">
<div className="flex h-full min-h-[15rem] flex-col rounded-[22px] border border-gray-200/80 bg-[linear-gradient(180deg,rgba(255,255,255,0.98),rgba(248,250,252,0.96))] p-3 shadow-sm dark:border-gray-700/60 dark:bg-[linear-gradient(180deg,rgb(var(--surface-card)/0.97),rgb(var(--surface-card)/0.99))]">
<div className="flex flex-wrap items-center justify-between gap-2 border-b border-gray-200/70 pb-2 dark:border-gray-700/60">
<div className="text-[11px] font-semibold uppercase tracking-[0.14em] text-slate-400">
Overall Utilization
</div>
@@ -173,15 +173,15 @@ export default function PeakTimesChart({
onClick={() => onSelectedPeriodChange?.(row.period)}
style={{
backgroundColor: isPinned
? "rgba(14, 165, 233, 0.08)"
? "rgb(var(--accent-400) / 0.12)"
: isActive
? "rgba(148, 163, 184, 0.08)"
? "rgb(var(--accent-400) / 0.05)"
: "transparent",
}}
>
<div className="relative flex min-h-0 flex-1 w-full items-end justify-center px-0.5">
<div className="relative h-full w-full max-w-[34px] sm:max-w-[42px]">
<div className="absolute inset-x-0 bottom-0 h-full rounded-t-xl bg-slate-100 dark:bg-slate-800/80" />
<div className="absolute inset-x-0 bottom-0 h-full rounded-t-xl bg-gray-100 dark:bg-gray-700/60" />
<div
className={`absolute inset-x-0 bottom-0 rounded-t-xl transition-all duration-150 ${utilizationBarTone(row.utilizationPct)} ${
isActive ? "opacity-100" : "opacity-80 group-hover:opacity-100"
@@ -286,7 +286,7 @@ export function PeakTimesWidget({ config, onConfigChange }: WidgetProps) {
].map((metric) => (
<div
key={metric.label}
className="min-w-0 flex-1 rounded-full border border-slate-200/80 bg-white/85 px-3 py-1.5 shadow-sm dark:border-slate-700/70 dark:bg-slate-900/60"
className="min-w-0 flex-1 rounded-full border border-gray-200/80 bg-white/85 px-3 py-1.5 shadow-sm dark:border-gray-700/60 dark:bg-[rgb(var(--surface-elevated)/0.85)]"
>
<div className="flex items-center justify-between gap-2">
<span className="text-[10px] font-semibold uppercase tracking-[0.14em] text-slate-400">
@@ -329,8 +329,8 @@ export function PeakTimesWidget({ config, onConfigChange }: WidgetProps) {
</div>
<div className="mt-2 min-h-0 lg:mt-0">
<div className="flex h-full flex-col rounded-[22px] border border-slate-200/80 bg-[linear-gradient(180deg,rgba(255,255,255,0.98),rgba(248,250,252,0.96))] p-3 shadow-sm dark:border-slate-700/70 dark:bg-[linear-gradient(180deg,rgba(15,23,42,0.96),rgba(15,23,42,0.98))]">
<div className="flex flex-wrap items-start justify-between gap-2 border-b border-slate-200/70 pb-2 dark:border-slate-700/60">
<div className="flex h-full flex-col rounded-[22px] border border-gray-200/80 bg-[linear-gradient(180deg,rgba(255,255,255,0.98),rgba(248,250,252,0.96))] p-3 shadow-sm dark:border-gray-700/60 dark:bg-[linear-gradient(180deg,rgb(var(--surface-card)/0.97),rgb(var(--surface-card)/0.99))]">
<div className="flex flex-wrap items-start justify-between gap-2 border-b border-gray-200/70 pb-2 dark:border-gray-700/60">
<div>
<div className="text-[11px] font-semibold uppercase tracking-[0.14em] text-slate-400">
Department Utilization
@@ -373,7 +373,7 @@ export function PeakTimesWidget({ config, onConfigChange }: WidgetProps) {
</div>
</div>
<div
className="relative h-2.5 overflow-visible rounded-full bg-slate-100 dark:bg-slate-800/80"
className="relative h-2.5 overflow-visible rounded-full bg-gray-100 dark:bg-gray-700/60"
title={`${group.name}: ${group.utilizationPct}% utilization, ${formatHours(group.hours)}h booked, ${formatHours(group.capacityHours)}h capacity, ${formatHours(group.remainingHours)}h free, ${formatHours(group.overbookedHours)}h overbooked`}
>
<div
@@ -391,7 +391,7 @@ export function PeakTimesWidget({ config, onConfigChange }: WidgetProps) {
);
})
) : (
<div className="rounded-2xl border border-dashed border-slate-200 bg-slate-50/80 px-3 py-4 text-sm text-slate-400 dark:border-slate-700 dark:bg-slate-900/40">
<div className="rounded-2xl border border-dashed border-gray-200 bg-gray-50/80 px-3 py-4 text-sm text-gray-400 dark:border-gray-700 dark:bg-gray-800/40">
No department data in the selected month.
</div>
)}
@@ -48,7 +48,7 @@ function StatCard({
return (
<FadeIn delay={delay} direction="up">
<div
className={`rounded-2xl border border-gray-200 bg-white/80 p-4 shadow-sm dark:border-gray-700 dark:bg-gray-900/70 hover-lift cursor-default ${
className={`rounded-2xl border border-gray-200 bg-white/80 p-4 shadow-sm dark:border-gray-700/60 dark:bg-[rgb(var(--surface-elevated))] hover-lift cursor-default ${
accentColor ? `border-l-[3px] ${accentBorder}` : ""
}`}
>
@@ -112,7 +112,7 @@ export function StatCardsWidget(props: Partial<WidgetProps> = {}) {
{[...Array(4)].map((_, i) => (
<div
key={i}
className="rounded-2xl border border-gray-200 bg-gray-100 p-4 dark:border-gray-700 dark:bg-gray-800"
className="rounded-2xl border border-gray-200 bg-gray-100 p-4 dark:border-gray-700/60 dark:bg-[rgb(var(--surface-elevated))]"
>
<div className="h-3 w-20 shimmer-skeleton rounded" />
<div className="h-7 w-16 bg-gray-300 dark:bg-gray-600 rounded" />