From aaa916fedf8da5336d5f5d52aedb34d214803449 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 14 Jan 2026 17:09:00 +0000 Subject: [PATCH] fix: Improve theme contrast and persistence - Fix theme persistence: properly check localStorage before using default - Plan9: make blue subtler (reduce saturation), darken gradient colors for better contrast on pale yellow background - Light theme: improve contrast with darker muted foreground and borders - Change theme selector from flat list to dropdown submenu --- src/components/nostr/user-menu.tsx | 105 ++++++++++++++--------------- src/lib/themes/builtin/light.ts | 18 ++--- src/lib/themes/builtin/plan9.ts | 56 +++++++-------- src/lib/themes/context.tsx | 11 ++- 4 files changed, 97 insertions(+), 93 deletions(-) diff --git a/src/components/nostr/user-menu.tsx b/src/components/nostr/user-menu.tsx index c6afaea..111a2ea 100644 --- a/src/components/nostr/user-menu.tsx +++ b/src/components/nostr/user-menu.tsx @@ -13,6 +13,9 @@ import { DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, + DropdownMenuSub, + DropdownMenuSubTrigger, + DropdownMenuSubContent, } from "@/components/ui/dropdown-menu"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import Nip05 from "./nip05"; @@ -155,33 +158,30 @@ export default function UserMenu() { )} - - - - Theme - - {availableThemes.map((theme) => ( - setTheme(theme.id)} - > - - {theme.name} - {themeId === theme.id && ( - - active - - )} - - ))} - + + + + Theme + + + {availableThemes.map((theme) => ( + setTheme(theme.id)} + > + + {theme.name} + + ))} + + Log out @@ -189,33 +189,30 @@ export default function UserMenu() { ) : ( <> - - - - Theme - - {availableThemes.map((theme) => ( - setTheme(theme.id)} - > - - {theme.name} - {themeId === theme.id && ( - - active - - )} - - ))} - + + + + Theme + + + {availableThemes.map((theme) => ( + setTheme(theme.id)} + > + + {theme.name} + + ))} + + setShowLogin(true)}> Log in diff --git a/src/lib/themes/builtin/light.ts b/src/lib/themes/builtin/light.ts index 82ab7d6..a49a412 100644 --- a/src/lib/themes/builtin/light.ts +++ b/src/lib/themes/builtin/light.ts @@ -25,23 +25,23 @@ export const lightTheme: Theme = { secondary: "210 40% 96.1%", secondaryForeground: "222.2 47.4% 11.2%", - accent: "270 80% 60%", + accent: "270 70% 55%", accentForeground: "0 0% 100%", muted: "210 40% 96.1%", - mutedForeground: "215.4 16.3% 46.9%", + mutedForeground: "215.4 16.3% 40%", - destructive: "0 84.2% 60.2%", - destructiveForeground: "210 40% 98%", + destructive: "0 72% 51%", + destructiveForeground: "0 0% 100%", - border: "214.3 31.8% 91.4%", + border: "214.3 31.8% 85%", input: "214.3 31.8% 91.4%", ring: "222.2 84% 4.9%", - // Status colors - success: "142 76% 36%", - warning: "45 93% 47%", - info: "199 89% 48%", + // Status colors (darker for better contrast) + success: "142 70% 30%", + warning: "38 92% 40%", + info: "199 80% 40%", }, syntax: { diff --git a/src/lib/themes/builtin/plan9.ts b/src/lib/themes/builtin/plan9.ts index f467cd9..b982f11 100644 --- a/src/lib/themes/builtin/plan9.ts +++ b/src/lib/themes/builtin/plan9.ts @@ -27,9 +27,9 @@ export const plan9Theme: Theme = { popover: "60 100% 97%", popoverForeground: "0 0% 0%", - // Dark blue for interactive elements - primary: "220 100% 25%", - primaryForeground: "60 100% 94%", + // Muted blue for interactive elements (subtler than before) + primary: "220 50% 35%", + primaryForeground: "60 100% 96%", // Muted green secondary secondary: "120 30% 88%", @@ -41,54 +41,54 @@ export const plan9Theme: Theme = { // Muted yellow for subdued elements muted: "60 30% 88%", - mutedForeground: "0 0% 35%", + mutedForeground: "0 0% 25%", // Red for destructive - destructive: "0 70% 45%", + destructive: "0 70% 40%", destructiveForeground: "0 0% 100%", - // Dark blue borders - border: "220 40% 50%", + // Subtle borders + border: "60 20% 75%", input: "60 30% 92%", - ring: "220 100% 25%", + ring: "220 50% 35%", - // Status colors - success: "120 60% 30%", - warning: "45 90% 45%", - info: "200 80% 40%", + // Status colors (darker for contrast) + success: "120 60% 25%", + warning: "35 90% 35%", + info: "200 70% 35%", }, syntax: { // Acme-inspired syntax colors comment: "0 0% 45%", // Gray punctuation: "0 0% 25%", // Dark gray - property: "220 100% 25%", // Dark blue + property: "220 50% 35%", // Muted blue string: "120 60% 28%", // Forest green - keyword: "280 60% 35%", // Purple - function: "220 100% 25%", // Dark blue + keyword: "280 50% 35%", // Muted purple + function: "220 50% 35%", // Muted blue variable: "0 0% 0%", // Black operator: "0 0% 15%", // Near black // Diff colors - subtle on yellow background diffInserted: "120 60% 25%", - diffInsertedBg: "120 50% 85%", - diffDeleted: "0 65% 40%", - diffDeletedBg: "0 50% 90%", - diffMeta: "200 70% 35%", - diffMetaBg: "200 50% 88%", + diffInsertedBg: "120 40% 85%", + diffDeleted: "0 60% 40%", + diffDeletedBg: "0 40% 90%", + diffMeta: "200 50% 35%", + diffMetaBg: "200 40% 88%", }, scrollbar: { - thumb: "220 30% 55%", - thumbHover: "220 40% 45%", - track: "60 30% 90%", + thumb: "60 20% 70%", + thumbHover: "60 25% 60%", + track: "60 30% 92%", }, gradient: { - // Muted gradient for Plan9 aesthetic - color1: "180 140 20", // Olive/mustard - color2: "200 120 50", // Burnt orange - color3: "100 60 180", // Muted purple - color4: "40 160 180", // Teal + // Darker gradient for contrast on pale yellow background + color1: "140 110 20", // Dark olive/mustard + color2: "180 90 40", // Dark burnt orange + color3: "80 50 140", // Dark muted purple + color4: "30 120 130", // Dark teal }, }; diff --git a/src/lib/themes/context.tsx b/src/lib/themes/context.tsx index 745ebae..7652240 100644 --- a/src/lib/themes/context.tsx +++ b/src/lib/themes/context.tsx @@ -108,9 +108,16 @@ export function ThemeProvider({ children, defaultTheme, }: ThemeProviderProps): React.ReactElement { - // Initialize from localStorage or default + // Initialize from localStorage, falling back to defaultTheme prop or DEFAULT_THEME_ID const [themeId, setThemeIdState] = React.useState(() => { - return defaultTheme || getSavedThemeId(); + const saved = getSavedThemeId(); + // Only use defaultTheme if nothing is saved (saved returns DEFAULT_THEME_ID when empty) + // Check localStorage directly to see if user has explicitly chosen a theme + const hasExplicitSave = localStorage.getItem(STORAGE_KEY) !== null; + if (hasExplicitSave) { + return saved; + } + return defaultTheme || DEFAULT_THEME_ID; }); const [customThemes, setCustomThemes] = React.useState(() => {