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:
Marcel
2026-03-25 12:22:50 +01:00
parent caba89dacc
commit e4539ed0f0
32 changed files with 569 additions and 613 deletions

View File

@@ -25,16 +25,16 @@ let {
<!-- Desktop / tablet panel (≥ sm): absolute overlay on the right side -->
<div
class="border-brand-sand absolute top-0 right-0 z-50 hidden h-full w-80 flex-col border-l bg-white shadow-2xl sm:flex"
class="absolute top-0 right-0 z-50 hidden h-full w-80 flex-col border-l border-line bg-surface shadow-2xl sm:flex"
>
<div class="border-brand-sand flex shrink-0 items-center justify-between border-b px-4 py-3">
<h3 class="font-sans text-xs font-bold tracking-widest text-brand-navy uppercase">
<div class="flex shrink-0 items-center justify-between border-b border-line px-4 py-3">
<h3 class="font-sans text-xs font-bold tracking-widest text-ink uppercase">
{m.comment_panel_title()}
</h3>
<button
onclick={onClose}
aria-label={m.comment_panel_close()}
class="hover:bg-brand-sand/50 rounded p-1 text-gray-400 transition-colors hover:text-brand-navy"
class="rounded p-1 text-ink-3 transition-colors hover:bg-muted hover:text-ink"
>
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
@@ -60,15 +60,15 @@ let {
<div class="flex-1 bg-black/40" onclick={onClose} role="presentation"></div>
<!-- Slide-up panel -->
<div class="flex max-h-[80vh] flex-col rounded-t-2xl bg-white shadow-2xl">
<div class="border-brand-sand flex shrink-0 items-center justify-between border-b px-4 py-3">
<h3 class="font-sans text-xs font-bold tracking-widest text-brand-navy uppercase">
<div class="flex max-h-[80vh] flex-col rounded-t-2xl bg-surface shadow-2xl">
<div class="flex shrink-0 items-center justify-between border-b border-line px-4 py-3">
<h3 class="font-sans text-xs font-bold tracking-widest text-ink uppercase">
{m.comment_panel_title()}
</h3>
<button
onclick={onClose}
aria-label={m.comment_panel_close()}
class="hover:bg-brand-sand/50 rounded p-1 text-gray-400 transition-colors hover:text-brand-navy"
class="rounded p-1 text-ink-3 transition-colors hover:bg-muted hover:text-ink"
>
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />

View File

@@ -26,20 +26,20 @@ const visible = $derived(activeAnnotationId !== null);
</script>
<div
class="border-brand-sand absolute inset-y-0 right-0 z-10 flex w-80 flex-col border-l bg-white shadow-[-4px_0_16px_rgba(0,0,0,0.08)] transition-transform duration-200 {visible
class="absolute inset-y-0 right-0 z-10 flex w-80 flex-col border-l border-line bg-surface shadow-[-4px_0_16px_rgba(0,0,0,0.08)] transition-transform duration-200 {visible
? 'translate-x-0'
: 'pointer-events-none translate-x-full'}"
data-testid="annotation-side-panel"
>
<!-- Header -->
<div class="border-brand-sand flex shrink-0 items-center justify-between border-b px-4 py-3">
<span class="font-sans text-xs font-medium text-brand-navy">
<div class="flex shrink-0 items-center justify-between border-b border-line px-4 py-3">
<span class="font-sans text-xs font-medium text-ink">
{m.doc_panel_discussion_annotation_tab({ page: String(activeAnnotationPage ?? '?') })}
</span>
<button
onclick={onClose}
aria-label={m.comment_panel_close()}
class="hover:bg-brand-sand/50 rounded p-1 text-gray-400 transition-colors hover:text-brand-navy"
class="rounded p-1 text-ink-3 transition-colors hover:bg-muted hover:text-ink"
>
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />

View File

@@ -191,26 +191,26 @@ onMount(() => {
<div class="space-y-4">
{#each comments as thread, ti (thread.id)}
<div class={ti > 0 ? 'border-brand-sand border-t pt-4' : ''}>
<div class={ti > 0 ? 'border-t border-line pt-4' : ''}>
<!-- Root comment -->
<div>
{#if editingId === thread.id}
<div class="flex flex-col gap-2">
<textarea
class="border-brand-sand w-full resize-none rounded border px-3 py-2 font-serif text-sm text-brand-navy focus:ring-1 focus:ring-brand-mint focus:outline-none"
class="w-full resize-none rounded border border-line px-3 py-2 font-serif text-sm text-ink focus:ring-1 focus:ring-accent focus:outline-none"
rows={3}
bind:value={editText}
></textarea>
<div class="flex items-center gap-3">
<button
class="rounded bg-brand-navy px-3 py-1.5 font-sans text-xs font-medium text-white hover:bg-brand-navy/80 disabled:opacity-40"
class="rounded bg-primary px-3 py-1.5 font-sans text-xs font-medium text-white hover:bg-primary/80 disabled:opacity-40"
disabled={posting}
onclick={() => saveEdit(thread.id)}
>
{m.btn_save()}
</button>
<button
class="font-sans text-xs text-gray-400 transition-colors hover:text-brand-navy"
class="font-sans text-xs text-ink-3 transition-colors hover:text-ink"
onclick={cancelEdit}
>
{m.btn_cancel()}
@@ -221,12 +221,10 @@ onMount(() => {
<div class="flex items-start justify-between gap-2">
<div class="min-w-0 flex-1">
<div class="flex flex-wrap items-center gap-2">
<span class="font-sans text-xs font-semibold text-brand-navy"
>{thread.authorName}</span
>
<span class="font-sans text-xs text-gray-400">{timeAgo(thread.createdAt)}</span>
<span class="font-sans text-xs font-semibold text-ink">{thread.authorName}</span>
<span class="font-sans text-xs text-ink-3">{timeAgo(thread.createdAt)}</span>
{#if wasEdited(thread)}
<span class="font-sans text-xs text-gray-400">
<span class="font-sans text-xs text-ink-3">
{m.comment_edited_label()}
{timeAgo(thread.updatedAt)}
</span>
@@ -237,13 +235,13 @@ onMount(() => {
{#if canModify(thread)}
<div class="flex shrink-0 items-center gap-2">
<button
class="font-sans text-xs text-gray-400 transition-colors hover:text-brand-navy"
class="font-sans text-xs text-ink-3 transition-colors hover:text-ink"
onclick={() => startEdit(thread)}
>
{m.btn_edit()}
</button>
<button
class="font-sans text-xs text-gray-400 transition-colors hover:text-brand-navy"
class="font-sans text-xs text-ink-3 transition-colors hover:text-ink"
onclick={() => deleteComment(thread.id)}
>
{m.btn_delete()}
@@ -255,7 +253,7 @@ onMount(() => {
{#if thread.replies.length === 0 && canComment}
<div class="mt-1">
<button
class="font-sans text-xs font-medium text-brand-mint transition-colors hover:text-brand-navy"
class="font-sans text-xs font-medium text-accent transition-colors hover:text-ink"
onclick={() => startReply(thread.id)}
>
{m.comment_btn_reply()}
@@ -267,24 +265,24 @@ onMount(() => {
<!-- Replies -->
{#each thread.replies as reply, ri (reply.id)}
<div class="border-brand-sand mt-3 ml-6 border-l-2 pl-4">
<div class="mt-3 ml-6 border-l-2 border-line pl-4">
{#if editingId === reply.id}
<div class="flex flex-col gap-2">
<textarea
class="border-brand-sand w-full resize-none rounded border px-3 py-2 font-serif text-sm text-brand-navy focus:ring-1 focus:ring-brand-mint focus:outline-none"
class="w-full resize-none rounded border border-line px-3 py-2 font-serif text-sm text-ink focus:ring-1 focus:ring-accent focus:outline-none"
rows={3}
bind:value={editText}
></textarea>
<div class="flex items-center gap-3">
<button
class="rounded bg-brand-navy px-3 py-1.5 font-sans text-xs font-medium text-white hover:bg-brand-navy/80 disabled:opacity-40"
class="rounded bg-primary px-3 py-1.5 font-sans text-xs font-medium text-white hover:bg-primary/80 disabled:opacity-40"
disabled={posting}
onclick={() => saveEdit(reply.id)}
>
{m.btn_save()}
</button>
<button
class="font-sans text-xs text-gray-400 transition-colors hover:text-brand-navy"
class="font-sans text-xs text-ink-3 transition-colors hover:text-ink"
onclick={cancelEdit}
>
{m.btn_cancel()}
@@ -295,12 +293,10 @@ onMount(() => {
<div class="flex items-start justify-between gap-2">
<div class="min-w-0 flex-1">
<div class="flex flex-wrap items-center gap-2">
<span class="font-sans text-xs font-semibold text-brand-navy"
>{reply.authorName}</span
>
<span class="font-sans text-xs text-gray-400">{timeAgo(reply.createdAt)}</span>
<span class="font-sans text-xs font-semibold text-ink">{reply.authorName}</span>
<span class="font-sans text-xs text-ink-3">{timeAgo(reply.createdAt)}</span>
{#if wasEdited(reply)}
<span class="font-sans text-xs text-gray-400">
<span class="font-sans text-xs text-ink-3">
{m.comment_edited_label()}
{timeAgo(reply.updatedAt)}
</span>
@@ -311,13 +307,13 @@ onMount(() => {
{#if canModify(reply)}
<div class="flex shrink-0 items-center gap-2">
<button
class="font-sans text-xs text-gray-400 transition-colors hover:text-brand-navy"
class="font-sans text-xs text-ink-3 transition-colors hover:text-ink"
onclick={() => startEdit(reply)}
>
{m.btn_edit()}
</button>
<button
class="font-sans text-xs text-gray-400 transition-colors hover:text-brand-navy"
class="font-sans text-xs text-ink-3 transition-colors hover:text-ink"
onclick={() => deleteComment(reply.id)}
>
{m.btn_delete()}
@@ -329,7 +325,7 @@ onMount(() => {
{#if ri === thread.replies.length - 1 && canComment}
<div class="mt-1">
<button
class="font-sans text-xs font-medium text-brand-mint transition-colors hover:text-brand-navy"
class="font-sans text-xs font-medium text-accent transition-colors hover:text-ink"
onclick={() => startReply(thread.id)}
>
{m.comment_btn_reply()}
@@ -344,21 +340,21 @@ onMount(() => {
{#if replyingTo === thread.id}
<div class="mt-3 ml-6 flex flex-col gap-2">
<textarea
class="border-brand-sand w-full resize-none rounded border px-3 py-2 font-serif text-sm text-brand-navy focus:ring-1 focus:ring-brand-mint focus:outline-none"
class="w-full resize-none rounded border border-line px-3 py-2 font-serif text-sm text-ink focus:ring-1 focus:ring-accent focus:outline-none"
rows={3}
placeholder={m.comment_placeholder()}
bind:value={replyText}
></textarea>
<div class="flex items-center gap-3">
<button
class="rounded bg-brand-navy px-3 py-1.5 font-sans text-xs font-medium text-white hover:bg-brand-navy/80 disabled:opacity-40"
class="rounded bg-primary px-3 py-1.5 font-sans text-xs font-medium text-white hover:bg-primary/80 disabled:opacity-40"
disabled={posting}
onclick={() => postReply(thread.id)}
>
{m.comment_btn_post()}
</button>
<button
class="font-sans text-xs text-gray-400 transition-colors hover:text-brand-navy"
class="font-sans text-xs text-ink-3 transition-colors hover:text-ink"
onclick={cancelReply}
>
{m.btn_cancel()}
@@ -371,17 +367,17 @@ onMount(() => {
<!-- New top-level comment textarea -->
{#if canComment}
<div class={comments.length > 0 ? 'border-brand-sand border-t pt-4' : ''}>
<div class={comments.length > 0 ? 'border-t border-line pt-4' : ''}>
<div class="flex flex-col gap-2">
<textarea
class="border-brand-sand w-full resize-none rounded border px-3 py-2 font-serif text-sm text-brand-navy focus:ring-1 focus:ring-brand-mint focus:outline-none"
class="w-full resize-none rounded border border-line px-3 py-2 font-serif text-sm text-ink focus:ring-1 focus:ring-accent focus:outline-none"
rows={3}
placeholder={m.comment_placeholder()}
bind:value={newText}
></textarea>
<div>
<button
class="rounded bg-brand-navy px-3 py-1.5 font-sans text-xs font-medium text-white hover:bg-brand-navy/80 disabled:opacity-40"
class="rounded bg-primary px-3 py-1.5 font-sans text-xs font-medium text-white hover:bg-primary/80 disabled:opacity-40"
disabled={posting || !newText.trim()}
onclick={postComment}
>

View File

@@ -121,13 +121,13 @@ const panelHeight = $derived(open ? height : MIN_HEIGHT);
</script>
<div
class="border-brand-sand fixed right-0 bottom-0 left-0 z-30 flex flex-col border-t bg-white shadow-[0_-4px_16px_rgba(0,0,0,0.08)]"
class="fixed right-0 bottom-0 left-0 z-30 flex flex-col border-t border-line bg-surface shadow-[0_-4px_16px_rgba(0,0,0,0.08)]"
style="height: {panelHeight}px"
data-testid="bottom-panel"
>
<!-- Drag handle -->
<div
class="flex h-2 shrink-0 cursor-ns-resize items-center justify-center bg-white"
class="flex h-2 shrink-0 cursor-ns-resize items-center justify-center bg-surface"
style="touch-action: none"
role="separator"
aria-orientation="horizontal"
@@ -141,13 +141,13 @@ const panelHeight = $derived(open ? height : MIN_HEIGHT);
</div>
<!-- Tab bar -->
<div class="border-brand-sand flex shrink-0 items-center border-b bg-white px-4">
<div class="flex shrink-0 items-center border-b border-line bg-surface px-4">
{#each tabs as tab (tab.id)}
<button
onclick={() => openTab(tab.id)}
class="mr-1 px-3 py-2.5 font-sans text-xs font-medium transition-colors {activeTab === tab.id && open
? 'border-b-2 border-brand-navy text-brand-navy'
: 'text-gray-400 hover:text-brand-navy'}"
? 'border-b-2 border-primary text-ink'
: 'text-ink-3 hover:text-ink'}"
aria-pressed={activeTab === tab.id && open}
>
{tab.label()}
@@ -162,7 +162,7 @@ const panelHeight = $derived(open ? height : MIN_HEIGHT);
onclick={closePanel}
data-testid="panel-close-btn"
aria-label="Panel schließen"
class="hover:bg-brand-sand/50 rounded p-1.5 text-gray-400 transition-colors hover:text-brand-navy"
class="rounded p-1.5 text-ink-3 transition-colors hover:bg-muted hover:text-ink"
>
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />

View File

@@ -58,17 +58,17 @@ const compactMeta = $derived.by(() => {
</script>
<div
class="border-brand-sand z-20 flex shrink-0 items-center justify-between border-b bg-white px-6 py-3 shadow-sm"
class="z-20 flex shrink-0 items-center justify-between border-b border-line bg-surface px-6 py-3 shadow-sm"
data-topbar
>
<!-- Left: back + title -->
<div class="flex min-w-0 items-center gap-4 overflow-hidden">
<a
href="/"
class="group flex shrink-0 items-center gap-2 font-sans text-sm font-medium text-gray-500 transition-colors hover:text-brand-navy"
class="group flex shrink-0 items-center gap-2 font-sans text-sm font-medium text-ink-2 transition-colors hover:text-ink"
>
<div
class="bg-brand-sand flex h-8 w-8 items-center justify-center rounded-full transition-colors group-hover:bg-brand-mint"
class="flex h-8 w-8 items-center justify-center rounded-full bg-canvas transition-colors group-hover:bg-accent"
>
<img
src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Arrow/Arrow-Left-MD.svg"
@@ -80,15 +80,15 @@ const compactMeta = $derived.by(() => {
<span class="hidden sm:inline">{m.btn_back()}</span>
</a>
<div class="min-w-0 border-l border-gray-200 pl-4">
<div class="min-w-0 border-l border-line pl-4">
<h1
class="truncate font-serif text-base leading-tight text-brand-navy"
class="truncate font-serif text-base leading-tight text-ink"
title={doc.title ?? doc.originalFilename ?? ''}
>
{doc.title || doc.originalFilename}
</h1>
{#if compactMeta}
<p class="truncate font-sans text-xs text-gray-500" title={compactMeta}>
<p class="truncate font-sans text-xs text-ink-2" title={compactMeta}>
{compactMeta}
</p>
{/if}
@@ -102,8 +102,8 @@ const compactMeta = $derived.by(() => {
onclick={() => (annotateMode = !annotateMode)}
aria-label={annotateMode ? m.doc_panel_annotate_stop() : m.doc_panel_annotate()}
class="flex items-center gap-1.5 rounded px-3 py-1.5 font-sans text-xs font-medium transition {annotateMode
? 'bg-brand-navy text-white'
: 'border border-brand-navy text-brand-navy hover:bg-brand-navy hover:text-white'}"
? 'bg-primary text-white'
: 'border border-primary text-ink hover:bg-primary hover:text-white'}"
>
<img
src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Note/Note-Add-MD.svg"
@@ -118,7 +118,7 @@ const compactMeta = $derived.by(() => {
{#if canWrite}
<a
href="/documents/{doc.id}/edit"
class="flex items-center gap-2 rounded border border-brand-navy bg-transparent px-3 py-1.5 text-xs font-medium text-brand-navy transition hover:bg-brand-navy hover:text-white"
class="flex items-center gap-2 rounded border border-primary bg-transparent px-3 py-1.5 text-xs font-medium text-ink transition hover:bg-primary hover:text-white"
>
<img
src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Edit-Content-MD.svg"
@@ -134,7 +134,7 @@ const compactMeta = $derived.by(() => {
<a
href={fileUrl}
download={doc.originalFilename}
class="bg-brand-sand/50 rounded border border-transparent p-1.5 text-brand-navy transition hover:bg-brand-mint"
class="rounded border border-transparent bg-muted p-1.5 text-ink transition hover:bg-accent"
title={m.doc_download_title()}
>
<img

View File

@@ -32,9 +32,9 @@ let {
}: Props = $props();
</script>
<div class="absolute inset-0 bg-[#2A2A2A]">
<div class="absolute inset-0 bg-pdf-bg">
{#if isLoading}
<div class="flex h-full flex-col items-center justify-center text-brand-mint">
<div class="flex h-full flex-col items-center justify-center text-accent">
<svg
class="mb-4 h-8 w-8 animate-spin"
xmlns="http://www.w3.org/2000/svg"
@@ -52,7 +52,7 @@ let {
<span class="font-sans text-sm tracking-wide">{m.doc_loading()}</span>
</div>
{:else if error}
<div class="flex h-full flex-col items-center justify-center px-4 text-center text-gray-400">
<div class="flex h-full flex-col items-center justify-center px-4 text-center text-ink-3">
<p class="mb-2 font-serif">{error}</p>
{#if doc.filePath}
<a
@@ -65,8 +65,8 @@ let {
{/if}
</div>
{:else if !doc.filePath}
<div class="flex h-full flex-col items-center justify-center text-gray-400">
<div class="mb-6 rounded-full bg-white/5 p-8">
<div class="flex h-full flex-col items-center justify-center text-ink-3">
<div class="mb-6 rounded-full bg-surface/5 p-8">
<img
src="/degruyter-icons/Simple/Medium-24px/SVG/Action/PDF-Document-MD.svg"
alt=""

View File

@@ -18,14 +18,14 @@ $effect(() => {
<div
bind:this={el}
style={!expanded ? `overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: ${maxLines}` : ''}
class="border-brand-sand bg-brand-sand/30 rounded border p-5 font-serif text-sm leading-relaxed whitespace-pre-wrap text-brand-navy"
class="rounded border border-line bg-muted p-5 font-serif text-sm leading-relaxed whitespace-pre-wrap text-ink"
>
{text}
</div>
{#if isClamped || expanded}
<button
onclick={() => (expanded = !expanded)}
class="mt-2 font-sans text-xs text-gray-400 transition hover:text-brand-navy"
class="mt-2 font-sans text-xs text-ink-3 transition hover:text-ink"
>
{expanded ? m.comp_expandable_show_less() : m.comp_expandable_show_more()}
</button>

View File

@@ -270,11 +270,11 @@ $effect(() => {
<div class="space-y-4 p-6">
{#if historyLoading}
<p class="font-sans text-xs text-gray-400">{m.history_loading()}</p>
<p class="font-sans text-xs text-ink-3">{m.history_loading()}</p>
{:else if !historyLoaded}
<!-- initial state before effect runs — show nothing -->
{:else if versions.length === 0}
<p class="font-serif text-sm text-gray-400 italic">{m.history_empty()}</p>
<p class="font-serif text-sm text-ink-3 italic">{m.history_empty()}</p>
{:else}
<!-- Compare mode toggle -->
<div class="flex justify-end">
@@ -286,8 +286,8 @@ $effect(() => {
selectedVersionId = null;
}}
class="font-sans text-xs font-medium transition {compareMode
? 'text-brand-navy underline'
: 'text-gray-400 hover:text-brand-navy'}"
? 'text-ink underline'
: 'text-ink-3 hover:text-ink'}"
>
{m.history_compare_mode()}
</button>
@@ -296,13 +296,13 @@ $effect(() => {
{#if compareMode}
<div class="space-y-2">
<div>
<label for="compare-a" class="mb-1 block font-sans text-[10px] text-gray-400 uppercase"
<label for="compare-a" class="mb-1 block font-sans text-[10px] text-ink-3 uppercase"
>{m.history_compare_select_a()}</label
>
<select
id="compare-a"
bind:value={compareA}
class="border-brand-sand w-full rounded border bg-white px-2 py-1 font-sans text-xs text-brand-navy focus:ring-1 focus:ring-brand-mint focus:outline-none"
class="w-full rounded border border-line bg-surface px-2 py-1 font-sans text-xs text-ink focus:ring-1 focus:ring-accent focus:outline-none"
>
<option value=""></option>
{#each versions as v, i (v.id)}
@@ -311,13 +311,13 @@ $effect(() => {
</select>
</div>
<div>
<label for="compare-b" class="mb-1 block font-sans text-[10px] text-gray-400 uppercase"
<label for="compare-b" class="mb-1 block font-sans text-[10px] text-ink-3 uppercase"
>{m.history_compare_select_b()}</label
>
<select
id="compare-b"
bind:value={compareB}
class="border-brand-sand w-full rounded border bg-white px-2 py-1 font-sans text-xs text-brand-navy focus:ring-1 focus:ring-brand-mint focus:outline-none"
class="w-full rounded border border-line bg-surface px-2 py-1 font-sans text-xs text-ink focus:ring-1 focus:ring-accent focus:outline-none"
>
<option value=""></option>
{#each versions as v, i (v.id)}
@@ -328,7 +328,7 @@ $effect(() => {
<button
onclick={applyCompare}
disabled={!compareA || !compareB || compareA === compareB}
class="w-full rounded bg-brand-navy px-3 py-1.5 font-sans text-xs font-medium text-white transition hover:bg-brand-navy/80 disabled:cursor-not-allowed disabled:opacity-40"
class="w-full rounded bg-primary px-3 py-1.5 font-sans text-xs font-medium text-white transition hover:bg-primary/80 disabled:cursor-not-allowed disabled:opacity-40"
>
{m.history_compare_apply()}
</button>
@@ -336,23 +336,23 @@ $effect(() => {
<!-- Diff panel for compare mode -->
{#if diffLoading}
<p class="font-sans text-xs text-gray-400">{m.history_loading()}</p>
<p class="font-sans text-xs text-ink-3">{m.history_loading()}</p>
{:else if noDiff}
<div
data-testid="history-diff"
class="border-brand-sand rounded-sm border bg-white p-4 font-serif text-sm text-gray-400 italic"
class="rounded-sm border border-line bg-surface p-4 font-serif text-sm text-ink-3 italic"
>
{m.history_diff_no_changes()}
</div>
{:else if diffEntries.length > 0}
<div
data-testid="history-diff"
class="border-brand-sand space-y-4 rounded-sm border bg-white p-4"
class="space-y-4 rounded-sm border border-line bg-surface p-4"
>
{#each diffEntries as entry (entry.field)}
<div>
<span
class="mb-1.5 block font-sans text-[10px] font-bold tracking-wide text-gray-400 uppercase"
class="mb-1.5 block font-sans text-[10px] font-bold tracking-wide text-ink-3 uppercase"
>{entry.label}</span
>
{#if entry.kind === 'text'}
@@ -371,7 +371,7 @@ $effect(() => {
<div class="flex items-center gap-2 font-serif text-sm">
<span class="text-red-600 line-through">{entry.oldVal || '—'}</span>
<svg
class="h-3 w-3 flex-shrink-0 text-gray-400"
class="h-3 w-3 flex-shrink-0 text-ink-3"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
@@ -412,25 +412,25 @@ $effect(() => {
<button
onclick={() => selectVersion(v.id)}
data-testid="history-version"
class="hover:bg-brand-sand/30 w-full py-2 text-left transition {selectedVersionId ===
class="w-full py-2 text-left transition hover:bg-muted {selectedVersionId ===
v.id
? 'border-l-2 border-brand-mint pl-2'
? 'border-l-2 border-accent pl-2'
: 'pl-0'}"
>
<div class="flex items-baseline justify-between gap-2">
<span class="font-sans text-xs font-medium text-brand-navy">
<span class="font-sans text-xs font-medium text-ink">
Version {i + 1}
</span>
<span class="font-sans text-[10px] text-gray-400">
<span class="font-sans text-[10px] text-ink-3">
{formatDateTime(v.savedAt)}
</span>
</div>
<span class="font-sans text-[11px] text-gray-500">{v.editorName}</span>
<span class="font-sans text-[11px] text-ink-2">{v.editorName}</span>
{#if v.changedFields && v.changedFields.length > 0}
<div class="mt-1 flex flex-wrap gap-1">
{#each v.changedFields as field (field)}
<span
class="bg-brand-sand/50 rounded px-1.5 py-0.5 font-sans text-[10px] tracking-wide text-gray-500 uppercase"
class="rounded bg-muted px-1.5 py-0.5 font-sans text-[10px] tracking-wide text-ink-2 uppercase"
>
{fieldLabels[field] ? fieldLabels[field]() : field}
</span>
@@ -442,23 +442,23 @@ $effect(() => {
<!-- Diff shown inline below the selected version -->
{#if selectedVersionId === v.id}
{#if diffLoading}
<p class="pb-3 pl-2 font-sans text-xs text-gray-400">{m.history_loading()}</p>
<p class="pb-3 pl-2 font-sans text-xs text-ink-3">{m.history_loading()}</p>
{:else if noDiff}
<div
data-testid="history-diff"
class="border-brand-sand mb-2 rounded-sm border bg-white p-4 font-serif text-sm text-gray-400 italic"
class="mb-2 rounded-sm border border-line bg-surface p-4 font-serif text-sm text-ink-3 italic"
>
{m.history_diff_no_changes()}
</div>
{:else if diffEntries.length > 0}
<div
data-testid="history-diff"
class="border-brand-sand mb-2 space-y-4 rounded-sm border bg-white p-4"
class="mb-2 space-y-4 rounded-sm border border-line bg-surface p-4"
>
{#each diffEntries as entry (entry.field)}
<div>
<span
class="mb-1.5 block font-sans text-[10px] font-bold tracking-wide text-gray-400 uppercase"
class="mb-1.5 block font-sans text-[10px] font-bold tracking-wide text-ink-3 uppercase"
>{entry.label}</span
>
{#if entry.kind === 'text'}
@@ -477,7 +477,7 @@ $effect(() => {
<div class="flex items-center gap-2 font-serif text-sm">
<span class="text-red-600 line-through">{entry.oldVal || '—'}</span>
<svg
class="h-3 w-3 flex-shrink-0 text-gray-400"
class="h-3 w-3 flex-shrink-0 text-ink-3"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"

View File

@@ -21,14 +21,14 @@ let { doc }: { doc: Doc } = $props();
<!-- DETAILS GROUP -->
<div>
<h3
class="border-brand-sand mb-4 border-b pb-2 font-sans text-xs font-bold tracking-widest text-brand-navy uppercase"
class="mb-4 border-b border-line pb-2 font-sans text-xs font-bold tracking-widest text-ink uppercase"
>
{m.doc_section_details()}
</h3>
<div class="space-y-5">
<!-- Date -->
<div class="flex items-start">
<span class="mt-0.5 w-8 text-brand-mint">
<span class="mt-0.5 w-8 text-accent">
<img
src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Calendar/Calendar-Add-MD.svg"
alt=""
@@ -37,16 +37,16 @@ let { doc }: { doc: Doc } = $props();
/>
</span>
<div>
<span class="block font-serif text-lg text-brand-navy">
<span class="block font-serif text-lg text-ink">
{doc.documentDate ? formatDate(doc.documentDate) : '—'}
</span>
<span class="font-sans text-xs text-gray-500">{m.doc_label_document_date()}</span>
<span class="font-sans text-xs text-ink-2">{m.doc_label_document_date()}</span>
</div>
</div>
<!-- Creation Location -->
<div class="flex items-start">
<span class="mt-0.5 w-8 text-brand-mint">
<span class="mt-0.5 w-8 text-accent">
<img
src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Location-MD.svg"
alt=""
@@ -55,17 +55,17 @@ let { doc }: { doc: Doc } = $props();
/>
</span>
<div>
<span class="block font-serif text-lg text-brand-navy">
<span class="block font-serif text-lg text-ink">
{doc.location ? doc.location : '—'}
</span>
<span class="font-sans text-xs text-gray-500">{m.doc_label_creation_location()}</span>
<span class="font-sans text-xs text-ink-2">{m.doc_label_creation_location()}</span>
</div>
</div>
<!-- Physical Archive Location -->
{#if doc.documentLocation}
<div class="flex items-start">
<span class="mt-0.5 w-8 text-brand-mint">
<span class="mt-0.5 w-8 text-accent">
<img
src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Folder-MD.svg"
alt=""
@@ -74,10 +74,10 @@ let { doc }: { doc: Doc } = $props();
/>
</span>
<div>
<span class="block font-serif text-lg text-brand-navy">
<span class="block font-serif text-lg text-ink">
{doc.documentLocation}
</span>
<span class="font-sans text-xs text-gray-500"
<span class="font-sans text-xs text-ink-2"
>{m.doc_label_archive_location_original()}</span
>
</div>
@@ -87,7 +87,7 @@ let { doc }: { doc: Doc } = $props();
<!-- Tags -->
{#if doc.tags && doc.tags.length > 0}
<div class="flex items-start">
<span class="mt-0.5 w-8 text-brand-mint">
<span class="mt-0.5 w-8 text-accent">
<img
src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Bookmark/Bookmark-Outline-MD.svg"
alt=""
@@ -100,14 +100,14 @@ let { doc }: { doc: Doc } = $props();
{#each doc.tags as tag (tag.id)}
<a
href="/?tag={encodeURIComponent(tag.name)}"
class="bg-brand-sand/50 inline-flex items-center rounded px-2 py-0.5 text-xs font-bold tracking-wide text-brand-navy uppercase transition-colors hover:bg-brand-navy hover:text-white"
class="inline-flex items-center rounded bg-muted px-2 py-0.5 text-xs font-bold tracking-wide text-ink uppercase transition-colors hover:bg-primary hover:text-white"
title={m.doc_tag_filter_title({ name: tag.name })}
>
{tag.name}
</a>
{/each}
</div>
<span class="font-sans text-xs text-gray-500">{m.form_label_tags()}</span>
<span class="font-sans text-xs text-ink-2">{m.form_label_tags()}</span>
</div>
</div>
{/if}
@@ -117,61 +117,59 @@ let { doc }: { doc: Doc } = $props();
<!-- PERSONEN GROUP -->
<div>
<h3
class="border-brand-sand mb-4 border-b pb-2 font-sans text-xs font-bold tracking-widest text-brand-navy uppercase"
class="mb-4 border-b border-line pb-2 font-sans text-xs font-bold tracking-widest text-ink uppercase"
>
{m.doc_section_persons()}
</h3>
<div class="mb-6">
<span class="mb-2 block font-sans text-xs text-gray-400 uppercase"
>{m.form_label_sender()}</span
>
<span class="mb-2 block font-sans text-xs text-ink-3 uppercase">{m.form_label_sender()}</span>
{#if doc.sender}
<a
href="/persons/{doc.sender.id}"
class="group border-brand-sand bg-brand-sand/20 block rounded border p-3 transition hover:border-brand-mint hover:bg-brand-mint/10"
class="group block rounded border border-line bg-muted p-3 transition hover:border-accent hover:bg-accent/10"
>
<div class="flex items-center gap-3">
<div
class="flex h-8 w-8 items-center justify-center rounded-full bg-brand-navy font-serif text-sm text-white"
class="flex h-8 w-8 items-center justify-center rounded-full bg-primary font-serif text-sm text-white"
>
{doc.sender.firstName[0]}{doc.sender.lastName[0]}
</div>
<div>
<p
class="font-serif text-brand-navy decoration-brand-mint underline-offset-2 group-hover:underline"
class="font-serif text-ink decoration-brand-mint underline-offset-2 group-hover:underline"
>
{doc.sender.firstName}
{doc.sender.lastName}
</p>
{#if doc.sender.alias}
<p class="font-sans text-xs text-gray-500">{doc.sender.alias}</p>
<p class="font-sans text-xs text-ink-2">{doc.sender.alias}</p>
{/if}
</div>
</div>
</a>
{:else}
<span class="font-serif text-sm text-gray-400 italic">{m.doc_sender_not_specified()}</span>
<span class="font-serif text-sm text-ink-3 italic">{m.doc_sender_not_specified()}</span>
{/if}
</div>
<div>
<span class="mb-2 block font-sans text-xs text-gray-400 uppercase"
<span class="mb-2 block font-sans text-xs text-ink-3 uppercase"
>{m.form_label_receivers()}</span
>
{#if doc.receivers && doc.receivers.length > 0}
<div class="space-y-2">
{#each doc.receivers as receiver (receiver.id)}
<div
class="group border-brand-sand flex items-center justify-between rounded border bg-white p-3 transition hover:border-brand-navy"
class="group flex items-center justify-between rounded border border-line bg-surface p-3 transition hover:border-primary"
>
<a href="/persons/{receiver.id}" class="flex min-w-0 flex-1 items-center gap-3">
<div
class="flex h-6 w-6 items-center justify-center rounded-full bg-gray-100 font-serif text-xs text-gray-500"
class="flex h-6 w-6 items-center justify-center rounded-full bg-gray-100 font-serif text-xs text-ink-2"
>
{receiver.firstName[0]}{receiver.lastName[0]}
</div>
<span class="truncate font-serif text-sm text-brand-navy">
<span class="truncate font-serif text-sm text-ink">
{receiver.firstName}
{receiver.lastName}
</span>
@@ -180,7 +178,7 @@ let { doc }: { doc: Doc } = $props();
{#if doc.sender}
<a
href="/conversations?senderId={doc.sender.id}&receiverId={receiver.id}"
class="text-gray-300 transition hover:text-brand-mint"
class="text-ink-3 transition hover:text-accent"
title={m.doc_conversation_title()}
>
<img
@@ -195,7 +193,7 @@ let { doc }: { doc: Doc } = $props();
{/each}
</div>
{:else}
<span class="font-serif text-sm text-gray-400 italic">{m.doc_no_receivers()}</span>
<span class="font-serif text-sm text-ink-3 italic">{m.doc_no_receivers()}</span>
{/if}
</div>
</div>

View File

@@ -12,28 +12,24 @@ let { doc }: { doc: Doc } = $props();
<div class="flex justify-center px-6 py-8">
<div class="w-full max-w-prose space-y-8">
{#if !doc.summary && !doc.transcription}
<p class="font-serif text-sm text-gray-400 italic"></p>
<p class="font-serif text-sm text-ink-3 italic"></p>
{/if}
{#if doc.summary}
<div>
<span
class="mb-3 block font-sans text-xs font-bold tracking-widest text-gray-400 uppercase"
>
<span class="mb-3 block font-sans text-xs font-bold tracking-widest text-ink-3 uppercase">
{m.doc_label_summary()}
</span>
<p class="font-serif text-base leading-relaxed text-brand-navy">{doc.summary}</p>
<p class="font-serif text-base leading-relaxed text-ink">{doc.summary}</p>
</div>
{/if}
{#if doc.transcription}
<div>
<span
class="mb-3 block font-sans text-xs font-bold tracking-widest text-gray-400 uppercase"
>
<span class="mb-3 block font-sans text-xs font-bold tracking-widest text-ink-3 uppercase">
{m.form_label_transcription()}
</span>
<p class="font-serif text-base leading-relaxed whitespace-pre-wrap text-brand-navy">
<p class="font-serif text-base leading-relaxed whitespace-pre-wrap text-ink">
{doc.transcription}
</p>
</div>

View File

@@ -295,25 +295,23 @@ function zoomOut() {
</script>
{#if !url}
<div class="flex h-full w-full items-center justify-center bg-[#2A2A2A] text-gray-400">
<div class="flex h-full w-full items-center justify-center bg-pdf-bg text-ink-3">
<p class="font-sans text-sm">Keine Datei vorhanden</p>
</div>
{:else if error}
<div
class="flex h-full w-full flex-col items-center justify-center gap-3 bg-[#2A2A2A] text-gray-300"
>
<div class="flex h-full w-full flex-col items-center justify-center gap-3 bg-pdf-bg text-ink-3">
<p class="font-sans text-sm text-red-400">Fehler beim Laden der PDF</p>
<a
href={url}
target="_blank"
rel="noopener noreferrer"
class="font-sans text-xs text-brand-mint underline hover:opacity-80"
class="font-sans text-xs text-accent underline hover:opacity-80"
>
Direkt öffnen
</a>
</div>
{:else}
<div class="flex h-full w-full flex-col bg-[#2A2A2A]">
<div class="flex h-full w-full flex-col bg-pdf-bg">
{#if outdatedCount > 0}
<div
class="flex shrink-0 items-center gap-2 border-b border-amber-500/30 bg-amber-500/10 px-4 py-2"
@@ -337,7 +335,7 @@ function zoomOut() {
{/if}
<!-- Controls -->
<div
class="flex shrink-0 items-center justify-between gap-2 border-b border-white/10 px-4 py-2"
class="flex shrink-0 items-center justify-between gap-2 border-b border-pdf-ctrl px-4 py-2"
>
<!-- Page navigation -->
<div class="flex items-center gap-2">
@@ -345,7 +343,7 @@ function zoomOut() {
onclick={prevPage}
disabled={currentPage <= 1}
aria-label="Zurück"
class="rounded p-1 text-gray-300 transition hover:bg-white/10 disabled:opacity-40"
class="rounded p-1 text-ink-3 transition hover:bg-surface/10 disabled:opacity-40"
>
<svg
class="h-4 w-4"
@@ -359,7 +357,7 @@ function zoomOut() {
</button>
{#if totalPages > 0}
<span class="font-sans text-xs text-gray-300 tabular-nums">
<span class="font-sans text-xs text-ink-3 tabular-nums">
{currentPage} / {totalPages}
</span>
{/if}
@@ -368,7 +366,7 @@ function zoomOut() {
onclick={nextPage}
disabled={!pdfDoc || currentPage >= totalPages}
aria-label="Weiter"
class="rounded p-1 text-gray-300 transition hover:bg-white/10 disabled:opacity-40"
class="rounded p-1 text-ink-3 transition hover:bg-surface/10 disabled:opacity-40"
>
<svg
class="h-4 w-4"
@@ -387,7 +385,7 @@ function zoomOut() {
<button
onclick={zoomOut}
aria-label="Verkleinern"
class="rounded p-1 text-gray-300 transition hover:bg-white/10"
class="rounded p-1 text-ink-3 transition hover:bg-surface/10"
>
<svg
class="h-4 w-4"
@@ -405,7 +403,7 @@ function zoomOut() {
<button
onclick={zoomIn}
aria-label="Vergrößern"
class="rounded p-1 text-gray-300 transition hover:bg-white/10"
class="rounded p-1 text-ink-3 transition hover:bg-surface/10"
>
<svg
class="h-4 w-4"
@@ -438,8 +436,8 @@ function zoomOut() {
onclick={() => (showAnnotations = !showAnnotations)}
aria-label={showAnnotations ? m.pdf_annotations_hide() : m.pdf_annotations_show()}
class="flex items-center gap-1.5 rounded px-2 py-1 font-sans text-xs transition {showAnnotations
? 'text-gray-300 hover:bg-white/10'
: 'bg-white/10 text-brand-mint'}"
? 'text-ink-3 hover:bg-surface/10'
: 'bg-surface/10 text-accent'}"
>
<svg
class="h-3.5 w-3.5 shrink-0"

View File

@@ -80,18 +80,18 @@ function clickOutside(node: HTMLElement) {
<div class="relative" use:clickOutside>
<div
class="flex min-h-[42px] flex-wrap gap-2 rounded border border-gray-300 bg-white p-2 focus-within:border-brand-navy focus-within:ring-1 focus-within:ring-brand-navy"
class="flex min-h-[42px] flex-wrap gap-2 rounded border border-gray-300 bg-surface p-2 focus-within:border-ink focus-within:ring-1 focus-within:ring-ink"
>
{#each selectedPersons as person (person.id)}
<span
class="bg-brand-sand/40 inline-flex items-center gap-1 rounded px-2 py-1 text-sm font-medium text-brand-navy"
class="inline-flex items-center gap-1 rounded bg-muted px-2 py-1 text-sm font-medium text-ink"
>
{person.firstName}
{person.lastName}
<button
type="button"
onclick={() => removePerson(person.id)}
class="ml-0.5 text-brand-navy/50 hover:text-red-500 focus:outline-none"
class="ml-0.5 text-ink/50 hover:text-red-500 focus:outline-none"
aria-label={m.comp_multiselect_remove()}
>
<svg class="h-3 w-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -121,14 +121,14 @@ function clickOutside(node: HTMLElement) {
{#if showDropdown && (results.length > 0 || loading)}
<div
style={dropdownStyle}
class="ring-opacity-5 z-50 max-h-60 overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black sm:text-sm"
class="ring-opacity-5 z-50 max-h-60 overflow-auto rounded-md bg-surface py-1 text-base shadow-lg ring-1 ring-black sm:text-sm"
>
{#if loading}
<div class="p-2 text-sm text-gray-500">{m.comp_multiselect_loading()}</div>
<div class="p-2 text-sm text-ink-2">{m.comp_multiselect_loading()}</div>
{:else}
{#each results as person (person.id)}
<div
class="hover:bg-brand-sand/30 cursor-pointer py-2 pr-9 pl-3 text-gray-900 select-none"
class="cursor-pointer py-2 pr-9 pl-3 text-gray-900 select-none hover:bg-muted"
onclick={() => selectPerson(person)}
onkeydown={(e) => e.key === 'Enter' && selectPerson(person)}
role="button"

View File

@@ -128,10 +128,10 @@ function clickOutside(node: HTMLElement) {
{#if showDropdown && (results.length > 0 || loading)}
<div
class="ring-opacity-5 absolute top-full left-0 z-50 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black focus:outline-none sm:text-sm"
class="ring-opacity-5 absolute top-full left-0 z-50 mt-1 max-h-60 w-full overflow-auto rounded-md bg-surface py-1 text-base shadow-lg ring-1 ring-black focus:outline-none sm:text-sm"
>
{#if loading}
<div class="p-2 text-sm text-gray-500">{m.comp_typeahead_loading()}</div>
<div class="p-2 text-sm text-ink-2">{m.comp_typeahead_loading()}</div>
{:else}
{#each results as person (person.id)}
<div

View File

@@ -85,19 +85,17 @@ function clickOutside(node: HTMLElement) {
<div class="w-full" use:clickOutside>
<!-- Tag Container -->
<div
class="flex min-h-[42px] flex-wrap gap-2 rounded border border-gray-300 bg-white p-2 focus-within:border-brand-navy focus-within:ring-1 focus-within:ring-brand-navy"
class="flex min-h-[42px] flex-wrap gap-2 rounded border border-gray-300 bg-surface p-2 focus-within:border-ink focus-within:ring-1 focus-within:ring-ink"
>
<!-- Render Selected Tags -->
{#each tags as tag, i (i)}
<span
class="bg-brand-sand/30 flex items-center gap-1 rounded px-2 py-1 text-sm font-medium text-brand-navy"
>
<span class="flex items-center gap-1 rounded bg-muted px-2 py-1 text-sm font-medium text-ink">
{tag}
<button
type="button"
onclick={() => removeTag(i)}
aria-label={m.comp_taginput_remove()}
class="text-brand-navy/50 hover:text-red-500 focus:outline-none"
class="text-ink/50 hover:text-red-500 focus:outline-none"
>
<svg class="h-3 w-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"
><path
@@ -130,15 +128,15 @@ function clickOutside(node: HTMLElement) {
<!-- Typeahead Dropdown -->
{#if showSuggestions && suggestions.length > 0}
<ul
class="absolute top-full left-0 z-50 mt-1 max-h-48 w-full overflow-y-auto rounded border border-gray-200 bg-white shadow-lg"
class="absolute top-full left-0 z-50 mt-1 max-h-48 w-full overflow-y-auto rounded border border-line bg-surface shadow-lg"
>
{#each suggestions as suggestion, i (i)}
<li
role="option"
aria-selected={i === activeIndex}
tabindex="0"
class="hover:bg-brand-sand/20 cursor-pointer px-3 py-2 text-sm {i === activeIndex
? 'bg-brand-sand/20 font-bold text-brand-navy'
class="cursor-pointer px-3 py-2 text-sm hover:bg-muted {i === activeIndex
? 'bg-muted font-bold text-ink'
: 'text-gray-700'}"
onclick={() => addTag(suggestion)}
onkeydown={(e) => e.key === 'Enter' && addTag(suggestion)}
@@ -151,6 +149,6 @@ function clickOutside(node: HTMLElement) {
</div>
</div>
{#if allowCreation}
<p class="mt-1 text-xs text-gray-400">{m.comp_taginput_create_hint()}</p>
<p class="mt-1 text-xs text-ink-3">{m.comp_taginput_create_hint()}</p>
{/if}
</div>