feat: add warm sand theme colors and light / dark mode toggle (#302)

This commit was merged in pull request #302.
This commit is contained in:
Arjun Patel
2026-06-21 10:56:59 -07:00
committed by GitHub
parent 3d80ac2993
commit d14cec5885
43 changed files with 540 additions and 347 deletions
+57 -1
View File
@@ -12,11 +12,16 @@ import {
Volume2,
ArrowLeft,
Camera,
Sun,
Moon,
Monitor,
Palette,
} from 'lucide-react';
import { HumanAvatar } from '@/components/human-avatar';
import { AvatarEditDialog } from '@/features/settings/avatar-edit-dialog';
import { Separator } from '@/components/ui/separator';
import { Switch } from '@/components/ui/switch';
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
import { WindowControls } from '@/components/window-controls';
import { Button } from '@/components/ui/button';
import { Muted } from '@/components/ui/typography';
@@ -24,6 +29,7 @@ import { ScrollArea } from '@/components/ui/scroll-area';
import { CopyableEmail } from '@/components/copyable-email';
import { useAuthStore } from '@/stores/auth-store';
import { useSoundEffectsStore } from '@/stores/sound-effects-store';
import { useThemeStore, type ThemeMode } from '@/stores/theme-store';
import { apiClient } from '@/api/client';
import { logError, toUserMessage } from '@/lib/errors';
import { toast } from 'sonner';
@@ -80,6 +86,50 @@ function SettingsGroup({
);
}
const THEME_OPTIONS: {
value: ThemeMode;
label: string;
icon: React.ReactNode;
}[] = [
{ value: 'system', label: 'System', icon: <Monitor className="size-3.5" /> },
{ value: 'light', label: 'Light', icon: <Sun className="size-3.5" /> },
{ value: 'dark', label: 'Dark', icon: <Moon className="size-3.5" /> },
];
function ThemeSetting() {
const mode = useThemeStore((s) => s.mode);
const setMode = useThemeStore((s) => s.setMode);
return (
<div className="flex w-full items-center gap-3 px-4 py-3">
<span className="text-muted-foreground flex size-5 items-center justify-center">
<Palette className="size-4" />
</span>
<span className="min-w-0 flex-1 text-sm font-medium">Theme</span>
<ToggleGroup
type="single"
size="sm"
value={mode}
onValueChange={(value) => value && setMode(value as ThemeMode)}
className="border-border bg-muted/50 border p-0.5"
spacing={2}
>
{THEME_OPTIONS.map((option) => (
<ToggleGroupItem
key={option.value}
value={option.value}
aria-label={option.label}
className="data-[state=on]:bg-background data-[state=on]:text-foreground data-[state=on]:shadow-sm gap-1.5 rounded-md px-2.5 text-xs"
>
{option.icon}
{option.label}
</ToggleGroupItem>
))}
</ToggleGroup>
</div>
);
}
export default function SettingsPage() {
const navigate = useNavigate();
const user = useAuthStore((s) => s.user);
@@ -150,7 +200,7 @@ export default function SettingsPage() {
initials={initials}
fallbackClassName="bg-primary/10 text-primary text-xl font-medium"
/>
<span className="absolute inset-0 flex items-center justify-center rounded-full bg-black/40 opacity-0 transition-opacity group-hover:opacity-100" />
<span className="bg-scrim/40 absolute inset-0 flex items-center justify-center rounded-full opacity-0 transition-opacity group-hover:opacity-100" />
<span className="bg-primary text-primary-foreground ring-background absolute bottom-0 right-0 flex size-5 items-center justify-center rounded-full ring-2">
<Camera className="size-2.5" />
</span>
@@ -165,6 +215,12 @@ export default function SettingsPage() {
<Separator />
<SettingsGroup title="Appearance">
<ThemeSetting />
</SettingsGroup>
<Separator className="mt-4" />
<SettingsGroup title="Notifications">
<div className="flex w-full items-center gap-3 px-4 py-3">
<span className="text-muted-foreground flex size-5 items-center justify-center">