fix(ui): neutralise dark theme — eliminate blue-shifted grays across all surfaces

Replace blue-shifted CSS variable values with balanced neutral RGB, add
comprehensive dark-mode overrides for bg-gray-*, border-gray-*, text-gray-*,
and their dark: variant forms. Remove light-mode text/border overrides that
leaked into both modes. Replace hardcoded rgba(255,255,255,...) in component
classes with CSS variable references. Merge duplicate fadeSlideIn keyframe
into fadeSlideUp. Change .app-data-table overflow to clip for sticky compat.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-10 09:20:38 +02:00
parent db892ae285
commit 2a91257e69
+183 -84
View File
@@ -108,16 +108,16 @@
.dark { .dark {
color-scheme: dark; color-scheme: dark;
--surface-page: 10 10 12; --surface-page: 10 10 10;
--surface-card: 16 17 19; --surface-card: 17 17 17;
--surface-elevated: 22 23 26; --surface-elevated: 24 24 24;
--surface-input: 13 14 16; --surface-input: 14 14 14;
--border-subtle: 36 38 44; --border-subtle: 38 38 38;
--border-input: 54 57 66; --border-input: 58 58 58;
--text-primary: 237 242 247; --text-primary: 240 241 243;
--text-secondary: 196 207 223; --text-secondary: 205 207 210;
--text-muted: 147 161 185; --text-muted: 158 160 164;
--text-very-muted: 118 133 161; --text-very-muted: 130 132 136;
--shadow-soft: 0 0 0 / 0.45; --shadow-soft: 0 0 0 / 0.45;
--shadow-strong: 0 0 0 / 0.75; --shadow-strong: 0 0 0 / 0.75;
} }
@@ -143,7 +143,7 @@
background-image: background-image:
radial-gradient(ellipse 80% 50% at 50% -10%, rgba(var(--accent-400), 0.07) 0%, transparent 60%), radial-gradient(ellipse 80% 50% at 50% -10%, rgba(var(--accent-400), 0.07) 0%, transparent 60%),
radial-gradient(circle at top left, rgb(var(--accent-600) / 0.16), transparent 26rem), radial-gradient(circle at top left, rgb(var(--accent-600) / 0.16), transparent 26rem),
linear-gradient(180deg, rgb(15 23 42 / 0.35), transparent 28rem); linear-gradient(180deg, rgb(10 10 12 / 0.35), transparent 28rem);
} }
h1, h1,
@@ -220,6 +220,26 @@
background-color: rgb(var(--surface-elevated)) !important; background-color: rgb(var(--surface-elevated)) !important;
} }
.dark .bg-gray-200 {
background-color: rgb(var(--surface-elevated)) !important;
}
.dark .bg-gray-300 {
background-color: rgb(var(--border-subtle)) !important;
}
.dark .bg-gray-400 {
background-color: rgb(var(--border-input)) !important;
}
.dark .bg-gray-500 {
background-color: rgb(80 80 80) !important;
}
.dark .bg-gray-600 {
background-color: rgb(var(--border-input)) !important;
}
.dark .border-gray-100 { .dark .border-gray-100 {
border-color: rgb(var(--border-subtle)) !important; border-color: rgb(var(--border-subtle)) !important;
} }
@@ -256,31 +276,11 @@
color: rgb(var(--text-very-muted)) !important; color: rgb(var(--text-very-muted)) !important;
} }
.text-gray-900 { /* Dark variant text overrides — catches className="dark:text-gray-*" */
color: rgb(var(--text-primary)) !important; .dark .dark\:text-gray-100 { color: rgb(var(--text-primary)) !important; }
} .dark .dark\:text-gray-200 { color: rgb(var(--text-secondary)) !important; }
.dark .dark\:text-gray-300 { color: rgb(var(--text-muted)) !important; }
.text-gray-800, .dark .dark\:text-gray-400 { color: rgb(var(--text-very-muted)) !important; }
.text-gray-700 {
color: rgb(var(--text-secondary)) !important;
}
.text-gray-600,
.text-gray-500 {
color: rgb(var(--text-muted)) !important;
}
.text-gray-400 {
color: rgb(var(--text-very-muted)) !important;
}
.border-gray-200 {
border-color: rgb(var(--border-subtle)) !important;
}
.border-gray-300 {
border-color: rgb(var(--border-input)) !important;
}
.dark input, .dark input,
.dark select, .dark select,
@@ -295,15 +295,13 @@
color: rgb(var(--text-muted)); color: rgb(var(--text-muted));
} }
/* Table alternating / hover */ /* Table row / interactive hover */
.dark .hover\:bg-gray-50:hover { .dark .hover\:bg-gray-50:hover,
.dark .hover\:bg-gray-100:hover,
.dark .hover\:bg-gray-200:hover {
background-color: rgb(var(--surface-elevated)) !important; background-color: rgb(var(--surface-elevated)) !important;
} }
.dark .divide-gray-100 > * + * {
border-color: rgb(var(--border-subtle)) !important;
}
/* Status badge adjustments in dark mode - keep them readable */ /* Status badge adjustments in dark mode - keep them readable */
.dark .bg-green-100 { .dark .bg-green-100 {
background-color: rgb(6 78 59 / 0.4) !important; background-color: rgb(6 78 59 / 0.4) !important;
@@ -332,12 +330,6 @@
.dark .text-red-700 { .dark .text-red-700 {
color: rgb(248 113 113) !important; color: rgb(248 113 113) !important;
} }
.dark .bg-gray-100 {
background-color: rgb(var(--surface-elevated)) !important;
}
.dark .text-gray-700 {
color: rgb(var(--text-secondary)) !important;
}
.dark .bg-purple-100 { .dark .bg-purple-100 {
background-color: rgb(76 29 149 / 0.4) !important; background-color: rgb(76 29 149 / 0.4) !important;
} }
@@ -402,24 +394,141 @@
color: rgb(var(--text-primary)); color: rgb(var(--text-primary));
} }
/* Divide override for gray-50 (ChargeabilityWidget sticky headers) */ /* Divide overrides */
.dark .divide-gray-50 > * + * { border-color: rgb(var(--border-subtle)) !important; } .dark .divide-gray-50 > * + *,
.dark .divide-gray-100 > * + *,
.dark .dark\:divide-gray-800 > * + * { border-color: rgb(var(--border-subtle)) !important; }
/* Slate-* normalization — map to CSS variable surfaces */ /* ═══════════════════════════════════════════════════════════════════════════
.dark .bg-slate-700, COMPREHENSIVE DARK SURFACE NORMALIZATION
.dark .bg-slate-800 { Tailwind's gray-700/800/900 and slate-* palette is blue-shifted.
In dark mode we remap all of these to our neutral CSS variable surfaces.
Tailwind v3 with darkMode:"class" generates selectors like:
.dark .dark\:bg-gray-800 { background-color: rgb(31 41 55 / ...) }
We override them here with neutral surfaces + !important.
Two forms per class:
1. Plain — matches className="bg-gray-800" (no variant)
2. dark\: — matches className="dark:bg-gray-800" (Tailwind dark variant)
═══════════════════════════════════════════════════════════════════════════ */
/* ── Light grays: gray-50 through gray-200 → elevated surface ──────────── */
.dark .dark\:bg-gray-50,
.dark .dark\:bg-gray-100,
.dark .dark\:bg-gray-200 {
background-color: rgb(var(--surface-elevated)) !important; background-color: rgb(var(--surface-elevated)) !important;
} }
.dark .bg-slate-900,
.dark .bg-gray-900 { /* ── Mid grays: gray-300 through gray-600 → border/mid tones ──────────── */
.dark .dark\:bg-gray-300 {
background-color: rgb(var(--border-subtle)) !important;
}
.dark .dark\:bg-gray-400,
.dark .dark\:bg-gray-600 {
background-color: rgb(var(--border-input)) !important;
}
.dark .dark\:bg-gray-500 {
background-color: rgb(80 80 80) !important;
}
/* ── Elevated surface: gray-700, gray-800, slate-700, slate-800 ─────────── */
.dark .bg-gray-700, .dark .dark\:bg-gray-700,
.dark .bg-gray-800, .dark .dark\:bg-gray-800,
.dark .bg-slate-700, .dark .dark\:bg-slate-700,
.dark .bg-slate-800, .dark .dark\:bg-slate-800 {
background-color: rgb(var(--surface-elevated)) !important;
}
/* ── Card surface: gray-900, slate-900, slate-950 ───────────────────────── */
.dark .bg-gray-900, .dark .dark\:bg-gray-900,
.dark .bg-slate-900, .dark .dark\:bg-slate-900,
.dark .bg-slate-950, .dark .dark\:bg-slate-950 {
background-color: rgb(var(--surface-card)) !important; background-color: rgb(var(--surface-card)) !important;
} }
.dark .border-slate-800,
.dark .border-slate-700 { /* ── Opacity variants: elevated surface (/95 → /20) ────────────────────── */
.dark .bg-gray-700\/95, .dark .dark\:bg-gray-700\/95,
.dark .bg-gray-700\/80, .dark .dark\:bg-gray-700\/80,
.dark .bg-gray-700\/70, .dark .dark\:bg-gray-700\/70,
.dark .bg-gray-700\/60, .dark .dark\:bg-gray-700\/60,
.dark .bg-gray-700\/50, .dark .dark\:bg-gray-700\/50,
.dark .bg-gray-700\/40, .dark .dark\:bg-gray-700\/40,
.dark .bg-gray-800\/95, .dark .dark\:bg-gray-800\/95,
.dark .bg-gray-800\/80, .dark .dark\:bg-gray-800\/80,
.dark .bg-gray-800\/70, .dark .dark\:bg-gray-800\/70,
.dark .bg-gray-800\/60, .dark .dark\:bg-gray-800\/60,
.dark .bg-gray-800\/50, .dark .dark\:bg-gray-800\/50,
.dark .bg-gray-800\/40, .dark .dark\:bg-gray-800\/40,
.dark .bg-gray-800\/30, .dark .dark\:bg-gray-800\/30,
.dark .bg-slate-700\/95, .dark .dark\:bg-slate-700\/95,
.dark .bg-slate-700\/80, .dark .dark\:bg-slate-700\/80,
.dark .bg-slate-700\/70, .dark .dark\:bg-slate-700\/70,
.dark .bg-slate-700\/60, .dark .dark\:bg-slate-700\/60,
.dark .bg-slate-700\/50, .dark .dark\:bg-slate-700\/50,
.dark .bg-slate-800\/95, .dark .dark\:bg-slate-800\/95,
.dark .bg-slate-800\/80, .dark .dark\:bg-slate-800\/80,
.dark .bg-slate-800\/70, .dark .dark\:bg-slate-800\/70,
.dark .bg-slate-800\/60, .dark .dark\:bg-slate-800\/60,
.dark .bg-slate-800\/50, .dark .dark\:bg-slate-800\/50 {
background-color: rgb(var(--surface-elevated) / 0.9) !important;
}
/* ── Opacity variants: card surface (/95 → /20) ─────────────────────────── */
.dark .bg-gray-900\/95, .dark .dark\:bg-gray-900\/95,
.dark .bg-gray-900\/70, .dark .dark\:bg-gray-900\/70,
.dark .bg-gray-900\/60, .dark .dark\:bg-gray-900\/60,
.dark .bg-gray-900\/50, .dark .dark\:bg-gray-900\/50,
.dark .bg-gray-900\/40, .dark .dark\:bg-gray-900\/40,
.dark .bg-gray-900\/30, .dark .dark\:bg-gray-900\/30,
.dark .bg-gray-900\/25, .dark .dark\:bg-gray-900\/25,
.dark .bg-gray-900\/20, .dark .dark\:bg-gray-900\/20,
.dark .bg-slate-900\/95, .dark .dark\:bg-slate-900\/95,
.dark .bg-slate-900\/70, .dark .dark\:bg-slate-900\/70,
.dark .bg-slate-900\/60, .dark .dark\:bg-slate-900\/60,
.dark .bg-slate-900\/50, .dark .dark\:bg-slate-900\/50,
.dark .bg-slate-900\/40, .dark .dark\:bg-slate-900\/40,
.dark .bg-slate-900\/30, .dark .dark\:bg-slate-900\/30,
.dark .bg-slate-950\/95, .dark .dark\:bg-slate-950\/95,
.dark .bg-slate-950\/70, .dark .dark\:bg-slate-950\/70,
.dark .bg-slate-950\/60, .dark .dark\:bg-slate-950\/60,
.dark .bg-slate-950\/40, .dark .dark\:bg-slate-950\/40 {
background-color: rgb(var(--surface-card) / 0.9) !important;
}
/* ── Hover state overrides ──────────────────────────────────────────────── */
.dark .hover\:bg-gray-700:hover, .dark .dark\:hover\:bg-gray-700:hover,
.dark .hover\:bg-gray-800:hover, .dark .dark\:hover\:bg-gray-800:hover,
.dark .hover\:bg-slate-700:hover, .dark .dark\:hover\:bg-slate-700:hover,
.dark .hover\:bg-slate-800:hover, .dark .dark\:hover\:bg-slate-800:hover {
background-color: rgb(var(--surface-elevated)) !important;
}
.dark .hover\:bg-gray-900:hover, .dark .dark\:hover\:bg-gray-900:hover,
.dark .hover\:bg-slate-900:hover, .dark .dark\:hover\:bg-slate-900:hover {
background-color: rgb(var(--surface-card)) !important;
}
/* ── Hover opacity variants ────────────────────────────────────────────── */
.dark .dark\:hover\:bg-gray-800\/50:hover,
.dark .dark\:hover\:bg-gray-800\/30:hover,
.dark .dark\:hover\:bg-gray-700\/50:hover {
background-color: rgb(var(--surface-elevated) / 0.5) !important;
}
/* ── Border normalization ───────────────────────────────────────────────── */
.dark .border-gray-600, .dark .dark\:border-gray-600,
.dark .border-gray-700, .dark .dark\:border-gray-700,
.dark .border-gray-800, .dark .dark\:border-gray-800,
.dark .border-slate-600, .dark .dark\:border-slate-600,
.dark .border-slate-700, .dark .dark\:border-slate-700,
.dark .border-slate-800, .dark .dark\:border-slate-800 {
border-color: rgb(var(--border-subtle)) !important; border-color: rgb(var(--border-subtle)) !important;
} }
.dark .hover\:bg-slate-800:hover {
background-color: rgb(var(--surface-elevated)) !important; /* Border opacity variants */
.dark .dark\:border-gray-700\/60,
.dark .dark\:border-gray-700\/40 {
border-color: rgb(var(--border-subtle) / 0.6) !important;
} }
/* Sidebar panel — overrides the hardcoded hex values in AppShell */ /* Sidebar panel — overrides the hardcoded hex values in AppShell */
@@ -496,7 +605,8 @@
} }
.app-data-table { .app-data-table {
@apply overflow-hidden rounded-2xl border border-gray-200 bg-white shadow-sm; @apply rounded-2xl border border-gray-200 bg-white shadow-sm;
overflow: clip;
} }
.app-data-table table { .app-data-table table {
@@ -548,28 +658,28 @@
:is(.dark) .app-surface { :is(.dark) .app-surface {
background-color: rgb(var(--surface-card)); background-color: rgb(var(--surface-card));
background-image: linear-gradient(135deg, rgba(255,255,255,0.045) 0%, rgba(255,255,255,0.015) 100%); background-image: linear-gradient(135deg, rgb(var(--text-primary) / 0.045) 0%, rgb(var(--text-primary) / 0.015) 100%);
backdrop-filter: blur(16px); backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);
border-color: rgba(255,255,255,0.09); border-color: rgb(var(--border-subtle));
box-shadow: 0 2px 8px rgba(0,0,0,0.4), 0 0 0 1px rgba(255,255,255,0.05), inset 0 1px 0 rgba(255,255,255,0.07); box-shadow: 0 2px 8px rgb(0 0 0 / 0.4), 0 0 0 1px rgb(var(--border-subtle) / 0.5), inset 0 1px 0 rgb(var(--text-primary) / 0.07);
} }
:is(.dark) .app-surface-strong { :is(.dark) .app-surface-strong {
background-color: rgb(var(--surface-card)); background-color: rgb(var(--surface-card));
background-image: linear-gradient(135deg, rgba(255,255,255,0.055) 0%, rgba(255,255,255,0.018) 100%); background-image: linear-gradient(135deg, rgb(var(--text-primary) / 0.055) 0%, rgb(var(--text-primary) / 0.018) 100%);
backdrop-filter: blur(20px); backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
border-color: rgba(255,255,255,0.10); border-color: rgb(var(--border-subtle));
box-shadow: 0 4px 16px rgba(0,0,0,0.5), 0 0 0 1px rgba(255,255,255,0.06), inset 0 1px 0 rgba(255,255,255,0.08); box-shadow: 0 4px 16px rgb(0 0 0 / 0.5), 0 0 0 1px rgb(var(--border-subtle) / 0.6), inset 0 1px 0 rgb(var(--text-primary) / 0.08);
} }
:is(.dark) .app-toolbar { :is(.dark) .app-toolbar {
background: rgb(16 17 19 / 0.82); background: rgb(var(--surface-card) / 0.82);
backdrop-filter: blur(20px); backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
border-color: rgba(255,255,255,0.08); border-color: rgb(var(--border-subtle) / 0.6);
box-shadow: 0 1px 0 rgba(255,255,255,0.04); box-shadow: 0 1px 0 rgb(var(--text-primary) / 0.04);
} }
:is(.dark) .app-input { :is(.dark) .app-input {
@@ -616,7 +726,7 @@
} }
:is(.dark) .app-data-table thead tr { :is(.dark) .app-data-table thead tr {
background: rgba(255,255,255,0.04); background-color: rgb(var(--surface-elevated));
} }
:is(.dark) .app-data-table th { :is(.dark) .app-data-table th {
@@ -716,25 +826,14 @@
} }
:is(.dark) .shimmer-skeleton { :is(.dark) .shimmer-skeleton {
background: linear-gradient(90deg, rgba(255,255,255,0.04) 0%, rgba(255,255,255,0.09) 50%, rgba(255,255,255,0.04) 100%); background: linear-gradient(90deg, rgb(var(--text-primary) / 0.04) 0%, rgb(var(--text-primary) / 0.09) 50%, rgb(var(--text-primary) / 0.04) 100%);
background-size: 200% 100%; background-size: 200% 100%;
animation: shimmer 1.5s ease-in-out infinite; animation: shimmer 1.5s ease-in-out infinite;
} }
/* ─── Table row stagger entrance ─────────────────────────────────────────── */ /* ─── Table row stagger entrance (reuses fadeSlideUp keyframes) ──────────── */
@keyframes fadeSlideIn {
from {
opacity: 0;
transform: translateY(6px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-row-enter { .animate-row-enter {
animation: fadeSlideIn 0.12s ease-out both; animation: fadeSlideUp 0.12s ease-out both;
} }
/* ─── Subtle hover lift for cards and table rows ─────────────────────────── */ /* ─── Subtle hover lift for cards and table rows ─────────────────────────── */