Skip to content

Commit 0eeed8e

Browse files
committed
refactor: update toggle theme function for better structure and a11y
Move theme switch js codes in Layout.astro file to external js file (toggle-theme.js). Update .prettierignore to exclude the new toggle-theme.js file. Update base.css for new data-theme attribute.
1 parent 5a67afc commit 0eeed8e

File tree

4 files changed

+61
-44
lines changed

4 files changed

+61
-44
lines changed

.prettierignore

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
# Except these files & folders
55
!/src
6+
!/public
67
!/.github
78
!tsconfig.json
89
!astro.config.mjs

public/toggle-theme.js

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
const primaryColorScheme = ""; // "light" | "dark"
2+
3+
// Get theme data from local storage
4+
const currentTheme = localStorage.getItem("theme");
5+
6+
function getPreferTheme() {
7+
// return theme value in local storage if it is set
8+
if (currentTheme) return currentTheme;
9+
10+
// return primary color scheme if it is set
11+
if (primaryColorScheme) return primaryColorScheme;
12+
13+
// return user device's prefer color scheme
14+
return window.matchMedia("(prefers-color-scheme: dark)").matches
15+
? "dark"
16+
: "light";
17+
}
18+
19+
let themeValue = getPreferTheme();
20+
21+
function setPreference() {
22+
localStorage.setItem("theme", themeValue);
23+
reflectPreference();
24+
}
25+
26+
function reflectPreference() {
27+
document.firstElementChild.setAttribute("data-theme", themeValue);
28+
29+
document.querySelector("#theme-btn")?.setAttribute("aria-label", themeValue);
30+
}
31+
32+
// set early so no page flashes / CSS is made aware
33+
reflectPreference();
34+
35+
window.onload = () => {
36+
// set on load so screen readers can get the latest value on the button
37+
reflectPreference();
38+
39+
// now this script can find and listen for clicks on the control
40+
document.querySelector("#theme-btn").addEventListener("click", () => {
41+
themeValue = themeValue === "light" ? "dark" : "light";
42+
setPreference();
43+
});
44+
};
45+
46+
// sync with system changes
47+
window
48+
.matchMedia("(prefers-color-scheme: dark)")
49+
.addEventListener("change", ({ matches: isDark }) => {
50+
themeValue = isDark ? "dark" : "light";
51+
setPreference();
52+
});

src/layouts/Layout.astro

+1-39
Original file line numberDiff line numberDiff line change
@@ -61,45 +61,7 @@ const socialImageURL = new URL(
6161
onload="this.onload=null;this.rel='stylesheet'"
6262
/>
6363

64-
<script is:inline>
65-
const primaryColorScheme = "none"; // "light" | "dark" | "none"
66-
67-
const darkModeMediaQuery = window.matchMedia(
68-
"(prefers-color-scheme: dark)"
69-
).matches;
70-
71-
// Get theme data from local storage
72-
const currentTheme = localStorage.getItem("theme");
73-
74-
let theme;
75-
76-
// Set theme to 'theme-dark' if currentTheme is 'dark'
77-
if (currentTheme) {
78-
theme = currentTheme === "dark" ? "theme-dark" : "";
79-
} else {
80-
// If primary color scheme is dark
81-
// or primary color scheme is not set and prefers-color-scheme is dark
82-
// choose dark mode
83-
if (
84-
primaryColorScheme === "dark" ||
85-
(primaryColorScheme === "none" && darkModeMediaQuery)
86-
) {
87-
theme = "theme-dark";
88-
}
89-
// If primary color scheme is light
90-
// choose light mode
91-
else if (primaryColorScheme === "light") {
92-
theme = "";
93-
}
94-
// fallback to prefers-color-scheme
95-
else {
96-
theme = darkModeMediaQuery ? "theme-dark" : "";
97-
}
98-
}
99-
100-
// Put dark class on html tag to enable dark mode
101-
document.querySelector("html").className = theme;
102-
</script>
64+
<script is:inline src="/toggle-theme.js"></script>
10365
</head>
10466
<body>
10567
<slot />

src/styles/base.css

+7-5
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@
33
@tailwind utilities;
44

55
@layer base {
6-
:root {
6+
:root,
7+
html[data-theme="light"] {
78
--color-fill: 251, 254, 251;
89
--color-text-base: 40, 39, 40;
910
--color-accent: 0, 108, 172;
1011
--color-card: 230, 230, 230;
1112
--color-card-muted: 205, 205, 205;
1213
--color-border: 236, 233, 233;
1314
}
14-
.theme-dark {
15+
html[data-theme="dark"] {
1516
--color-fill: 47, 55, 65;
1617
--color-text-base: 230, 230, 230;
1718
--color-accent: 26, 217, 217;
@@ -20,11 +21,11 @@
2021
--color-border: 59, 70, 85;
2122
}
2223
#sun-svg,
23-
.theme-dark #moon-svg {
24+
html[data-theme="dark"] #moon-svg {
2425
display: none;
2526
}
2627
#moon-svg,
27-
.theme-dark #sun-svg {
28+
html[data-theme="dark"] #sun-svg {
2829
display: block;
2930
}
3031
body {
@@ -36,7 +37,8 @@
3637
@apply max-w-3xl mx-auto px-4;
3738
}
3839
a {
39-
@apply outline-offset-1 outline-skin-fill outline-2 focus-visible:outline-dashed focus-visible:no-underline;
40+
@apply outline-offset-1 outline-skin-fill outline-2
41+
focus-visible:outline-dashed focus-visible:no-underline;
4042
}
4143
svg {
4244
@apply w-6 h-6 inline-block fill-skin-base group-hover:fill-skin-accent;

0 commit comments

Comments
 (0)