From a2a43b1d5afc2f1c81473aa30d2fe15375f0242b Mon Sep 17 00:00:00 2001 From: Till JS Date: Wed, 22 Apr 2026 15:23:55 +0200 Subject: [PATCH] refactor(theming): migrate 6 ListViews + ai-missions badges to theme tokens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace raw white-alpha Tailwind utilities (text-white/x, bg-white/x, border-white/x) with canonical theme tokens (text-foreground, bg-muted, border-border, etc.) in cards, context, food, moodlit, storage, music ListViews. Replace hardcoded hex badge/dot/phase colors in ai-missions with success/warning/error/primary tokens. Fix two transition-all bugs (food:160, moodlit:223) that prevented CSS custom property colors from resolving on first paint under theme switches. Add scripts/validate-theme-tokens.mjs to prevent regression; run via pnpm run validate:theme-tokens. Not yet in validate:all — 12 modules still use raw white utilities (citycorners, guides, inventory, memoro, picture, plants, playground, presi, questions, times, uload, who). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../lib/modules/ai-missions/ListView.svelte | 48 ++++---- .../web/src/lib/modules/cards/ListView.svelte | 10 +- .../src/lib/modules/context/ListView.svelte | 20 ++-- .../web/src/lib/modules/food/ListView.svelte | 34 +++--- .../src/lib/modules/moodlit/ListView.svelte | 16 ++- .../web/src/lib/modules/music/ListView.svelte | 12 +- .../src/lib/modules/storage/ListView.svelte | 19 ++-- package.json | 1 + scripts/validate-theme-tokens.mjs | 103 ++++++++++++++++++ 9 files changed, 188 insertions(+), 75 deletions(-) create mode 100644 scripts/validate-theme-tokens.mjs diff --git a/apps/mana/apps/web/src/lib/modules/ai-missions/ListView.svelte b/apps/mana/apps/web/src/lib/modules/ai-missions/ListView.svelte index 3087e922a..70d45b995 100644 --- a/apps/mana/apps/web/src/lib/modules/ai-missions/ListView.svelte +++ b/apps/mana/apps/web/src/lib/modules/ai-missions/ListView.svelte @@ -564,16 +564,16 @@ border-radius: 999px; } .dot-active { - background: #22c55e; + background: hsl(var(--color-success)); } .dot-paused { - background: #f59e0b; + background: hsl(var(--color-warning)); } .dot-done { - background: #6b7280; + background: hsl(var(--color-muted-foreground)); } .dot-archived { - background: #374151; + background: hsl(var(--color-muted-foreground) / 0.5); } .m-meta { display: flex; @@ -730,24 +730,24 @@ text-transform: uppercase; } .badge-awaiting-review { - background: #fef0c9; - color: #8a5a00; + background: hsl(var(--color-warning) / 0.18); + color: hsl(var(--color-warning)); } .badge-approved { - background: #d7f7e3; - color: #1b7a3a; + background: hsl(var(--color-success) / 0.18); + color: hsl(var(--color-success)); } .badge-rejected, .badge-failed { - background: #f7d7d7; - color: #8a1b1b; + background: hsl(var(--color-error) / 0.18); + color: hsl(var(--color-error)); } .badge-running { - background: #d7ecff; - color: #0a548b; + background: hsl(var(--color-primary) / 0.18); + color: hsl(var(--color-primary)); } .it-running { - border-color: color-mix(in oklab, #0a548b 35%, hsl(var(--color-border))); + border-color: color-mix(in oklab, hsl(var(--color-primary)) 35%, hsl(var(--color-border))); } .phase-block { display: flex; @@ -755,7 +755,7 @@ gap: 0.375rem; padding: 0.5rem 0.625rem; margin: 0.375rem 0; - background: color-mix(in oklab, #0a548b 6%, transparent); + background: hsl(var(--color-primary) / 0.08); border-radius: 0.375rem; } .phase-line { @@ -806,8 +806,8 @@ font-size: 0.75rem; } .cancel-btn:hover:not(:disabled) { - color: #8a1b1b; - border-color: #e99; + color: hsl(var(--color-error)); + border-color: hsl(var(--color-error) / 0.5); } .cancel-btn:disabled { opacity: 0.5; @@ -815,10 +815,10 @@ } .err-details { margin-top: 0.375rem; - border: 1px solid #f7d7d7; + border: 1px solid hsl(var(--color-error) / 0.3); border-radius: 0.375rem; padding: 0.375rem 0.5rem; - background: color-mix(in oklab, #8a1b1b 4%, transparent); + background: hsl(var(--color-error) / 0.05); font-size: 0.8125rem; } .err-details summary { @@ -830,7 +830,7 @@ .err-name { font-family: var(--font-mono, ui-monospace, monospace); font-weight: 600; - color: #8a1b1b; + color: hsl(var(--color-error)); } .err-phase { color: hsl(var(--color-muted-foreground)); @@ -838,7 +838,7 @@ } .err-message { margin: 0.375rem 0 0; - color: #6a1515; + color: hsl(var(--color-error)); word-break: break-word; } .err-stack { @@ -931,12 +931,12 @@ border-radius: 999px; } .grant-pill-ok { - background: #d7f7e3; - color: #1b7a3a; + background: hsl(var(--color-success) / 0.18); + color: hsl(var(--color-success)); } .grant-pill-warn { - background: #fde7c8; - color: #8a4f00; + background: hsl(var(--color-warning) / 0.18); + color: hsl(var(--color-warning)); } .grant-pill-muted { background: hsl(var(--color-surface)); diff --git a/apps/mana/apps/web/src/lib/modules/cards/ListView.svelte b/apps/mana/apps/web/src/lib/modules/cards/ListView.svelte index a5c5cb3e0..94ecff4bf 100644 --- a/apps/mana/apps/web/src/lib/modules/cards/ListView.svelte +++ b/apps/mana/apps/web/src/lib/modules/cards/ListView.svelte @@ -37,7 +37,7 @@ d.id} emptyTitle="Keine Decks"> {#snippet header()} {decks.length} Decks - {dueForReview} fällig + {dueForReview} fällig {/snippet} {#snippet item(deck)} @@ -48,15 +48,15 @@ _siblingIds: decks.map((d) => d.id), _siblingKey: 'deckId', })} - class="mb-2 w-full rounded-md border border-white/10 px-3 py-2.5 text-left transition-colors hover:bg-white/5 min-h-[44px]" + class="mb-2 w-full rounded-md border border-border px-3 py-2.5 text-left transition-colors hover:bg-muted/50 min-h-[44px]" >
-

{deck.name}

- {cardsInDeck(deck.id)} +

{deck.name}

+ {cardsInDeck(deck.id)}
{#if deck.description} -

{deck.description}

+

{deck.description}

{/if} {/snippet} diff --git a/apps/mana/apps/web/src/lib/modules/context/ListView.svelte b/apps/mana/apps/web/src/lib/modules/context/ListView.svelte index 5803463e7..fb550cec9 100644 --- a/apps/mana/apps/web/src/lib/modules/context/ListView.svelte +++ b/apps/mana/apps/web/src/lib/modules/context/ListView.svelte @@ -60,14 +60,14 @@
Alle Dokumente → @@ -196,25 +194,25 @@ disabled={photoUploading} aria-label="Foto hinzufuegen" title="Foto" - class="rounded-md border border-white/10 bg-white/5 px-3 py-2 text-sm text-white/70 transition-colors hover:bg-white/10 disabled:opacity-30" + class="rounded-md border border-border bg-muted/30 px-3 py-2 text-sm text-foreground/80 transition-colors hover:bg-muted disabled:opacity-30" > {photoUploading ? '...' : '📷'} {#if showPhotoMenu}
@@ -250,19 +248,19 @@ {#snippet item(meal)}
- {mealTypeLabels[meal.mealType] ?? meal.mealType} {#if meal.inputType === 'photo'} - 📷 + 📷 {/if}
-

{meal.description}

+

{meal.description}

{#if meal.photoThumbnailUrl || meal.photoUrl} {/if} {#if meal.nutrition} - {Math.round(meal.nutrition.calories)} kcal {/if} diff --git a/apps/mana/apps/web/src/lib/modules/moodlit/ListView.svelte b/apps/mana/apps/web/src/lib/modules/moodlit/ListView.svelte index fc2ae15c0..97e478a8b 100644 --- a/apps/mana/apps/web/src/lib/modules/moodlit/ListView.svelte +++ b/apps/mana/apps/web/src/lib/modules/moodlit/ListView.svelte @@ -33,7 +33,7 @@ } function gradientStyle(colors: string[]): string { - if (colors.length === 0) return 'background: #333'; + if (colors.length === 0) return 'background: hsl(var(--color-muted))'; if (colors.length === 1) return `background: ${colors[0]}`; return `background: linear-gradient(135deg, ${colors.join(', ')})`; } @@ -178,7 +178,7 @@ {#if newColors.length > 1} {/if} @@ -206,7 +206,7 @@