diff --git a/web/src/app.css b/web/src/app.css index 9a73efd..32456ac 100644 --- a/web/src/app.css +++ b/web/src/app.css @@ -1,9 +1,23 @@ @import "tailwindcss"; +/* Dark variant definition */ +@variant dark (&:where(.dark, .dark *)); + @theme { /* Custom colors */ --color-zinc-850: #1d1d20; + /* Semantic color tokens - Light mode defaults */ + --color-bg-primary: #ffffff; + --color-bg-secondary: #f4f4f5; + --color-surface: #ffffff; + --color-surface-secondary: #fafafa; + --color-border: #e4e4e7; + --color-border-subtle: #f4f4f5; + --color-text-primary: #18181b; + --color-text-secondary: #52525b; + --color-text-tertiary: #71717a; + /* Custom font sizes */ --font-size-10xl: 10rem; @@ -29,22 +43,22 @@ --animate-fade-left: fade-left 3s ease-in-out forwards; --animate-fade-right: fade-right 3s ease-in-out forwards; - /* Admin colors - Geist-inspired semantic scale */ - --color-admin-bg: #0a0a0b; - --color-admin-bg-secondary: #18181b; - --color-admin-surface: #27272a; - --color-admin-surface-hover: #3f3f46; - --color-admin-border: #27272a; - --color-admin-border-hover: #3f3f46; - --color-admin-text: #fafafa; - --color-admin-text-secondary: #a1a1aa; - --color-admin-text-muted: #71717a; + /* Admin colors - Light mode defaults */ + --color-admin-bg: #f9fafb; + --color-admin-bg-secondary: #ffffff; + --color-admin-surface: #ffffff; + --color-admin-surface-hover: #f3f4f6; + --color-admin-border: #e5e7eb; + --color-admin-border-hover: #d1d5db; + --color-admin-text: #111827; + --color-admin-text-secondary: #4b5563; + --color-admin-text-muted: #6b7280; --color-admin-accent: #6366f1; --color-admin-accent-hover: #818cf8; /* Legacy aliases for backward compatibility */ - --color-admin-panel: #18181b; - --color-admin-hover: #3f3f46; + --color-admin-panel: #ffffff; + --color-admin-hover: #f3f4f6; /* Status colors */ --color-status-active: #22c55e; @@ -56,6 +70,34 @@ --color-status-info: #06b6d4; } +/* Dark mode overrides */ +.dark { + --color-bg-primary: #000000; + --color-bg-secondary: #09090b; + --color-surface: #18181b; + --color-surface-secondary: #27272a; + --color-border: #27272a; + --color-border-subtle: #18181b; + --color-text-primary: #fafafa; + --color-text-secondary: #d4d4d8; + --color-text-tertiary: #a1a1aa; + + /* Admin colors - Dark mode overrides */ + --color-admin-bg: #0a0a0b; + --color-admin-bg-secondary: #18181b; + --color-admin-surface: #27272a; + --color-admin-surface-hover: #3f3f46; + --color-admin-border: #27272a; + --color-admin-border-hover: #3f3f46; + --color-admin-text: #fafafa; + --color-admin-text-secondary: #a1a1aa; + --color-admin-text-muted: #71717a; + + /* Legacy aliases */ + --color-admin-panel: #18181b; + --color-admin-hover: #3f3f46; +} + @keyframes fade { 0% { opacity: 0%; @@ -126,11 +168,27 @@ html, body { - @apply font-inter overflow-x-hidden text-white; + @apply font-inter overflow-x-hidden; + color: var(--color-text-primary); + transition: background-color 0.3s ease-in-out, color 0.3s ease-in-out; } body { @apply h-full; + background-color: var(--color-bg-primary); +} + +/* Smooth theme transitions for all elements */ +*:not(canvas):not([class*="animate-"]) { + transition-property: background-color, border-color, color, fill, stroke; + transition-duration: 0.3s; + transition-timing-function: ease-in-out; +} + +/* Elements with explicit transition classes should extend, not replace */ +[class*="transition-colors"], +[class*="transition-all"] { + transition-duration: 0.3s !important; } /* OverlayScrollbars theme customization */ diff --git a/web/src/app.html b/web/src/app.html index adf8bd8..c7e85f4 100644 --- a/web/src/app.html +++ b/web/src/app.html @@ -3,6 +3,15 @@ + %sveltekit.head% diff --git a/web/src/lib/components/AppWrapper.svelte b/web/src/lib/components/AppWrapper.svelte index 1ec3db0..43dbda4 100644 --- a/web/src/lib/components/AppWrapper.svelte +++ b/web/src/lib/components/AppWrapper.svelte @@ -2,23 +2,31 @@ import { cn } from "$lib/utils"; import type { Snippet } from "svelte"; import Dots from "./Dots.svelte"; + import ThemeToggle from "./ThemeToggle.svelte"; let { class: className = "", backgroundClass = "", - bgColor = "bg-black", + bgColor = "", + showThemeToggle = true, children, }: { class?: string; backgroundClass?: string; bgColor?: string; + showThemeToggle?: boolean; children?: Snippet; } = $props(); -
+
-
+{#if showThemeToggle} +
+ +
+{/if} +
{#if children} {@render children()} {/if} diff --git a/web/src/lib/components/ProjectCard.svelte b/web/src/lib/components/ProjectCard.svelte index 9d5c639..a177ab4 100644 --- a/web/src/lib/components/ProjectCard.svelte +++ b/web/src/lib/components/ProjectCard.svelte @@ -39,22 +39,22 @@ target="_blank" rel="noopener noreferrer" class={cn( - "group flex h-44 flex-col gap-2.5 rounded-lg border border-zinc-800 bg-zinc-900/50 p-3 transition-all hover:border-zinc-700 hover:bg-zinc-800/70", + "group flex h-44 flex-col gap-2.5 rounded-lg border border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/50 p-3 transition-all hover:border-zinc-300 dark:hover:border-zinc-700 hover:bg-zinc-100 dark:hover:bg-zinc-800/70", className, )} >

{project.name}

- + {formatDate(project.updatedAt)}
-

+

{project.shortDescription}

@@ -63,7 +63,7 @@ {#each project.tags as tag (tag.name)} {#if tag.iconSvg} @@ -80,22 +80,22 @@ {:else}

{project.name}

- + {formatDate(project.updatedAt)}
-

+

{project.shortDescription}

@@ -103,7 +103,7 @@
{#each project.tags as tag (tag.name)} {#if tag.iconSvg} diff --git a/web/src/lib/components/ThemeToggle.svelte b/web/src/lib/components/ThemeToggle.svelte new file mode 100644 index 0000000..f00adb8 --- /dev/null +++ b/web/src/lib/components/ThemeToggle.svelte @@ -0,0 +1,25 @@ + + + diff --git a/web/src/lib/components/admin/Button.svelte b/web/src/lib/components/admin/Button.svelte index 27ac5eb..248176e 100644 --- a/web/src/lib/components/admin/Button.svelte +++ b/web/src/lib/components/admin/Button.svelte @@ -28,12 +28,12 @@ const variantStyles = { primary: - "bg-indigo-600 text-white hover:bg-indigo-500 focus-visible:ring-indigo-500 shadow-sm hover:shadow", + "bg-admin-accent text-white hover:bg-admin-accent-hover focus-visible:ring-admin-accent shadow-sm hover:shadow", secondary: - "bg-transparent text-admin-text border border-zinc-700 hover:border-zinc-600 hover:bg-zinc-800/50 focus-visible:ring-zinc-500", + "bg-transparent text-admin-text border border-admin-border hover:border-admin-border-hover hover:bg-admin-surface-hover/50 focus-visible:ring-admin-accent", danger: "bg-red-600 text-white hover:bg-red-500 focus-visible:ring-red-500 shadow-sm hover:shadow", - ghost: "text-admin-text hover:bg-zinc-800/50 focus-visible:ring-zinc-500", + ghost: "text-admin-text hover:bg-admin-surface-hover focus-visible:ring-admin-accent", }; const sizeStyles = { diff --git a/web/src/lib/components/admin/ColorPicker.svelte b/web/src/lib/components/admin/ColorPicker.svelte index 7f624bb..c986214 100644 --- a/web/src/lib/components/admin/ColorPicker.svelte +++ b/web/src/lib/components/admin/ColorPicker.svelte @@ -77,7 +77,7 @@
{#if label} - + {/if} @@ -88,8 +88,8 @@ class={cn( "size-8 rounded border-2 transition-all hover:scale-110", selectedColor === preset.value - ? "border-white ring-2 ring-white/20" - : "border-zinc-700 hover:border-zinc-500", + ? "border-admin-accent ring-2 ring-admin-accent/20" + : "border-admin-border hover:border-admin-border-hover", )} style="background-color: #{preset.value}" title={preset.name} @@ -103,13 +103,13 @@ class={cn( "size-8 rounded border-2 transition-all hover:scale-110 flex items-center justify-center", !selectedColor - ? "border-white ring-2 ring-white/20 bg-zinc-800" - : "border-zinc-700 hover:border-zinc-500 bg-zinc-900", + ? "border-admin-accent ring-2 ring-admin-accent/20 bg-admin-surface-hover" + : "border-admin-border hover:border-admin-border-hover bg-admin-surface", )} title="No color" onclick={clearColor} > - +
@@ -117,7 +117,7 @@
- #
@@ -143,7 +143,7 @@ {#if selectedColor && validateHexColor(selectedColor)}
diff --git a/web/src/lib/components/admin/EventLog.svelte b/web/src/lib/components/admin/EventLog.svelte index 74a781b..3d785eb 100644 --- a/web/src/lib/components/admin/EventLog.svelte +++ b/web/src/lib/components/admin/EventLog.svelte @@ -39,7 +39,7 @@ defer style="max-height: {maxHeight}" > -
+
{#each events as event (event.id)} {@const levelColors = { info: "text-cyan-500/60", @@ -51,7 +51,7 @@ warning: "WARN", error: "ERR", }} -
+
@@ -60,23 +60,23 @@ > {levelLabels[event.level]} - + {event.message} - - target={event.target} + + target={event.target}
{#if showMetadata && event.metadata} {/if} - + {formatTimestamp(event.timestamp)}
@@ -85,10 +85,10 @@ {#if showMetadata && expandedEventId === event.id && event.metadata}
-

Metadata:

-
{JSON.stringify(
+              

Metadata:

+
{JSON.stringify(
                   event.metadata,
                   null,
                   2,
diff --git a/web/src/lib/components/admin/IconPicker.svelte b/web/src/lib/components/admin/IconPicker.svelte
index 129f559..942b13f 100644
--- a/web/src/lib/components/admin/IconPicker.svelte
+++ b/web/src/lib/components/admin/IconPicker.svelte
@@ -211,7 +211,7 @@
           
           {@html selectedIconSvg}
         {:else}
-          
+
{/if}
@@ -234,8 +234,8 @@ class={cn( "rounded-md px-3 py-1.5 text-sm font-medium transition-colors", selectedCollection === "all" - ? "bg-indigo-600 text-white" - : "bg-admin-panel text-admin-text-muted hover:bg-admin-hover hover:text-admin-text", + ? "bg-admin-accent text-white" + : "bg-admin-surface text-admin-text-muted hover:bg-admin-surface-hover hover:text-admin-text", )} onclick={() => (selectedCollection = "all")} > @@ -247,8 +247,8 @@ class={cn( "rounded-md px-3 py-1.5 text-sm font-medium transition-colors", selectedCollection === collection.id - ? "bg-indigo-600 text-white" - : "bg-admin-panel text-admin-text-muted hover:bg-admin-hover hover:text-admin-text", + ? "bg-admin-accent text-white" + : "bg-admin-surface text-admin-text-muted hover:bg-admin-surface-hover hover:text-admin-text", )} onclick={() => (selectedCollection = collection.id)} > @@ -265,7 +265,7 @@ type="text" bind:value={searchQuery} {placeholder} - class="w-full rounded-md border border-admin-border bg-admin-panel px-3 py-2 text-sm text-admin-text placeholder:text-admin-text-muted focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500" + class="w-full rounded-md border border-admin-border bg-admin-bg-secondary px-3 py-2 text-sm text-admin-text placeholder:text-admin-text-muted focus:border-admin-accent focus:outline-none focus:ring-1 focus:ring-admin-accent" onfocus={handleInputFocus} onblur={handleInputBlur} /> @@ -273,7 +273,7 @@ {#if showDropdown && searchResults.length > 0}
@@ -293,14 +293,14 @@ {@html cachedSvg} {:else}
{/if}
@@ -318,7 +318,7 @@
{:else if showDropdown && searchQuery && !isLoading}
No icons found for "{searchQuery}"
diff --git a/web/src/lib/components/admin/Input.svelte b/web/src/lib/components/admin/Input.svelte index 8b308d7..43b5514 100644 --- a/web/src/lib/components/admin/Input.svelte +++ b/web/src/lib/components/admin/Input.svelte @@ -42,7 +42,7 @@ const inputId = `input-${Math.random().toString(36).substring(2, 11)}`; const inputStyles = - "block w-full rounded-md border border-zinc-700 bg-zinc-900 px-3 py-2 text-sm text-zinc-200 placeholder:text-zinc-500 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 disabled:cursor-not-allowed disabled:opacity-50 transition-colors"; + "block w-full rounded-md border border-admin-border bg-admin-bg-secondary px-3 py-2 text-sm text-admin-text placeholder:text-admin-text-muted focus:border-admin-accent focus:outline-none focus:ring-1 focus:ring-admin-accent disabled:cursor-not-allowed disabled:opacity-50 transition-colors"; const errorStyles = $derived( error ? "border-red-500 focus:border-red-500 focus:ring-red-500" : "", diff --git a/web/src/lib/components/admin/Modal.svelte b/web/src/lib/components/admin/Modal.svelte index 4f9b800..dba4713 100644 --- a/web/src/lib/components/admin/Modal.svelte +++ b/web/src/lib/components/admin/Modal.svelte @@ -51,18 +51,18 @@ tabindex="-1" >