Skip to content

Commit dbb680b

Browse files
committed
Add dark mode support: update header and CSS styles
1 parent 19be346 commit dbb680b

File tree

3 files changed

+333
-8
lines changed

3 files changed

+333
-8
lines changed

src/components/Layout/Header.tsx

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,36 @@
1-
import React from 'react';
2-
import { Globe } from 'lucide-react';
1+
import React, { useState, useEffect } from 'react';
2+
import { Globe, Moon, Sun } from 'lucide-react';
33

44
/**
5-
* Header component with the application logo and title
5+
* Header component with the application logo, title, and dark mode toggle
66
* @component
7-
* @returns {JSX.Element} The application header with ProgramEarth branding
7+
* @returns {JSX.Element} The application header with ProgramEarth branding and dark mode toggle
88
*/
99
export const Header: React.FC = () => {
10+
const [isDarkMode, setIsDarkMode] = useState(false);
11+
12+
// Load dark mode preference from localStorage on mount
13+
useEffect(() => {
14+
const savedTheme = localStorage.getItem('darkMode');
15+
if (savedTheme === 'true') {
16+
setIsDarkMode(true);
17+
document.documentElement.classList.add('dark');
18+
}
19+
}, []);
20+
21+
// Toggle dark mode and save preference
22+
const toggleDarkMode = () => {
23+
const newDarkMode = !isDarkMode;
24+
setIsDarkMode(newDarkMode);
25+
localStorage.setItem('darkMode', newDarkMode.toString());
26+
27+
if (newDarkMode) {
28+
document.documentElement.classList.add('dark');
29+
} else {
30+
document.documentElement.classList.remove('dark');
31+
}
32+
};
33+
1034
return (
1135
<header className="header">
1236
<div className="header-logo">
@@ -15,6 +39,25 @@ export const Header: React.FC = () => {
1539
</div>
1640
<span>ProgramEarth</span>
1741
</div>
42+
43+
<div className="header-controls">
44+
<div className="dark-mode-container">
45+
<span className="dark-mode-label">
46+
{isDarkMode ? 'Light Mode' : 'Dark Mode'}
47+
</span>
48+
<button
49+
className={`dark-mode-toggle ${isDarkMode ? 'dark-mode-toggle--active' : ''}`}
50+
onClick={toggleDarkMode}
51+
aria-label={`Switch to ${isDarkMode ? 'light' : 'dark'} mode`}
52+
>
53+
<div className="dark-mode-toggle__slider">
54+
<div className="dark-mode-toggle__icon">
55+
{isDarkMode ? <Sun size={18} /> : <Moon size={18} />}
56+
</div>
57+
</div>
58+
</button>
59+
</div>
60+
</div>
1861
</header>
1962
);
2063
};

src/styles/globals.css

Lines changed: 122 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ body {
1010
min-height: 100vh;
1111
margin: 0;
1212
padding: 0;
13+
transition: all 0.3s ease;
14+
}
15+
16+
/* Dark mode body */
17+
.dark body {
18+
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
1319
}
1420

1521
.app-container {
@@ -22,6 +28,13 @@ body {
2228
margin: 10px;
2329
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
2430
overflow: hidden;
31+
transition: all 0.3s ease;
32+
}
33+
34+
/* Dark mode app container */
35+
.dark .app-container {
36+
background: rgba(0, 0, 0, 0.2);
37+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
2538
}
2639

2740
.main-content {
@@ -35,8 +48,116 @@ body {
3548
width: 16px;
3649
height: 16px;
3750
cursor: pointer;
51+
accent-color: #667eea;
52+
border: 1px solid rgba(102, 126, 234, 0.3);
53+
border-radius: 3px;
3854
}
3955

4056
.checkbox-input--disabled {
4157
cursor: not-allowed;
42-
}
58+
opacity: 0.5;
59+
}
60+
61+
/* Header controls */
62+
.header-controls {
63+
display: flex;
64+
align-items: center;
65+
gap: 16px;
66+
margin-left: auto;
67+
}
68+
69+
/* Dark mode container */
70+
.dark-mode-container {
71+
display: flex;
72+
align-items: center;
73+
gap: 12px;
74+
background: rgba(255, 255, 255, 0.1);
75+
padding: 8px 16px;
76+
border-radius: 25px;
77+
backdrop-filter: blur(10px);
78+
border: 1px solid rgba(255, 255, 255, 0.2);
79+
transition: all 0.3s ease;
80+
}
81+
82+
.dark-mode-container:hover {
83+
background: rgba(255, 255, 255, 0.15);
84+
transform: translateY(-1px);
85+
}
86+
87+
.dark-mode-label {
88+
font-size: 14px;
89+
font-weight: 600;
90+
color: #2c3e50;
91+
transition: all 0.3s ease;
92+
white-space: nowrap;
93+
}
94+
95+
/* Dark mode toggle switch */
96+
.dark-mode-toggle {
97+
position: relative;
98+
width: 70px;
99+
height: 35px;
100+
background: rgba(255, 255, 255, 0.2);
101+
border-radius: 17.5px;
102+
border: none;
103+
cursor: pointer;
104+
transition: all 0.3s ease;
105+
backdrop-filter: blur(5px);
106+
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
107+
}
108+
109+
.dark-mode-toggle:hover {
110+
background: rgba(255, 255, 255, 0.3);
111+
transform: scale(1.05);
112+
}
113+
114+
.dark-mode-toggle--active {
115+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
116+
}
117+
118+
.dark-mode-toggle--active:hover {
119+
background: linear-gradient(135deg, #5a6fd8 0%, #6a4190 100%);
120+
}
121+
122+
.dark-mode-toggle__slider {
123+
position: absolute;
124+
top: 2px;
125+
left: 2px;
126+
width: 31px;
127+
height: 31px;
128+
background: white;
129+
border-radius: 50%;
130+
transition: all 0.3s ease;
131+
display: flex;
132+
align-items: center;
133+
justify-content: center;
134+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
135+
}
136+
137+
.dark-mode-toggle--active .dark-mode-toggle__slider {
138+
transform: translateX(35px);
139+
background: #1a1a2e;
140+
}
141+
142+
.dark-mode-toggle__icon {
143+
color: #667eea;
144+
transition: all 0.3s ease;
145+
}
146+
147+
.dark-mode-toggle--active .dark-mode-toggle__icon {
148+
color: #fbbf24;
149+
}
150+
151+
/* Dark mode styles for toggle */
152+
.dark .dark-mode-container {
153+
background: rgba(0, 0, 0, 0.2);
154+
border: 1px solid rgba(255, 255, 255, 0.1);
155+
}
156+
157+
.dark .dark-mode-container:hover {
158+
background: rgba(0, 0, 0, 0.3);
159+
}
160+
161+
.dark .dark-mode-label {
162+
color: #e2e8f0;
163+
}

0 commit comments

Comments
 (0)