diff --git a/client/src/components/layout/NavHeader.tsx b/client/src/components/layout/NavHeader.tsx
index b782a80..3cd1efd 100644
--- a/client/src/components/layout/NavHeader.tsx
+++ b/client/src/components/layout/NavHeader.tsx
@@ -9,6 +9,7 @@ import {
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
+import { SeasonSelector } from "@/components/ui/season-selector";
export function NavHeader() {
const { theme, setTheme } = useTheme();
@@ -76,6 +77,8 @@ export function NavHeader() {
+
+
diff --git a/client/src/components/ui/season-selector.tsx b/client/src/components/ui/season-selector.tsx
new file mode 100644
index 0000000..2572ece
--- /dev/null
+++ b/client/src/components/ui/season-selector.tsx
@@ -0,0 +1,65 @@
+import { Button } from "@/components/ui/button";
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu";
+import { useSeason } from "@/hooks/use-season";
+import { Leaf, Sun, Umbrella, Snowflake } from "lucide-react";
+
+interface SeasonSelectorProps {
+ className?: string;
+}
+
+export function SeasonSelector({ className }: SeasonSelectorProps) {
+ const { season, setSeason } = useSeason();
+
+ const getIcon = () => {
+ switch (season) {
+ case "spring":
+ return ;
+ case "summer":
+ return ;
+ case "autumn":
+ return ;
+ case "winter":
+ return ;
+ default:
+ return ;
+ }
+ };
+
+ return (
+
+
+
+
+
+ setSeason("spring")}>
+
+ Spring
+
+ setSeason("summer")}>
+
+ Summer
+
+ setSeason("autumn")}>
+
+ Autumn
+
+ setSeason("winter")}>
+
+ Winter
+
+
+
+ );
+}
diff --git a/client/src/hooks/use-season.tsx b/client/src/hooks/use-season.tsx
new file mode 100644
index 0000000..5fb80b2
--- /dev/null
+++ b/client/src/hooks/use-season.tsx
@@ -0,0 +1,44 @@
+import { createContext, useContext, useEffect, useState } from "react";
+
+type Season = "spring" | "summer" | "autumn" | "winter";
+
+interface SeasonContextProps {
+ season: Season;
+ setSeason: (season: Season) => void;
+}
+
+const defaultSeason: Season = "spring";
+
+const SeasonContext = createContext({
+ season: defaultSeason,
+ setSeason: () => {},
+});
+
+export function SeasonProvider({ children }: { children: React.ReactNode }) {
+ const [season, setSeason] = useState(() => {
+ // Try to get the saved season from localStorage
+ if (typeof window !== 'undefined') {
+ const savedSeason = localStorage.getItem("season") as Season;
+ return savedSeason || defaultSeason;
+ }
+ return defaultSeason;
+ });
+
+ useEffect(() => {
+ // Apply the season class to the document element
+ const html = document.documentElement;
+ html.classList.remove("spring", "summer", "autumn", "winter");
+ html.classList.add(season);
+
+ // Save the current season to localStorage
+ localStorage.setItem("season", season);
+ }, [season]);
+
+ return (
+
+ {children}
+
+ );
+}
+
+export const useSeason = () => useContext(SeasonContext);
diff --git a/client/src/index.css b/client/src/index.css
index f13b04d..abca8dd 100644
--- a/client/src/index.css
+++ b/client/src/index.css
@@ -4,84 +4,328 @@
@tailwind components;
@tailwind utilities;
+/**
+ * Base Theme Definition
+ * These variables serve as shared values across all seasonal themes
+ */
:root {
- --background: 180 7% 97%;
- --foreground: 0 0% 20%;
-
- --muted: 180 7% 94%;
- --muted-foreground: 0 0% 45%;
-
- --popover: 180 7% 97%;
- --popover-foreground: 0 0% 20%;
-
- --card: 180 7% 97%;
- --card-foreground: 0 0% 20%;
-
- --border: 174 10% 80%;
- --input: 174 10% 80%;
-
- --primary: 174 40% 24%;
- --primary-foreground: 0 0% 100%;
-
- --secondary: 180 7% 94%;
- --secondary-foreground: 0 0% 20%;
-
- --accent: 174 30% 40%;
- --accent-foreground: 0 0% 100%;
+ --radius: 0.5rem;
+ /* Default destructive/error colors that aren't seasonal */
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 100%;
- --ring: 174 40% 24%;
-
- --radius: 0.5rem;
-
- /* Custom theme colors */
- --cream: 180 7% 97%;
- --navy: 174 40% 24%;
- --russet: 0 60% 45%;
- --sage: 174 30% 35%;
- --ink: 0 0% 20%;
- --paper: 180 7% 94%;
+ /* Content width variables */
+ --content-width: min(90%, 1200px);
+ --reading-width: min(90%, 700px);
}
-.dark {
- --background: 174 20% 12%;
- --foreground: 0 0% 90%;
+/**
+ * Spring Theme 🌱
+ * Fresh, vibrant green palette inspired by new growth
+ */
+.spring {
+ /* Main colors */
+ --background: 120 30% 96%;
+ --foreground: 120 15% 15%;
- --muted: 174 20% 18%;
- --muted-foreground: 0 0% 70%;
+ --muted: 120 20% 92%;
+ --muted-foreground: 120 10% 40%;
- --popover: 174 20% 14%;
- --popover-foreground: 0 0% 90%;
+ --popover: 120 30% 96%;
+ --popover-foreground: 120 15% 15%;
- --card: 174 20% 14%;
- --card-foreground: 0 0% 90%;
+ --card: 120 30% 97%;
+ --card-foreground: 120 15% 15%;
- --border: 174 15% 25%;
- --input: 174 15% 25%;
+ --border: 120 30% 85%;
+ --input: 120 30% 85%;
- --primary: 174 35% 45%;
- --primary-foreground: 0 0% 100%;
+ --primary: 142 50% 30%;
+ --primary-foreground: 120 54% 95%;
- --secondary: 174 20% 20%;
- --secondary-foreground: 0 0% 90%;
+ --secondary: 120 20% 92%;
+ --secondary-foreground: 120 15% 15%;
- --accent: 174 40% 45%;
- --accent-foreground: 0 0% 100%;
+ --accent: 142 76% 45%;
+ --accent-foreground: 120 54% 98%;
- --destructive: 0 62.8% 30.6%;
- --destructive-foreground: 0 0% 100%;
+ --ring: 142 50% 30%;
- --ring: 174 40% 45%;
+ /* Legacy semantic colors for backward compatibility */
+ --cream: 120 30% 96%;
+ --navy: 142 50% 30%;
+ --russet: 22 80% 50%;
+ --sage: 142 40% 55%;
+ --ink: 120 15% 15%;
+ --paper: 120 20% 92%;
+}
+
+.spring.dark {
+ --background: 143 30% 12%;
+ --foreground: 120 15% 90%;
- /* Custom theme colors */
- --cream: 0 0% 90%;
- --navy: 174 35% 45%;
+ --muted: 143 25% 18%;
+ --muted-foreground: 120 10% 70%;
+
+ --popover: 143 30% 14%;
+ --popover-foreground: 120 15% 90%;
+
+ --card: 143 30% 14%;
+ --card-foreground: 120 15% 90%;
+
+ --border: 143 25% 25%;
+ --input: 143 25% 25%;
+
+ --primary: 142 75% 45%;
+ --primary-foreground: 120 54% 98%;
+
+ --secondary: 143 25% 20%;
+ --secondary-foreground: 120 15% 90%;
+
+ --accent: 142 75% 45%;
+ --accent-foreground: 120 54% 98%;
+
+ --ring: 142 75% 45%;
+
+ /* Legacy semantic colors for backward compatibility */
+ --cream: 120 15% 90%;
+ --navy: 142 75% 45%;
+ --russet: 22 70% 55%;
+ --sage: 142 60% 45%;
+ --ink: 120 15% 90%;
+ --paper: 143 25% 18%;
+}
+
+/**
+ * Summer Theme ☀️
+ * Warm, sunny palette with amber and gold tones
+ */
+.summer {
+ /* Main colors */
+ --background: 48 30% 96%;
+ --foreground: 30 15% 15%;
+
+ --muted: 48 20% 92%;
+ --muted-foreground: 30 10% 40%;
+
+ --popover: 48 30% 96%;
+ --popover-foreground: 30 15% 15%;
+
+ --card: 48 30% 97%;
+ --card-foreground: 30 15% 15%;
+
+ --border: 48 30% 85%;
+ --input: 48 30% 85%;
+
+ --primary: 36 80% 40%;
+ --primary-foreground: 48 54% 98%;
+
+ --secondary: 48 20% 92%;
+ --secondary-foreground: 30 15% 15%;
+
+ --accent: 36 80% 50%;
+ --accent-foreground: 48 54% 98%;
+
+ --ring: 36 80% 40%;
+
+ /* Legacy semantic colors */
+ --cream: 48 30% 96%;
+ --navy: 36 80% 40%;
+ --russet: 16 80% 55%;
+ --sage: 54 60% 45%;
+ --ink: 30 15% 15%;
+ --paper: 48 20% 92%;
+}
+
+.summer.dark {
+ --background: 36 30% 12%;
+ --foreground: 48 15% 90%;
+
+ --muted: 36 25% 18%;
+ --muted-foreground: 30 10% 70%;
+
+ --popover: 36 30% 14%;
+ --popover-foreground: 48 15% 90%;
+
+ --card: 36 30% 14%;
+ --card-foreground: 48 15% 90%;
+
+ --border: 36 25% 25%;
+ --input: 36 25% 25%;
+
+ --primary: 36 70% 55%;
+ --primary-foreground: 48 54% 98%;
+
+ --secondary: 36 25% 20%;
+ --secondary-foreground: 48 15% 90%;
+
+ --accent: 36 70% 55%;
+ --accent-foreground: 48 54% 98%;
+
+ --ring: 36 70% 55%;
+
+ /* Legacy semantic colors */
+ --cream: 48 15% 90%;
+ --navy: 36 70% 55%;
+ --russet: 16 70% 60%;
+ --sage: 54 50% 50%;
+ --ink: 48 15% 90%;
+ --paper: 36 25% 18%;
+}
+
+/**
+ * Autumn Theme 🍂
+ * Rich, earthy palette with warm reds and oranges
+ */
+.autumn {
+ /* Main colors */
+ --background: 24 30% 96%;
+ --foreground: 24 15% 15%;
+
+ --muted: 24 20% 92%;
+ --muted-foreground: 18 10% 40%;
+
+ --popover: 24 30% 96%;
+ --popover-foreground: 24 15% 15%;
+
+ --card: 24 30% 97%;
+ --card-foreground: 24 15% 15%;
+
+ --border: 24 30% 85%;
+ --input: 24 30% 85%;
+
+ --primary: 18 75% 35%;
+ --primary-foreground: 24 54% 98%;
+
+ --secondary: 24 20% 92%;
+ --secondary-foreground: 24 15% 15%;
+
+ --accent: 18 75% 45%;
+ --accent-foreground: 24 54% 98%;
+
+ --ring: 18 75% 35%;
+
+ /* Legacy semantic colors */
+ --cream: 24 30% 96%;
+ --navy: 18 75% 35%;
+ --russet: 12 80% 45%;
+ --sage: 36 60% 45%;
+ --ink: 24 15% 15%;
+ --paper: 24 20% 92%;
+}
+
+.autumn.dark {
+ --background: 18 30% 12%;
+ --foreground: 24 15% 90%;
+
+ --muted: 18 25% 18%;
+ --muted-foreground: 18 10% 70%;
+
+ --popover: 18 30% 14%;
+ --popover-foreground: 24 15% 90%;
+
+ --card: 18 30% 14%;
+ --card-foreground: 24 15% 90%;
+
+ --border: 18 25% 25%;
+ --input: 18 25% 25%;
+
+ --primary: 18 65% 50%;
+ --primary-foreground: 24 54% 98%;
+
+ --secondary: 18 25% 20%;
+ --secondary-foreground: 24 15% 90%;
+
+ --accent: 18 65% 50%;
+ --accent-foreground: 24 54% 98%;
+
+ --ring: 18 65% 50%;
+
+ /* Legacy semantic colors */
+ --cream: 24 15% 90%;
+ --navy: 18 65% 50%;
+ --russet: 12 70% 50%;
+ --sage: 36 50% 50%;
+ --ink: 24 15% 90%;
+ --paper: 18 25% 18%;
+}
+
+/**
+ * Winter Theme ❄️
+ * Cool, crisp palette with blues and silvers
+ */
+.winter {
+ /* Main colors */
+ --background: 210 30% 97%;
+ --foreground: 210 15% 15%;
+
+ --muted: 210 20% 93%;
+ --muted-foreground: 210 10% 40%;
+
+ --popover: 210 30% 97%;
+ --popover-foreground: 210 15% 15%;
+
+ --card: 210 30% 98%;
+ --card-foreground: 210 15% 15%;
+
+ --border: 210 30% 85%;
+ --input: 210 30% 85%;
+
+ --primary: 220 70% 35%;
+ --primary-foreground: 210 54% 98%;
+
+ --secondary: 210 20% 93%;
+ --secondary-foreground: 210 15% 15%;
+
+ --accent: 220 70% 45%;
+ --accent-foreground: 210 54% 98%;
+
+ --ring: 220 70% 35%;
+
+ /* Legacy semantic colors */
+ --cream: 210 30% 97%;
+ --navy: 220 70% 35%;
--russet: 0 60% 50%;
- --sage: 174 30% 40%;
- --ink: 0 0% 90%;
- --paper: 174 20% 16%;
+ --sage: 200 60% 45%;
+ --ink: 210 15% 15%;
+ --paper: 210 20% 93%;
+}
+
+.winter.dark {
+ --background: 220 30% 12%;
+ --foreground: 210 15% 90%;
+
+ --muted: 220 25% 18%;
+ --muted-foreground: 210 10% 70%;
+
+ --popover: 220 30% 14%;
+ --popover-foreground: 210 15% 90%;
+
+ --card: 220 30% 14%;
+ --card-foreground: 210 15% 90%;
+
+ --border: 220 25% 25%;
+ --input: 220 25% 25%;
+
+ --primary: 220 60% 55%;
+ --primary-foreground: 210 54% 98%;
+
+ --secondary: 220 25% 20%;
+ --secondary-foreground: 210 15% 90%;
+
+ --accent: 220 60% 55%;
+ --accent-foreground: 210 54% 98%;
+
+ --ring: 220 60% 55%;
+
+ /* Legacy semantic colors */
+ --cream: 210 15% 90%;
+ --navy: 220 60% 55%;
+ --russet: 0 55% 55%;
+ --sage: 200 45% 50%;
+ --ink: 210 15% 90%;
+ --paper: 220 25% 18%;
}
/* Content width variables */
@@ -97,23 +341,24 @@
html {
scroll-behavior: smooth;
+ @apply spring; /* Default to spring theme */
}
body {
- @apply bg-cream text-ink font-serif antialiased;
+ @apply bg-background text-foreground font-serif antialiased;
}
h1, h2, h3, h4, h5, h6 {
- @apply font-serif text-navy dark:text-cream;
+ @apply font-serif text-primary dark:text-primary-foreground;
}
p {
- @apply text-ink dark:text-cream/90;
+ @apply text-foreground dark:text-foreground/90;
}
/* Line numbers for reading view */
.line-number {
- @apply inline-block min-w-8 mr-2 text-right text-sage/70 dark:text-sage/60 select-none;
+ @apply inline-block min-w-8 mr-2 text-right text-muted-foreground/70 dark:text-muted-foreground/60 select-none;
}
/* Text lines in reading view */
@@ -122,7 +367,7 @@
}
.text-line:hover {
- @apply bg-navy/5 dark:bg-cream/5;
+ @apply bg-primary/5 dark:bg-primary-foreground/5;
}
/* Custom scrollbar */
@@ -131,15 +376,15 @@
}
::-webkit-scrollbar-track {
- @apply bg-paper/50 dark:bg-[#1e1e1e]/50;
+ @apply bg-muted/50 dark:bg-muted/50;
}
::-webkit-scrollbar-thumb {
- @apply bg-sage rounded-full;
+ @apply bg-accent/80 rounded-full;
}
.dark ::-webkit-scrollbar-thumb {
- @apply bg-sage/70;
+ @apply bg-accent/70;
}
/* Font size utilities for reading view */
diff --git a/client/src/main.tsx b/client/src/main.tsx
index fb3bfaf..143f7da 100644
--- a/client/src/main.tsx
+++ b/client/src/main.tsx
@@ -2,9 +2,12 @@ import { createRoot } from "react-dom/client";
import App from "./App";
import "./index.css";
import { ThemeProvider } from "@/components/ui/theme-provider";
+import { SeasonProvider } from "@/hooks/use-season";
createRoot(document.getElementById("root")!).render(
-
+
+
+
);