refactor(components): replace all hardcoded colors with semantic tokens
Replaces bg-white, text-brand-navy, border-brand-sand, text-gray-*, bg-[#2A2A2A], bg-brand-purple/15, hover:bg-brand-sand, etc. across all 35 .svelte files with semantic token utilities (bg-surface, text-ink, border-line, bg-pdf-bg, bg-nav-active, bg-muted, text-accent, bg-primary, ...). Also adds CSS filter: invert(1) in layout.css for De Gruyter <img> icons in dark mode, excluding icons that carry .invert already (to prevent double-inversion). Closes #64 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -67,19 +67,19 @@ const enrichedDocuments = $derived(
|
||||
|
||||
<div class="mx-auto max-w-5xl px-4 py-10">
|
||||
<!-- Page Header -->
|
||||
<div class="mb-8 border-b border-brand-navy/10 pb-4">
|
||||
<h1 class="font-serif text-3xl font-medium text-brand-navy">{m.conv_heading()}</h1>
|
||||
<p class="mt-2 font-sans text-sm text-brand-navy/60">
|
||||
<div class="mb-8 border-b border-ink/10 pb-4">
|
||||
<h1 class="font-serif text-3xl font-medium text-ink">{m.conv_heading()}</h1>
|
||||
<p class="mt-2 font-sans text-sm text-ink/60">
|
||||
{m.conv_subtitle()}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- FILTER BAR -->
|
||||
<div class="border-brand-sand relative z-20 mb-10 border bg-white p-8 shadow-sm">
|
||||
<div class="relative z-20 mb-10 border border-line bg-surface p-8 shadow-sm">
|
||||
<div class="mb-6 grid grid-cols-1 items-end gap-4 md:grid-cols-[1fr_auto_1fr] md:gap-6">
|
||||
<!-- Sender -->
|
||||
<div
|
||||
class="relative z-30 [&_input]:border-gray-300 [&_input]:py-2.5 [&_input]:focus:border-brand-navy [&_input]:focus:ring-brand-navy [&_label]:mb-2 [&_label]:text-xs [&_label]:font-bold [&_label]:tracking-widest [&_label]:text-gray-500 [&_label]:uppercase"
|
||||
class="relative z-30 [&_input]:border-gray-300 [&_input]:py-2.5 [&_input]:focus:border-ink [&_input]:focus:ring-ink [&_label]:mb-2 [&_label]:text-xs [&_label]:font-bold [&_label]:tracking-widest [&_label]:text-ink-2 [&_label]:uppercase"
|
||||
>
|
||||
<PersonTypeahead
|
||||
name="senderId"
|
||||
@@ -98,7 +98,7 @@ const enrichedDocuments = $derived(
|
||||
<button
|
||||
data-testid="conv-swap-btn"
|
||||
onclick={swapPersons}
|
||||
class="border-brand-sand flex w-full items-center justify-center gap-2 border px-3 py-2.5 text-xs font-bold tracking-widest text-brand-navy uppercase transition-colors hover:bg-brand-navy hover:text-white md:w-auto {senderId &&
|
||||
class="flex w-full items-center justify-center gap-2 border border-line px-3 py-2.5 text-xs font-bold tracking-widest text-ink uppercase transition-colors hover:bg-primary hover:text-white md:w-auto {senderId &&
|
||||
receiverId
|
||||
? ''
|
||||
: 'invisible'}"
|
||||
@@ -123,7 +123,7 @@ const enrichedDocuments = $derived(
|
||||
|
||||
<!-- Receiver -->
|
||||
<div
|
||||
class="relative z-30 [&_input]:border-gray-300 [&_input]:py-2.5 [&_input]:focus:border-brand-navy [&_input]:focus:ring-brand-navy [&_label]:mb-2 [&_label]:text-xs [&_label]:font-bold [&_label]:tracking-widest [&_label]:text-gray-500 [&_label]:uppercase"
|
||||
class="relative z-30 [&_input]:border-gray-300 [&_input]:py-2.5 [&_input]:focus:border-ink [&_input]:focus:ring-ink [&_label]:mb-2 [&_label]:text-xs [&_label]:font-bold [&_label]:tracking-widest [&_label]:text-ink-2 [&_label]:uppercase"
|
||||
>
|
||||
<PersonTypeahead
|
||||
name="receiverId"
|
||||
@@ -141,7 +141,7 @@ const enrichedDocuments = $derived(
|
||||
<div>
|
||||
<label
|
||||
for="dateFrom"
|
||||
class="mb-2 block text-xs font-bold tracking-widest text-gray-500 uppercase"
|
||||
class="mb-2 block text-xs font-bold tracking-widest text-ink-2 uppercase"
|
||||
>{m.conv_label_from()}</label
|
||||
>
|
||||
<input
|
||||
@@ -149,7 +149,7 @@ const enrichedDocuments = $derived(
|
||||
type="date"
|
||||
bind:value={fromDate}
|
||||
onchange={() => applyFilters()}
|
||||
class="block w-full border-gray-300 py-2.5 text-sm shadow-sm focus:border-brand-navy focus:ring-brand-navy"
|
||||
class="block w-full border-gray-300 py-2.5 text-sm shadow-sm focus:border-ink focus:ring-ink"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -157,7 +157,7 @@ const enrichedDocuments = $derived(
|
||||
<div>
|
||||
<label
|
||||
for="dateTo"
|
||||
class="mb-2 block text-xs font-bold tracking-widest text-gray-500 uppercase"
|
||||
class="mb-2 block text-xs font-bold tracking-widest text-ink-2 uppercase"
|
||||
>{m.conv_label_to()}</label
|
||||
>
|
||||
<input
|
||||
@@ -165,7 +165,7 @@ const enrichedDocuments = $derived(
|
||||
type="date"
|
||||
bind:value={toDate}
|
||||
onchange={() => applyFilters()}
|
||||
class="block w-full border-gray-300 py-2.5 text-sm shadow-sm focus:border-brand-navy focus:ring-brand-navy"
|
||||
class="block w-full border-gray-300 py-2.5 text-sm shadow-sm focus:border-ink focus:ring-ink"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -173,7 +173,7 @@ const enrichedDocuments = $derived(
|
||||
<div>
|
||||
<button
|
||||
onclick={toggleSort}
|
||||
class="border-brand-sand flex h-[42px] w-full items-center justify-center border text-xs font-bold tracking-wide text-brand-navy uppercase transition-colors hover:bg-brand-navy hover:text-white"
|
||||
class="flex h-[42px] w-full items-center justify-center border border-line text-xs font-bold tracking-wide text-ink uppercase transition-colors hover:bg-primary hover:text-white"
|
||||
>
|
||||
<span class="mr-2">{m.conv_sort_label()}</span>
|
||||
<span>{sortDir === 'DESC' ? m.conv_sort_newest() : m.conv_sort_oldest()}</span>
|
||||
@@ -196,9 +196,9 @@ const enrichedDocuments = $derived(
|
||||
<!-- RESULTS LIST SECTION -->
|
||||
{#if !senderId || !receiverId}
|
||||
<div
|
||||
class="border-brand-sand flex flex-col items-center justify-center rounded-sm border border-dashed bg-white py-24 text-center"
|
||||
class="flex flex-col items-center justify-center rounded-sm border border-dashed border-line bg-surface py-24 text-center"
|
||||
>
|
||||
<div class="bg-brand-sand/30 mb-4 rounded-full p-4 text-brand-navy">
|
||||
<div class="mb-4 rounded-full bg-muted p-4 text-ink">
|
||||
<svg class="h-8 w-8" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
||||
><path
|
||||
stroke-linecap="round"
|
||||
@@ -208,25 +208,25 @@ const enrichedDocuments = $derived(
|
||||
/></svg
|
||||
>
|
||||
</div>
|
||||
<p class="font-serif text-lg text-brand-navy">{m.conv_empty_heading()}</p>
|
||||
<p class="mt-1 font-sans text-sm text-gray-500">{m.conv_empty_text()}</p>
|
||||
<p class="font-serif text-lg text-ink">{m.conv_empty_heading()}</p>
|
||||
<p class="mt-1 font-sans text-sm text-ink-2">{m.conv_empty_text()}</p>
|
||||
</div>
|
||||
{:else if data.documents.length === 0}
|
||||
<div
|
||||
class="border-brand-sand flex flex-col items-center justify-center rounded-sm border bg-white py-24 text-center shadow-sm"
|
||||
class="flex flex-col items-center justify-center rounded-sm border border-line bg-surface py-24 text-center shadow-sm"
|
||||
>
|
||||
<p class="font-serif text-brand-navy">{m.conv_no_results_heading()}</p>
|
||||
<p class="mt-2 text-sm text-gray-400">{m.conv_no_results_text()}</p>
|
||||
<p class="font-serif text-ink">{m.conv_no_results_heading()}</p>
|
||||
<p class="mt-2 text-sm text-ink-3">{m.conv_no_results_text()}</p>
|
||||
</div>
|
||||
{:else}
|
||||
<!-- Summary bar -->
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
{#if yearFrom !== null && yearTo !== null}
|
||||
<p data-testid="conv-summary" class="font-sans text-sm font-medium text-brand-navy/70">
|
||||
<p data-testid="conv-summary" class="font-sans text-sm font-medium text-ink/70">
|
||||
{m.conv_summary({ count: data.documents.length, yearFrom, yearTo })}
|
||||
</p>
|
||||
{:else}
|
||||
<p data-testid="conv-summary" class="font-sans text-sm font-medium text-brand-navy/70">
|
||||
<p data-testid="conv-summary" class="font-sans text-sm font-medium text-ink/70">
|
||||
{data.documents.length}
|
||||
</p>
|
||||
{/if}
|
||||
@@ -234,7 +234,7 @@ const enrichedDocuments = $derived(
|
||||
<a
|
||||
data-testid="conv-new-doc-link"
|
||||
href="/documents/new?senderId={senderId}&receiverId={receiverId}"
|
||||
class="inline-flex items-center gap-1 text-sm font-medium text-brand-navy/60 transition-colors hover:text-brand-navy"
|
||||
class="inline-flex items-center gap-1 text-sm font-medium text-ink/60 transition-colors hover:text-ink"
|
||||
>
|
||||
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"
|
||||
@@ -246,10 +246,10 @@ const enrichedDocuments = $derived(
|
||||
</div>
|
||||
|
||||
<!-- CHAT CONTAINER -->
|
||||
<div class="border-brand-sand relative overflow-hidden rounded-sm border bg-white shadow-sm">
|
||||
<div class="relative overflow-hidden rounded-sm border border-line bg-surface shadow-sm">
|
||||
<!-- Decoration: Central Timeline Line -->
|
||||
<div
|
||||
class="bg-brand-sand/30 absolute top-0 bottom-0 left-1/2 hidden w-px -translate-x-1/2 transform md:block"
|
||||
class="absolute top-0 bottom-0 left-1/2 hidden w-px -translate-x-1/2 transform bg-muted md:block"
|
||||
></div>
|
||||
|
||||
<div class="p-6 md:p-8">
|
||||
@@ -257,12 +257,11 @@ const enrichedDocuments = $derived(
|
||||
{#each enrichedDocuments as { doc, year, showYearDivider } (doc.id)}
|
||||
{#if showYearDivider}
|
||||
<div data-testid="year-divider" class="relative flex items-center py-2 text-center">
|
||||
<div class="border-brand-sand flex-grow border-t"></div>
|
||||
<span
|
||||
class="mx-4 font-sans text-xs font-bold tracking-widest text-brand-navy/40 uppercase"
|
||||
<div class="flex-grow border-t border-line"></div>
|
||||
<span class="mx-4 font-sans text-xs font-bold tracking-widest text-ink/40 uppercase"
|
||||
>{year}</span
|
||||
>
|
||||
<div class="border-brand-sand flex-grow border-t"></div>
|
||||
<div class="flex-grow border-t border-line"></div>
|
||||
</div>
|
||||
{/if}
|
||||
{@const isRight = doc.sender?.id === senderId}
|
||||
@@ -280,8 +279,8 @@ const enrichedDocuments = $derived(
|
||||
<div
|
||||
class="flex h-8 w-8 items-center justify-center rounded-full border font-serif text-xs shadow-sm
|
||||
{isRight
|
||||
? 'border-brand-navy bg-brand-navy text-white'
|
||||
: 'border-brand-sand bg-white text-brand-navy'}"
|
||||
? 'border-primary bg-primary text-white'
|
||||
: 'border-line bg-surface text-ink'}"
|
||||
>
|
||||
{#if doc.sender}
|
||||
{doc.sender.firstName[0]}{doc.sender.lastName[0]}
|
||||
@@ -296,15 +295,15 @@ const enrichedDocuments = $derived(
|
||||
href="/documents/{doc.id}"
|
||||
class="group block transform rounded border p-4 shadow-sm transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md
|
||||
{isRight
|
||||
? 'rounded-br-none border-brand-navy bg-brand-navy text-white'
|
||||
: 'border-brand-sand bg-brand-sand/10 rounded-bl-none text-brand-navy'}"
|
||||
? 'rounded-br-none border-primary bg-primary text-white'
|
||||
: 'rounded-bl-none border-line bg-muted/50 text-ink'}"
|
||||
>
|
||||
<!-- Header -->
|
||||
<div class="mb-2 flex items-start justify-between gap-4">
|
||||
<h3
|
||||
class="font-serif text-sm leading-snug font-medium {isRight
|
||||
? 'text-white'
|
||||
: 'text-brand-navy'}"
|
||||
: 'text-ink'}"
|
||||
>
|
||||
{doc.title || doc.originalFilename}
|
||||
</h3>
|
||||
@@ -313,7 +312,7 @@ const enrichedDocuments = $derived(
|
||||
<span
|
||||
class="mt-1.5 h-1.5 w-1.5 flex-shrink-0 rounded-full
|
||||
{doc.status === 'UPLOADED'
|
||||
? 'bg-brand-mint'
|
||||
? 'bg-accent'
|
||||
: 'bg-yellow-400'}"
|
||||
title={doc.status}
|
||||
>
|
||||
@@ -324,7 +323,7 @@ const enrichedDocuments = $derived(
|
||||
<div
|
||||
class="flex flex-wrap gap-3 font-sans text-[10px] tracking-wider uppercase opacity-80 {isRight
|
||||
? 'text-blue-100'
|
||||
: 'text-gray-500'}"
|
||||
: 'text-ink-2'}"
|
||||
>
|
||||
<span class="flex items-center">
|
||||
{doc.documentDate ? formatDate(doc.documentDate) : '—'}
|
||||
|
||||
Reference in New Issue
Block a user