Skip to content

Commit 5e84dec

Browse files
committed
feat: Dark mode:
1 parent a23945c commit 5e84dec

File tree

8 files changed

+211
-12
lines changed

8 files changed

+211
-12
lines changed

panda.config.ts

Lines changed: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,18 @@ import { defineConfig } from "@pandacss/dev";
22

33
export const tokens = {
44
colors: {
5-
primary: { value: "#1E40AF" }, // Blue-800
6-
secondary: { value: "#9333EA" }, // Purple-600
7-
accent: { value: "#FACC15" }, // Yellow-400
8-
background: { value: "#F3F4F6" }, // Gray-200
9-
text: { value: "#111827" }, // Gray-900
5+
primary: { value: "#006157" }, // Primary color for links, logo, buttons
6+
secondary: { value: "#AE6300" }, // Secondary color
7+
accent: { value: "#00C7B2" }, // Link underline color
8+
background: { value: "#F6FFFE" }, // Body background
9+
text: { value: "#515554" }, // Text color
10+
// Dark mode colors
11+
dark: {
12+
background: { value: "#0A0A0A" }, // Dark background
13+
text: { value: "#F5F5F5" }, // Light text for dark mode
14+
primary: { value: "#00C7B2" }, // Teal for primary in dark mode
15+
secondary: { value: "#FFB74D" }, // Orange for secondary in dark mode
16+
},
1017
},
1118
spacing: {
1219
sm: { value: "8px" },
@@ -20,15 +27,52 @@ export const globalCss = {
2027
backgroundColor: "{colors.background}",
2128
color: "{colors.text}",
2229
fontFamily: "system-ui, sans-serif",
30+
transition: "background-color 0.3s ease, color 0.3s ease",
31+
},
32+
":root": {
33+
colorScheme: "light dark",
34+
},
35+
".dark": {
36+
colorScheme: "dark",
2337
},
2438
};
2539

2640
export const semanticTokens = {
2741
colors: {
42+
surfaceBg: {
43+
value: {
44+
base: "{colors.background}",
45+
_dark: "{colors.dark.background}",
46+
},
47+
},
48+
surfaceText: {
49+
value: {
50+
base: "{colors.text}",
51+
_dark: "{colors.dark.text}",
52+
},
53+
},
54+
interactivePrimary: {
55+
value: {
56+
base: "{colors.primary}",
57+
_dark: "{colors.dark.primary}",
58+
},
59+
},
60+
interactiveSecondary: {
61+
value: {
62+
base: "{colors.secondary}",
63+
_dark: "{colors.dark.secondary}",
64+
},
65+
},
2866
buttonBg: {
2967
value: {
3068
base: "{colors.primary}",
31-
_hover: "{colors.secondary}",
69+
_dark: "{colors.dark.primary}",
70+
},
71+
},
72+
buttonBgHover: {
73+
value: {
74+
base: "{colors.secondary}",
75+
_dark: "{colors.dark.secondary}",
3276
},
3377
},
3478
},
@@ -51,7 +95,23 @@ export default defineConfig({
5195
theme: {
5296
extend: { tokens, semanticTokens, textStyles },
5397
},
54-
globalCss,
98+
conditions: {
99+
dark: ".dark &",
100+
},
101+
globalCss: {
102+
"html, body": {
103+
backgroundColor: "{colors.surfaceBg}",
104+
color: "{colors.surfaceText}",
105+
fontFamily: "system-ui, sans-serif",
106+
transition: "background-color 0.3s ease, color 0.3s ease",
107+
},
108+
":root": {
109+
colorScheme: "light dark",
110+
},
111+
".dark": {
112+
colorScheme: "dark",
113+
},
114+
},
55115
include: [
56116
"./src/**/*.{ts,tsx,js,jsx,astro}",
57117
"./pages/**/*.{ts,tsx,js,jsx,astro}",

public/about-lb.png

12.5 KB
Loading

public/about-me.jpg

126 KB
Loading

src/components/Header.astro

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
import HeaderLink from "./HeaderLink.astro";
33
import Container from "./Container.astro";
4+
import ThemeToggle from './ThemeToggle.astro';
45
56
import { SITE_TITLE } from "../consts";
67
import { css } from "../../styled-system/css";
@@ -21,7 +22,7 @@ import { visuallyHidden } from "../../styled-system/patterns";
2122
display: "flex",
2223
width: "full",
2324
justifyContent: "space-between",
24-
p: { base: "4", md: "7" },
25+
py: { base: "4", md: "8" },
2526
})}
2627
>
2728
<h1>
@@ -103,3 +104,4 @@ import { visuallyHidden } from "../../styled-system/patterns";
103104
</nav>
104105
</Container>
105106
</header>
107+

src/components/ThemeToggle.astro

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
---
2+
import { css } from '../../styled-system/css';
3+
---
4+
5+
<button
6+
id="theme-toggle"
7+
class={css({
8+
position: 'sticky',
9+
left: '4',
10+
top: '4',
11+
width: '40px',
12+
height: '40px',
13+
borderRadius: 'full',
14+
backgroundColor: 'background',
15+
border: '1px solid',
16+
borderColor: 'primary',
17+
display: 'flex',
18+
alignItems: 'center',
19+
justifyContent: 'center',
20+
cursor: 'pointer',
21+
transition: 'all 0.2s',
22+
_hover: {
23+
transform: 'scale(1.1)',
24+
},
25+
_focus: {
26+
outline: '2px solid',
27+
outlineColor: 'primary',
28+
outlineOffset: '2px',
29+
},
30+
})}
31+
aria-label="Toggle dark mode"
32+
>
33+
<svg
34+
id="sun-icon"
35+
xmlns="http://www.w3.org/2000/svg"
36+
width="24"
37+
height="24"
38+
viewBox="0 0 24 24"
39+
fill="none"
40+
stroke="currentColor"
41+
stroke-width="2"
42+
stroke-linecap="round"
43+
stroke-linejoin="round"
44+
class={css({ color: 'primary', display: 'none' })}
45+
>
46+
<circle cx="12" cy="12" r="5" />
47+
<line x1="12" y1="1" x2="12" y2="3" />
48+
<line x1="12" y1="21" x2="12" y2="23" />
49+
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
50+
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
51+
<line x1="1" y1="12" x2="3" y2="12" />
52+
<line x1="21" y1="12" x2="23" y2="12" />
53+
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
54+
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
55+
</svg>
56+
<svg
57+
id="moon-icon"
58+
xmlns="http://www.w3.org/2000/svg"
59+
width="24"
60+
height="24"
61+
viewBox="0 0 24 24"
62+
fill="none"
63+
stroke="currentColor"
64+
stroke-width="2"
65+
stroke-linecap="round"
66+
stroke-linejoin="round"
67+
class={css({ color: 'accent' })}
68+
>
69+
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
70+
</svg>
71+
</button>
72+
73+
<script>
74+
const theme = (() => {
75+
if (typeof localStorage !== 'undefined' && localStorage.getItem('theme')) {
76+
return localStorage.getItem('theme');
77+
}
78+
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
79+
return 'dark';
80+
}
81+
return 'light';
82+
})();
83+
84+
if (theme === 'light') {
85+
document.documentElement.classList.remove('dark');
86+
} else {
87+
document.documentElement.classList.add('dark');
88+
}
89+
90+
window.localStorage.setItem('theme', theme!);
91+
92+
const handleToggleClick = () => {
93+
const element = document.documentElement;
94+
const isDark = element.classList.contains('dark');
95+
const sunIcon = document.getElementById('sun-icon');
96+
const moonIcon = document.getElementById('moon-icon');
97+
98+
element.classList.toggle('dark');
99+
sunIcon?.classList.toggle('hidden');
100+
moonIcon?.classList.toggle('hidden');
101+
102+
const newTheme = isDark ? 'light' : 'dark';
103+
localStorage.setItem('theme', newTheme);
104+
};
105+
106+
document.getElementById('theme-toggle')?.addEventListener('click', handleToggleClick);
107+
</script>
108+
109+
<style>
110+
.hidden {
111+
display: none;
112+
}
113+
</style>

src/layouts/BaseLayout.astro

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import BaseHead from "../components/BaseHead.astro";
55
import Header from "../components/Header.astro";
66
import Container from "../components/Container.astro";
77
import Footer from "../components/Footer.astro";
8+
import ThemeToggle from '../components/ThemeToggle.astro';
9+
810
911
const { title, description } = Astro.props;
1012
---
@@ -24,5 +26,6 @@ const { title, description } = Astro.props;
2426
</Container>
2527
</main>
2628
<Footer />
29+
<ThemeToggle />
2730
</body>
2831
</html>

src/styles/global.css

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,34 @@
1515
font-display: swap;
1616
}
1717

18+
:root {
19+
color-scheme: light dark;
20+
}
21+
22+
html {
23+
height: 100%;
24+
}
25+
1826
body {
1927
font-family: "Atkinson Hyperlegible", sans-serif;
20-
background-color: var(--colors-gray-100);
21-
color: var(--colors-gray-900);
28+
background-color: var(--colors-surfaceBg);
29+
color: var(--colors-surfaceText);
2230
margin: 0;
2331
padding: 0;
32+
transition: background-color 0.3s ease, color 0.3s ease;
33+
height: 100vh;
34+
}
35+
36+
/* Force dark mode styles */
37+
.dark {
38+
background-color: var(--colors-surfaceBg);
39+
color: var(--colors-surfaceText);
2440
}
2541

2642
a {
27-
color: var(--colors-primary);
43+
color: var(--colors-interactivePrimary);
2844
text-decoration: none;
45+
transition: color 0.3s ease;
2946
}
3047

3148
a:hover {
@@ -40,6 +57,8 @@ h5,
4057
h6 {
4158
margin: 0;
4259
padding: 0;
60+
color: var(--colors-surfaceText);
61+
transition: color 0.3s ease;
4362
}
4463

4564
ul {
@@ -49,4 +68,5 @@ ul {
4968

5069
button {
5170
cursor: pointer;
71+
transition: background-color 0.3s ease, color 0.3s ease;
5272
}

tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
"extends": "astro/tsconfigs/strict",
33
"compilerOptions": {
44
"strictNullChecks": true,
5-
"types": ["node"]
5+
"types": ["node"],
6+
"typeRoots": ["./node_modules/@types"]
67
},
78
"include": ["src", "styled-system"]
89
}

0 commit comments

Comments
 (0)