diff --git a/backend/api/migrations/0008_alter_user_theme.py b/backend/api/migrations/0008_alter_user_theme.py
new file mode 100644
index 0000000..65155ec
--- /dev/null
+++ b/backend/api/migrations/0008_alter_user_theme.py
@@ -0,0 +1,18 @@
+# Generated by Django 6.0.2 on 2026-02-16 13:11
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('api', '0007_flavor_image_flavor_is_legacy_alter_flavor_image_url'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='user',
+ name='theme',
+ field=models.CharField(choices=[('latte', 'Latte'), ('frappe', 'Frappé'), ('macchiato', 'Macchiato'), ('mocha', 'Mocha'), ('pink', 'Pastel Pink'), ('atom', 'Atom One Dark'), ('dracula', 'Dracula'), ('gruvbox', 'Gruvbox Dark'), ('nord', 'Nord'), ('cyberpunk', 'Cyberpunk'), ('forest', 'Forest Dark')], default='mocha', max_length=20),
+ ),
+ ]
diff --git a/backend/api/migrations/0009_alter_user_theme.py b/backend/api/migrations/0009_alter_user_theme.py
new file mode 100644
index 0000000..41ab65b
--- /dev/null
+++ b/backend/api/migrations/0009_alter_user_theme.py
@@ -0,0 +1,18 @@
+# Generated by Django 6.0.2 on 2026-02-16 13:12
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('api', '0008_alter_user_theme'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='user',
+ name='theme',
+ field=models.CharField(choices=[('latte', 'Latte'), ('pink', 'Pastel Pink'), ('solarized_light', 'Solarized Light'), ('one_light', 'One Light'), ('paper', 'Paper White'), ('frappe', 'Frappé'), ('macchiato', 'Macchiato'), ('mocha', 'Mocha'), ('atom', 'Atom One Dark'), ('dracula', 'Dracula'), ('gruvbox', 'Gruvbox Dark'), ('nord', 'Nord'), ('cyberpunk', 'Cyberpunk'), ('forest', 'Forest Dark')], default='mocha', max_length=20),
+ ),
+ ]
diff --git a/backend/api/models.py b/backend/api/models.py
index 34b9b82..2a9a096 100644
--- a/backend/api/models.py
+++ b/backend/api/models.py
@@ -5,10 +5,19 @@ from django.core.validators import MinValueValidator, MaxValueValidator
class User(AbstractUser):
THEME_CHOICES = [
('latte', 'Latte'),
+ ('pink', 'Pastel Pink'),
+ ('solarized_light', 'Solarized Light'),
+ ('one_light', 'One Light'),
+ ('paper', 'Paper White'),
('frappe', 'Frappé'),
('macchiato', 'Macchiato'),
('mocha', 'Mocha'),
- ('pink', 'Pastel Pink'),
+ ('atom', 'Atom One Dark'),
+ ('dracula', 'Dracula'),
+ ('gruvbox', 'Gruvbox Dark'),
+ ('nord', 'Nord'),
+ ('cyberpunk', 'Cyberpunk'),
+ ('forest', 'Forest Dark'),
]
theme = models.CharField(max_length=20, choices=THEME_CHOICES, default='mocha')
pending_email = models.EmailField(max_length=254, blank=True, null=True)
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 0bfa83f..27af35e 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -21,7 +21,8 @@ import {
useMediaQuery,
TextField,
InputAdornment,
- Autocomplete
+ Autocomplete,
+ ListSubheader
} from '@mui/material';
import PaletteIcon from '@mui/icons-material/Palette';
import MenuIcon from '@mui/icons-material/Menu';
@@ -259,12 +260,27 @@ const App: React.FC = () => {
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={() => setAnchorEl(null)}
+ sx={{ maxHeight: 500 }}
>
+ Light Themes
+
+
+
+
+
+
+
+ Dark Themes
+
-
-
+
+
+
+
+
+
diff --git a/frontend/src/theme.ts b/frontend/src/theme.ts
index 7673709..55765ab 100644
--- a/frontend/src/theme.ts
+++ b/frontend/src/theme.ts
@@ -1,27 +1,122 @@
import { createTheme, type ThemeOptions } from '@mui/material/styles';
import { flavors } from '@catppuccin/palette';
-export type CatppuccinTheme = 'latte' | 'frappe' | 'macchiato' | 'mocha' | 'pink';
+export type CatppuccinTheme =
+ | 'latte' | 'pink' | 'solarized_light' | 'one_light' | 'paper'
+ | 'frappe' | 'macchiato' | 'mocha' | 'atom' | 'dracula' | 'gruvbox' | 'nord' | 'cyberpunk' | 'forest';
+
+export const isLightTheme = (mode: CatppuccinTheme): boolean => {
+ return ['latte', 'pink', 'solarized_light', 'one_light', 'paper'].includes(mode);
+};
export const getTheme = (mode: CatppuccinTheme) => {
- let palette;
+ let palette: any;
if (mode === 'pink') {
palette = {
- base: { hex: '#fdf6f7' }, // Soft cream pink
- surface0: { hex: '#f8e1e5' }, // Pastel pink
- crust: { hex: '#f2ccd5' }, // Deeper pink for headers
- text: { hex: '#4a3135' }, // Deep maroon-brown for high readability
+ base: { hex: '#fdf6f7' },
+ surface0: { hex: '#f8e1e5' },
+ crust: { hex: '#f2ccd5' },
+ text: { hex: '#4a3135' },
subtext0: { hex: '#7d5c62' },
- mauve: { hex: '#e05a8d' }, // Rich pink (Primary)
- pink: { hex: '#4ecdc4' }, // Fresh Turquoise (Secondary/Accent)
+ mauve: { hex: '#e05a8d' },
+ pink: { hex: '#4ecdc4' },
+ };
+ } else if (mode === 'solarized_light') {
+ palette = {
+ base: { hex: '#fdf6e3' },
+ surface0: { hex: '#eee8d5' },
+ crust: { hex: '#e3dbbc' },
+ text: { hex: '#586e75' },
+ subtext0: { hex: '#839496' },
+ mauve: { hex: '#6c71c4' },
+ pink: { hex: '#d33682' },
+ };
+ } else if (mode === 'one_light') {
+ palette = {
+ base: { hex: '#fafafa' },
+ surface0: { hex: '#f0f0f0' },
+ crust: { hex: '#e5e5e6' },
+ text: { hex: '#383a42' },
+ subtext0: { hex: '#a0a1a7' },
+ mauve: { hex: '#a626a4' },
+ pink: { hex: '#e45649' },
+ };
+ } else if (mode === 'paper') {
+ palette = {
+ base: { hex: '#ffffff' },
+ surface0: { hex: '#f5f5f5' },
+ crust: { hex: '#eeeeee' },
+ text: { hex: '#222222' },
+ subtext0: { hex: '#666666' },
+ mauve: { hex: '#007aff' },
+ pink: { hex: '#ff2d55' },
+ };
+ } else if (mode === 'atom') {
+ palette = {
+ base: { hex: '#282c34' },
+ surface0: { hex: '#353b45' },
+ crust: { hex: '#21252b' },
+ text: { hex: '#abb2bf' },
+ subtext0: { hex: '#5c6370' },
+ mauve: { hex: '#c678dd' },
+ pink: { hex: '#e06c75' },
+ };
+ } else if (mode === 'dracula') {
+ palette = {
+ base: { hex: '#282a36' },
+ surface0: { hex: '#44475a' },
+ crust: { hex: '#191a21' },
+ text: { hex: '#f8f8f2' },
+ subtext0: { hex: '#6272a4' },
+ mauve: { hex: '#bd93f9' },
+ pink: { hex: '#ff79c6' },
+ };
+ } else if (mode === 'gruvbox') {
+ palette = {
+ base: { hex: '#282828' },
+ surface0: { hex: '#3c3836' },
+ crust: { hex: '#1d2021' },
+ text: { hex: '#ebdbb2' },
+ subtext0: { hex: '#928374' },
+ mauve: { hex: '#d3869b' },
+ pink: { hex: '#fb4934' },
+ };
+ } else if (mode === 'nord') {
+ palette = {
+ base: { hex: '#2e3440' },
+ surface0: { hex: '#3b4252' },
+ crust: { hex: '#242933' },
+ text: { hex: '#eceff4' },
+ subtext0: { hex: '#d8dee9' },
+ mauve: { hex: '#b48ead' },
+ pink: { hex: '#88c0d0' },
+ };
+ } else if (mode === 'cyberpunk') {
+ palette = {
+ base: { hex: '#1a1a2e' },
+ surface0: { hex: '#16213e' },
+ crust: { hex: '#0f3460' },
+ text: { hex: '#e94560' },
+ subtext0: { hex: '#0f3460' },
+ mauve: { hex: '#e94560' },
+ pink: { hex: '#00d2ff' },
+ };
+ } else if (mode === 'forest') {
+ palette = {
+ base: { hex: '#1b262c' },
+ surface0: { hex: '#213e3b' },
+ crust: { hex: '#0f161c' },
+ text: { hex: '#e8e8e8' },
+ subtext0: { hex: '#aebb83' },
+ mauve: { hex: '#4e8d7c' },
+ pink: { hex: '#aebb83' },
};
} else {
- const flavor = flavors[mode];
+ const flavor = (flavors as any)[mode];
if (flavor) {
palette = flavor.colors;
} else {
- // Fallback to mocha if theme name is invalid
palette = flavors['mocha'].colors;
mode = 'mocha';
}
@@ -29,7 +124,7 @@ export const getTheme = (mode: CatppuccinTheme) => {
const themeOptions: ThemeOptions = {
palette: {
- mode: (mode === 'latte' || mode === 'pink') ? 'light' : 'dark',
+ mode: isLightTheme(mode) ? 'light' : 'dark',
primary: {
main: palette.mauve.hex,
},