feat: Add theme selector to user menu, remove configurable border radius

- Remove border radius from theme configuration (borders are always square)
- Add theme selector dropdown to user menu (available to all users)
- Theme selector shows active theme indicator
- Theme selection persists via localStorage
This commit is contained in:
Claude
2026-01-14 16:55:17 +00:00
parent 2e20d8ed57
commit 8c642eb91c
6 changed files with 64 additions and 31 deletions

View File

@@ -1,4 +1,4 @@
import { User, HardDrive } from "lucide-react";
import { User, HardDrive, Palette } from "lucide-react";
import accounts from "@/services/accounts";
import { useProfile } from "@/hooks/useProfile";
import { use$ } from "applesauce-react/hooks";
@@ -20,6 +20,7 @@ import { RelayLink } from "./RelayLink";
import SettingsDialog from "@/components/SettingsDialog";
import LoginDialog from "./LoginDialog";
import { useState } from "react";
import { useTheme } from "@/lib/themes";
function UserAvatar({ pubkey }: { pubkey: string }) {
const profile = useProfile(pubkey);
@@ -57,6 +58,7 @@ export default function UserMenu() {
const blossomServers = state.activeAccount?.blossomServers;
const [showSettings, setShowSettings] = useState(false);
const [showLogin, setShowLogin] = useState(false);
const { themeId, setTheme, availableThemes } = useTheme();
function openProfile() {
if (!account?.pubkey) return;
@@ -153,22 +155,72 @@ export default function UserMenu() {
)}
<DropdownMenuSeparator />
{/* <DropdownMenuItem
onClick={() => setShowSettings(true)}
className="cursor-pointer"
>
<Settings className="mr-2 size-4" />
Settings
</DropdownMenuItem>
<DropdownMenuSeparator /> */}
<DropdownMenuGroup>
<DropdownMenuLabel className="text-xs text-muted-foreground font-normal flex items-center gap-1.5">
<Palette className="size-3.5" />
<span>Theme</span>
</DropdownMenuLabel>
{availableThemes.map((theme) => (
<DropdownMenuItem
key={theme.id}
className="cursor-crosshair"
onClick={() => setTheme(theme.id)}
>
<span
className={`size-3 rounded-full mr-2 ${
themeId === theme.id
? "bg-primary"
: "bg-muted-foreground/30"
}`}
/>
<span className="text-sm">{theme.name}</span>
{themeId === theme.id && (
<span className="ml-auto text-xs text-muted-foreground">
active
</span>
)}
</DropdownMenuItem>
))}
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={logout} className="cursor-crosshair">
Log out
</DropdownMenuItem>
</>
) : (
<DropdownMenuItem onClick={() => setShowLogin(true)}>
Log in
</DropdownMenuItem>
<>
<DropdownMenuGroup>
<DropdownMenuLabel className="text-xs text-muted-foreground font-normal flex items-center gap-1.5">
<Palette className="size-3.5" />
<span>Theme</span>
</DropdownMenuLabel>
{availableThemes.map((theme) => (
<DropdownMenuItem
key={theme.id}
className="cursor-crosshair"
onClick={() => setTheme(theme.id)}
>
<span
className={`size-3 rounded-full mr-2 ${
themeId === theme.id
? "bg-primary"
: "bg-muted-foreground/30"
}`}
/>
<span className="text-sm">{theme.name}</span>
{themeId === theme.id && (
<span className="ml-auto text-xs text-muted-foreground">
active
</span>
)}
</DropdownMenuItem>
))}
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={() => setShowLogin(true)}>
Log in
</DropdownMenuItem>
</>
)}
</DropdownMenuContent>
</DropdownMenu>

View File

@@ -81,12 +81,6 @@ export function applyTheme(theme: Theme): void {
root.style.setProperty("--gradient-2", theme.gradient.color2);
root.style.setProperty("--gradient-3", theme.gradient.color3);
root.style.setProperty("--gradient-4", theme.gradient.color4);
// Layout
root.style.setProperty("--radius", theme.radius);
// Remove the dark class management - we now handle this via CSS variables directly
// The dark class is no longer needed as themes apply their own color values
}
/**
@@ -144,7 +138,5 @@ export function getThemeVariables(): string[] {
"--gradient-2",
"--gradient-3",
"--gradient-4",
// Layout
"--radius",
];
}

View File

@@ -75,6 +75,4 @@ export const darkTheme: Theme = {
color3: "168 85 247", // purple-500
color4: "34 211 238", // cyan-400
},
radius: "0.5rem",
};

View File

@@ -75,6 +75,4 @@ export const lightTheme: Theme = {
color3: "147 51 234", // purple-600
color4: "6 182 212", // cyan-500
},
radius: "0.5rem",
};

View File

@@ -9,7 +9,6 @@ import type { Theme } from "../types";
* - Black text for high contrast
* - Bright yellow selections
* - Dark blue accents
* - No rounded corners (squared aesthetic)
*/
export const plan9Theme: Theme = {
id: "plan9",
@@ -92,7 +91,4 @@ export const plan9Theme: Theme = {
color3: "100 60 180", // Muted purple
color4: "40 160 180", // Teal
},
// Plan9 has no rounded corners - everything is squared
radius: "0",
};

View File

@@ -126,9 +126,6 @@ export interface Theme {
/** Gradient colors */
gradient: ThemeGradient;
/** Border radius base value (e.g., "0.5rem", "0") */
radius: string;
}
/** Theme metadata for listings (without full color data) */