feat: add light/dark theme toggle with system preference detection

- Implement theme store with localStorage persistence
- Add ThemeToggle component with animated icon transitions
- Update color system with semantic tokens for light/dark modes
- Add blocking script in app.html to prevent FOUC
- Apply theme-aware styling across all public and admin pages
This commit is contained in:
2026-01-06 20:31:24 -06:00
parent 0149dc1df9
commit 5c4d3b6efa
26 changed files with 336 additions and 190 deletions
+71 -13
View File
@@ -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 */