diff --git a/src/components/layout/Header.jsx b/src/components/layout/Header.jsx
index e106937..a6bd657 100644
--- a/src/components/layout/Header.jsx
+++ b/src/components/layout/Header.jsx
@@ -6,6 +6,7 @@ import { doSignOut } from '/src/firebase/auth'
import { useState } from 'react'
import styles from '../../styles/layout.module.css'
import logo from '../../assets/logo.svg'
+import { DarkModeButton } from '../ui/ThemeToggle.jsx'
export default function Header() {
const [isSignInOpen, setIsSignInOpen] = useState(false)
@@ -30,6 +31,7 @@ export default function Header() {
+
{!userLoggedIn ? (
<>
diff --git a/src/components/ui/Button.jsx b/src/components/ui/Button.jsx
index e908902..2925160 100644
--- a/src/components/ui/Button.jsx
+++ b/src/components/ui/Button.jsx
@@ -12,5 +12,11 @@ function SignInButton({ onClick }) {
)
}
+
+ function DarkModeButton({ onClick }) {
+ return (
+
+ )
+ }
- export { SignInButton, SignUpButton }
\ No newline at end of file
+ export { SignInButton, SignUpButton, DarkModeButton }
\ No newline at end of file
diff --git a/src/components/ui/ThemeToggle.jsx b/src/components/ui/ThemeToggle.jsx
new file mode 100644
index 0000000..35cf150
--- /dev/null
+++ b/src/components/ui/ThemeToggle.jsx
@@ -0,0 +1,42 @@
+import { useTheme } from '../../context/ThemeContext';
+
+export function DarkModeButton() {
+ const { isDarkMode, toggleDarkMode } = useTheme();
+
+ return (
+
+
+ {isDarkMode ? '🌙' : '☀️'}
+
+
+ );
+}
diff --git a/src/context/ThemeContext.jsx b/src/context/ThemeContext.jsx
index b236fa8..382ab4e 100644
--- a/src/context/ThemeContext.jsx
+++ b/src/context/ThemeContext.jsx
@@ -1,2 +1,22 @@
-// ThemeContext - Manages the light/dark mode theme
-// TODO: Implement theme context provider
\ No newline at end of file
+import { createContext, useContext } from 'react';
+import { useDarkMode } from '../hooks/useDarkMode';
+
+const ThemeContext = createContext();
+
+export function ThemeProvider({ children }) {
+ const { isDarkMode, toggleDarkMode } = useDarkMode();
+
+ return (
+
+ {children}
+
+ );
+}
+
+export function useTheme() {
+ const context = useContext(ThemeContext);
+ if (context === undefined) {
+ throw new Error('useTheme must be used within a ThemeProvider');
+ }
+ return context;
+}
\ No newline at end of file
diff --git a/src/hooks/useDarkMode.js b/src/hooks/useDarkMode.js
index 2f992e2..c106f36 100644
--- a/src/hooks/useDarkMode.js
+++ b/src/hooks/useDarkMode.js
@@ -1,2 +1,46 @@
-// useDarkMode hook - Manages dark mode state and local storage
-// TODO: Implement dark mode hook with theme switching
\ No newline at end of file
+import { useState, useEffect } from 'react';
+
+export function useDarkMode() {
+ // Initialize state from localStorage or default to system preference
+ const [isDarkMode, setIsDarkMode] = useState(() => {
+ const saved = localStorage.getItem('darkMode');
+ if (saved !== null) {
+ return JSON.parse(saved);
+ }
+ // Default to system preference if no saved preference
+ return window.matchMedia('(prefers-color-scheme: dark)').matches;
+ });
+
+ // Update localStorage and document class when theme changes
+ useEffect(() => {
+ localStorage.setItem('darkMode', JSON.stringify(isDarkMode));
+
+ // Add or remove 'dark' class to document for CSS targeting
+ if (isDarkMode) {
+ document.documentElement.classList.add('dark');
+ } else {
+ document.documentElement.classList.remove('dark');
+ }
+ }, [isDarkMode]);
+
+ // Listen for system theme changes
+ useEffect(() => {
+ const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
+
+ const handleChange = (e) => {
+ // Only update if user hasn't set a preference
+ if (localStorage.getItem('darkMode') === null) {
+ setIsDarkMode(e.matches);
+ }
+ };
+
+ mediaQuery.addEventListener('change', handleChange);
+ return () => mediaQuery.removeEventListener('change', handleChange);
+ }, []);
+
+ const toggleDarkMode = () => {
+ setIsDarkMode(prev => !prev);
+ };
+
+ return { isDarkMode, toggleDarkMode };
+}
diff --git a/src/index.css b/src/index.css
index 5c137dc..c9e207a 100644
--- a/src/index.css
+++ b/src/index.css
@@ -1,9 +1,26 @@
@import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100..900;1,100..900&display=swap');
+
:root {
+ --text: #1a1a1a;
+ --background: #ffffff;
+ --buttonBackground: #f9f9f9;
+ --buttonBorder: #202020;
+ --inputBackground: #f9f9f9;
+ --inputBorder: #202020;
+ --googleButtonBackground: #f9f9f9;
+ --cardBackground: white;
+ --cardSpanBackground: white;
+ --spanTextColor: #6b7280;
+ --dividerColor: #e5e7eb;
+ font-family: 'Roboto', sans-serif;
+}
+
+/* Dark mode styles */
+.dark {
--text: #f9f9f9;
--background: #202020;
--buttonBackground: #1a1a1a;
- --buttonBorder: #d1d5db;;
+ --buttonBorder: #d1d5db;
--inputBackground: #1a1a1a;
--inputBorder: #202020;
--googleButtonBackground: #1a1a1a;
@@ -11,11 +28,12 @@
--cardSpanBackground: #1a1a1a;
--spanTextColor: #f9f9f9;
--dividerColor: #2a2b2d;
- font-family: 'Roboto', sans-serif;
}
-body{
+
+body {
color: var(--text);
background-color: var(--background);
+ transition: color 0.3s ease, background-color 0.3s ease;
}
button {
@@ -27,21 +45,11 @@ button {
font-weight: 500;
font-family: inherit;
background-color: var(--buttonBackground);
- border-color: var(--borderColor);
+ border-color: var(--buttonBorder);
cursor: pointer;
+ transition: all 0.3s ease;
}
-@media (prefers-color-scheme: light) {
- :root {
- --text: #1a1a1a;
- --buttonBorder: #202020;
- --background: #ffffff;
- --buttonBackground: #f9f9f9;
- --inputBackground: #f9f9f9;
- --cardSpanBackground: white;
- --cardBackground: white;
- --googleButtonBackground: #f9f9f9;
- --spanTextColor: #6b7280;
- --dividerColor: #e5e7eb;
- }
+button:hover {
+ opacity: 0.8;
}
diff --git a/src/main.jsx b/src/main.jsx
index ec394e1..6cdbf40 100644
--- a/src/main.jsx
+++ b/src/main.jsx
@@ -3,11 +3,14 @@ import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.jsx'
import { AuthProvider } from './context/AuthContext'
+import { ThemeProvider } from './context/ThemeContext'
createRoot(document.getElementById('root')).render(
-
-
-
+
+
+
+
+
,
)
diff --git a/src/styles/layout.module.css b/src/styles/layout.module.css
index 539a4a6..b8377bd 100644
--- a/src/styles/layout.module.css
+++ b/src/styles/layout.module.css
@@ -31,8 +31,7 @@
flex-shrink: 0;
}
-@media (prefers-color-scheme: dark) {
- .logo {
- filter: brightness(0) invert(1);
- }
+/* Dark mode logo styling */
+:global(.dark) .logo {
+ filter: brightness(0) invert(1);
}
\ No newline at end of file