diff --git a/app/globals.css b/app/globals.css index db74de8..f74310e 100644 --- a/app/globals.css +++ b/app/globals.css @@ -5,7 +5,8 @@ @tailwind utilities; @layer base { - :root { + /* Default Dark Theme */ + :root, .theme-dark { --background: #0a0a0f; --surface: #1a1a1f; --primary: #8b5cf6; @@ -14,6 +15,64 @@ --secondary: #ec4899; --accent: #06b6d4; --border: #2a2a2f; + --foreground: #ffffff; + --muted: #6b7280; + } + + /* Light Theme */ + .theme-light { + --background: #ffffff; + --surface: #f9fafb; + --primary: #7c3aed; + --primary-light: #8b5cf6; + --primary-dark: #6d28d9; + --secondary: #db2777; + --accent: #0891b2; + --border: #e5e7eb; + --foreground: #111827; + --muted: #6b7280; + } + + /* Synthwave Theme */ + .theme-synthwave { + --background: #2b213a; + --surface: #241b2f; + --primary: #ff6ac1; + --primary-light: #ff8ad8; + --primary-dark: #ff4aaa; + --secondary: #9d72ff; + --accent: #72f1b8; + --border: #495495; + --foreground: #f8f8f2; + --muted: #a599e9; + } + + /* Forest Theme */ + .theme-forest { + --background: #0d1b1e; + --surface: #1a2f33; + --primary: #2dd4bf; + --primary-light: #5eead4; + --primary-dark: #14b8a6; + --secondary: #34d399; + --accent: #a7f3d0; + --border: #234e52; + --foreground: #ecfdf5; + --muted: #6ee7b7; + } + + /* Ocean Theme */ + .theme-ocean { + --background: #0c1821; + --surface: #1b2838; + --primary: #3b82f6; + --primary-light: #60a5fa; + --primary-dark: #2563eb; + --secondary: #06b6d4; + --accent: #38bdf8; + --border: #1e3a5f; + --foreground: #dbeafe; + --muted: #7dd3fc; } * { @@ -22,7 +81,7 @@ body { background-color: var(--background); - color: white; + color: var(--foreground); font-family: var(--font-inter), 'Inter', sans-serif; } diff --git a/src/components/ThemeSwitcher.tsx b/src/components/ThemeSwitcher.tsx new file mode 100644 index 0000000..f3f13b9 --- /dev/null +++ b/src/components/ThemeSwitcher.tsx @@ -0,0 +1,52 @@ +import { Palette } from '@phosphor-icons/react'; +import { Button } from '@/components/ui/button'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu'; +import { useTheme, Theme } from '@/hooks/use-theme'; +import { Check } from '@phosphor-icons/react'; + +export function ThemeSwitcher() { + const { theme, setTheme, themes } = useTheme(); + + return ( + + + + + + Choose Theme + + {Object.entries(themes).map(([key, config]) => ( + setTheme(key as Theme)} + className="flex items-start gap-2 cursor-pointer" + > +
+
+ {config.label} + {theme === key && } +
+

+ {config.description} +

+
+
+ ))} +
+
+ ); +} diff --git a/src/components/Toolbar.tsx b/src/components/Toolbar.tsx index ffe776c..adc73b8 100644 --- a/src/components/Toolbar.tsx +++ b/src/components/Toolbar.tsx @@ -12,6 +12,7 @@ import { Copy, FileCode, Download, Info, Play, FolderPlus, User, SignOut, List } import { toast } from 'sonner'; import { useState, useEffect } from 'react'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog'; +import { ThemeSwitcher } from './ThemeSwitcher'; interface ToolbarProps { code: string; @@ -138,6 +139,8 @@ export function Toolbar({ code, onTemplatesClick, onPreviewClick, onNewProjectCl About + + {/* Mobile: Hamburger menu with essential actions */} diff --git a/src/hooks/use-theme.ts b/src/hooks/use-theme.ts new file mode 100644 index 0000000..31bd51c --- /dev/null +++ b/src/hooks/use-theme.ts @@ -0,0 +1,68 @@ +import { useEffect, useState } from 'react'; + +export type Theme = 'dark' | 'light' | 'synthwave' | 'forest' | 'ocean'; + +interface ThemeConfig { + name: string; + label: string; + description: string; +} + +export const themes: Record = { + dark: { + name: 'dark', + label: 'Dark', + description: 'Classic dark theme for comfortable coding', + }, + light: { + name: 'light', + label: 'Light', + description: 'Clean light theme for bright environments', + }, + synthwave: { + name: 'synthwave', + label: 'Synthwave', + description: 'Retro neon aesthetic with vibrant colors', + }, + forest: { + name: 'forest', + label: 'Forest', + description: 'Calming green tones inspired by nature', + }, + ocean: { + name: 'ocean', + label: 'Ocean', + description: 'Deep blue theme for focused work', + }, +}; + +export function useTheme() { + const [theme, setThemeState] = useState(() => { + if (typeof window === 'undefined') return 'dark'; + const stored = localStorage.getItem('aethex-theme'); + return (stored as Theme) || 'dark'; + }); + + useEffect(() => { + if (typeof window === 'undefined') return; + + const root = document.documentElement; + + // Remove all theme classes + Object.keys(themes).forEach((t) => { + root.classList.remove(`theme-${t}`); + }); + + // Add current theme class + root.classList.add(`theme-${theme}`); + + // Save to localStorage + localStorage.setItem('aethex-theme', theme); + }, [theme]); + + const setTheme = (newTheme: Theme) => { + setThemeState(newTheme); + }; + + return { theme, setTheme, themes }; +}