fix(ui): move all :is(.dark) component class rules outside @layer

Rules inside @layer components lose to unlayered styles in the CSS cascade,
causing dark mode overrides to be silently ignored. Move ALL :is(.dark) rules
for app-surface, app-surface-strong, app-toolbar, app-input, app-select,
app-label, app-page-title, app-page-subtitle, app-data-table, and action
classes outside @layer — the same fix that resolved app-data-table white bg.

Also switch app-surface/strong from background: shorthand to separate
background-color + background-image to ensure the dark surface-card base
color is always applied independently of the gradient overlay.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-10 07:19:16 +02:00
parent 9b5cd8549d
commit db892ae285
+95 -88
View File
@@ -435,38 +435,22 @@
} }
@layer components { @layer components {
/* Light mode only — dark overrides are ALL outside @layer to ensure they win */
.app-surface { .app-surface {
@apply rounded-2xl border border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-900/90; @apply rounded-2xl border border-gray-200 bg-white;
background: linear-gradient(145deg, #ffffff 0%, #fdfdfd 100%); background-image: linear-gradient(145deg, #ffffff 0%, #fdfdfd 100%);
--tw-shadow: 0 1px 3px rgb(var(--accent-400) / 0.06), 0 4px 16px rgb(var(--accent-400) / 0.04); --tw-shadow: 0 1px 3px rgb(var(--accent-400) / 0.06), 0 4px 16px rgb(var(--accent-400) / 0.04);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
} }
:is(.dark) .app-surface {
background: linear-gradient(135deg, rgba(255,255,255,0.055) 0%, rgba(255,255,255,0.018) 100%);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border-color: rgba(255,255,255,0.09);
--tw-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: var(--tw-shadow);
}
.app-surface-strong { .app-surface-strong {
@apply rounded-3xl border border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-900; @apply rounded-3xl border border-gray-200 bg-white;
background: linear-gradient(145deg, #ffffff 0%, #fdfdfd 100%); background-image: linear-gradient(145deg, #ffffff 0%, #fdfdfd 100%);
--tw-shadow: 0 2px 8px rgb(var(--accent-400) / 0.06), 0 8px 24px rgb(var(--accent-400) / 0.04); --tw-shadow: 0 2px 8px rgb(var(--accent-400) / 0.06), 0 8px 24px rgb(var(--accent-400) / 0.04);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
} }
:is(.dark) .app-surface-strong {
background: linear-gradient(135deg, rgba(255,255,255,0.065) 0%, rgba(255,255,255,0.022) 100%);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-color: rgba(255,255,255,0.10);
--tw-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: var(--tw-shadow);
}
.app-toolbar { .app-toolbar {
@apply sticky top-0 z-10 rounded-2xl border p-4; @apply sticky top-0 z-10 rounded-2xl border p-4;
background: rgb(255 255 255 / 0.85); background: rgb(255 255 255 / 0.85);
@@ -476,14 +460,6 @@
box-shadow: 0 1px 3px rgb(var(--accent-400) / 0.06); box-shadow: 0 1px 3px rgb(var(--accent-400) / 0.06);
} }
:is(.dark) .app-toolbar {
background: rgb(16 17 19 / 0.82);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-color: rgba(255,255,255,0.08);
box-shadow: 0 1px 0 rgba(255,255,255,0.04);
}
.app-input { .app-input {
@apply w-full rounded-xl border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 outline-none transition; @apply w-full rounded-xl border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 outline-none transition;
} }
@@ -494,41 +470,15 @@
outline: none; outline: none;
} }
:is(.dark) .app-input {
background-color: rgb(var(--surface-input));
border-color: rgb(var(--border-input));
color: rgb(var(--text-primary));
}
:is(.dark) .app-input:focus {
border-color: rgb(var(--accent-400));
box-shadow: 0 0 0 3px rgba(var(--accent-400), 0.20), inset 0 0 8px rgba(var(--accent-400), 0.08);
}
.app-select { .app-select {
@apply rounded-xl border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 outline-none transition; @apply rounded-xl border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 outline-none transition;
@apply focus:border-brand-500 focus:ring-4 focus:ring-brand-100/80; @apply focus:border-brand-500 focus:ring-4 focus:ring-brand-100/80;
} }
:is(.dark) .app-select {
background-color: rgb(var(--surface-input));
border-color: rgb(var(--border-input));
color: rgb(var(--text-primary));
}
:is(.dark) .app-select:focus {
border-color: rgb(var(--accent-400));
box-shadow: 0 0 0 3px rgb(var(--accent-400) / 0.20);
}
.app-label { .app-label {
@apply mb-1.5 block text-[11px] font-semibold uppercase tracking-[0.18em] text-gray-500; @apply mb-1.5 block text-[11px] font-semibold uppercase tracking-[0.18em] text-gray-500;
} }
:is(.dark) .app-label {
color: rgb(var(--text-very-muted));
}
.app-page { .app-page {
@apply p-6 md:p-8; @apply p-6 md:p-8;
} }
@@ -541,32 +491,14 @@
@apply font-display text-3xl font-semibold text-gray-900; @apply font-display text-3xl font-semibold text-gray-900;
} }
:is(.dark) .app-page-title {
background: linear-gradient(135deg, rgb(var(--accent-200)) 0%, rgb(var(--accent-100)) 50%, rgb(237 242 247) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.app-page-subtitle { .app-page-subtitle {
@apply text-sm text-gray-500; @apply text-sm text-gray-500;
} }
:is(.dark) .app-page-subtitle {
color: rgb(var(--text-muted));
}
.app-data-table { .app-data-table {
@apply overflow-hidden rounded-2xl border border-gray-200 bg-white shadow-sm; @apply overflow-hidden rounded-2xl border border-gray-200 bg-white shadow-sm;
} }
/* Explicit dark rule — @apply dark: inside @layer components is unreliable */
:is(.dark) .app-data-table {
background-color: rgb(var(--surface-card));
border-color: rgb(var(--border-subtle));
box-shadow: none;
}
.app-data-table table { .app-data-table table {
@apply min-w-full text-sm; @apply min-w-full text-sm;
} }
@@ -575,19 +507,11 @@
@apply bg-gray-50/90; @apply bg-gray-50/90;
} }
:is(.dark) .app-data-table thead tr {
background: rgba(255,255,255,0.04);
}
.app-data-table th { .app-data-table th {
@apply text-[11px] font-semibold uppercase tracking-[0.16em] text-gray-500; @apply text-[11px] font-semibold uppercase tracking-[0.16em] text-gray-500;
} }
:is(.dark) .app-data-table th { /* ─── Semantic action classes (light mode only) ──────────────────────── */
color: rgb(var(--text-very-muted));
}
/* ─── Semantic action classes ─────────────────────────────────────────── */
.app-action-edit { .app-action-edit {
@apply text-xs font-medium cursor-pointer transition-colors; @apply text-xs font-medium cursor-pointer transition-colors;
@@ -597,8 +521,6 @@
color: rgb(37 99 235); color: rgb(37 99 235);
text-decoration: underline; text-decoration: underline;
} }
:is(.dark) .app-action-edit { color: rgb(96 165 250); }
:is(.dark) .app-action-edit:hover { color: rgb(147 197 253); }
.app-action-delete { .app-action-delete {
@apply text-xs font-medium cursor-pointer transition-colors; @apply text-xs font-medium cursor-pointer transition-colors;
@@ -608,8 +530,6 @@
color: rgb(185 28 28); color: rgb(185 28 28);
text-decoration: underline; text-decoration: underline;
} }
:is(.dark) .app-action-delete { color: rgb(248 113 113); }
:is(.dark) .app-action-delete:hover { color: rgb(252 165 165); }
.app-action-danger-btn { .app-action-danger-btn {
@apply rounded px-2 py-1 text-sm font-medium cursor-pointer transition-colors; @apply rounded px-2 py-1 text-sm font-medium cursor-pointer transition-colors;
@@ -619,12 +539,99 @@
color: rgb(185 28 28); color: rgb(185 28 28);
background-color: rgb(254 242 242); background-color: rgb(254 242 242);
} }
}
/* ─── Dark overrides for component classes — ALL outside @layer ──────────────
Rules inside @layer components are lower priority than unlayered styles.
Placing :is(.dark) rules here (unlayered) ensures they always win.
─────────────────────────────────────────────────────────────────────────── */
:is(.dark) .app-surface {
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%);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border-color: rgba(255,255,255,0.09);
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);
}
:is(.dark) .app-surface-strong {
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%);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-color: rgba(255,255,255,0.10);
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);
}
:is(.dark) .app-toolbar {
background: rgb(16 17 19 / 0.82);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-color: rgba(255,255,255,0.08);
box-shadow: 0 1px 0 rgba(255,255,255,0.04);
}
:is(.dark) .app-input {
background-color: rgb(var(--surface-input));
border-color: rgb(var(--border-input));
color: rgb(var(--text-primary));
}
:is(.dark) .app-input:focus {
border-color: rgb(var(--accent-400));
box-shadow: 0 0 0 3px rgba(var(--accent-400), 0.20), inset 0 0 8px rgba(var(--accent-400), 0.08);
}
:is(.dark) .app-select {
background-color: rgb(var(--surface-input));
border-color: rgb(var(--border-input));
color: rgb(var(--text-primary));
}
:is(.dark) .app-select:focus {
border-color: rgb(var(--accent-400));
box-shadow: 0 0 0 3px rgb(var(--accent-400) / 0.20);
}
:is(.dark) .app-label {
color: rgb(var(--text-very-muted));
}
:is(.dark) .app-page-title {
background-image: linear-gradient(135deg, rgb(var(--accent-200)) 0%, rgb(var(--accent-100)) 50%, rgb(237 242 247) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
:is(.dark) .app-page-subtitle {
color: rgb(var(--text-muted));
}
:is(.dark) .app-data-table {
background-color: rgb(var(--surface-card));
border-color: rgb(var(--border-subtle));
box-shadow: none;
}
:is(.dark) .app-data-table thead tr {
background: rgba(255,255,255,0.04);
}
:is(.dark) .app-data-table th {
color: rgb(var(--text-very-muted));
}
:is(.dark) .app-action-edit { color: rgb(96 165 250); }
:is(.dark) .app-action-edit:hover { color: rgb(147 197 253); }
:is(.dark) .app-action-delete { color: rgb(248 113 113); }
:is(.dark) .app-action-delete:hover { color: rgb(252 165 165); }
:is(.dark) .app-action-danger-btn { color: rgb(248 113 113); } :is(.dark) .app-action-danger-btn { color: rgb(248 113 113); }
:is(.dark) .app-action-danger-btn:hover { :is(.dark) .app-action-danger-btn:hover {
color: rgb(252 165 165); color: rgb(252 165 165);
background-color: rgb(127 29 29 / 0.2); background-color: rgb(127 29 29 / 0.2);
} }
}
/* ─── Timeline utilities (unchanged) ────────────────────────────────────── */ /* ─── Timeline utilities (unchanged) ────────────────────────────────────── */