Skip to content

Commit 610afd8

Browse files
committed
feat: add light/dark mode toggle
1 parent 1b5aad9 commit 610afd8

File tree

2 files changed

+97
-4
lines changed

2 files changed

+97
-4
lines changed

cmd/chromad/static/index.css

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,12 @@ textarea {
1515
#output pre {
1616
padding: 0;
1717
}
18+
19+
/* Override system theme preference when data-theme is set */
20+
:root[data-theme="light"] {
21+
color-scheme: light;
22+
}
23+
24+
:root[data-theme="dark"] {
25+
color-scheme: dark;
26+
}

cmd/chromad/static/index.js

Lines changed: 88 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ import * as Base64 from "./base64.js";
22
import { chroma } from "./chroma.js";
33

44
function init() {
5-
const darkMode = window.matchMedia?.("(prefers-color-scheme: dark)").matches;
5+
const systemDarkModeQuery = window.matchMedia?.(
6+
"(prefers-color-scheme: dark)",
7+
);
8+
const systemDarkMode = systemDarkModeQuery?.matches;
69
const style = document.createElement("style");
710
const ref = document.querySelector("script");
811
ref.parentNode.insertBefore(style, ref);
@@ -15,6 +18,82 @@ function init() {
1518
const csrfToken = form.elements["gorilla.csrf.Token"].value;
1619
const output = document.getElementById("output");
1720
const htmlCheckbox = document.getElementById("html");
21+
const themeToggle = document.getElementById("theme-toggle");
22+
const themeIcon = document.getElementById("theme-icon");
23+
24+
function getThemePreference() {
25+
const stored = localStorage.getItem("theme");
26+
if (stored) {
27+
return stored;
28+
}
29+
return "auto";
30+
}
31+
32+
function setThemePreference(theme) {
33+
if (theme === "auto") {
34+
localStorage.removeItem("theme");
35+
} else {
36+
localStorage.setItem("theme", theme);
37+
}
38+
}
39+
40+
function getEffectiveTheme(theme) {
41+
if (theme === "auto") {
42+
const currentSystemDarkMode = systemDarkModeQuery?.matches ?? false;
43+
return currentSystemDarkMode ? "dark" : "light";
44+
}
45+
return theme;
46+
}
47+
48+
function applyTheme(theme) {
49+
const effectiveTheme = getEffectiveTheme(theme);
50+
const isDark = effectiveTheme === "dark";
51+
document.documentElement.setAttribute("data-theme", effectiveTheme);
52+
53+
// Set icon based on the effective theme (current mode)
54+
if (theme === "auto") {
55+
themeIcon.setAttribute("name", "ellipse-outline");
56+
} else if (isDark) {
57+
themeIcon.setAttribute("name", "moon-outline");
58+
} else {
59+
themeIcon.setAttribute("name", "sunny-outline");
60+
}
61+
62+
if (isDark && styleSelect.value === "monokailight") {
63+
styleSelect.value = "monokai";
64+
update(new Event("change"));
65+
} else if (!isDark && styleSelect.value === "monokai") {
66+
styleSelect.value = "monokailight";
67+
update(new Event("change"));
68+
}
69+
}
70+
71+
function toggleTheme() {
72+
const currentTheme = getThemePreference();
73+
let newTheme;
74+
if (currentTheme === "light") {
75+
newTheme = "dark";
76+
} else if (currentTheme === "dark") {
77+
newTheme = "auto";
78+
} else {
79+
newTheme = "light";
80+
}
81+
setThemePreference(newTheme);
82+
applyTheme(newTheme);
83+
}
84+
85+
themeToggle.addEventListener("click", toggleTheme);
86+
87+
// Listen for system preference changes
88+
if (systemDarkModeQuery) {
89+
systemDarkModeQuery.addEventListener("change", (e) => {
90+
const currentTheme = getThemePreference();
91+
if (currentTheme === "auto") {
92+
// Re-apply theme to update based on new system preference
93+
applyTheme("auto");
94+
}
95+
});
96+
}
1897

1998
(document.querySelectorAll(".notification .delete") || []).forEach((el) => {
2099
const notification = el.parentNode;
@@ -183,6 +262,9 @@ function init() {
183262
event.preventDefault();
184263
}
185264

265+
const initialTheme = getThemePreference();
266+
applyTheme(initialTheme);
267+
186268
if (location.hash) {
187269
let json = Base64.decode(location.hash.substring(1));
188270
json = JSON.parse(json);
@@ -191,10 +273,12 @@ function init() {
191273
styleSelect.value = json.style;
192274
htmlCheckbox.checked = json.classes;
193275
update(new Event("change"));
194-
} else if (darkMode) {
195-
styleSelect.value = "monokai";
196-
update(new Event("change"));
197276
} else {
277+
const effectiveTheme = getEffectiveTheme(initialTheme);
278+
const isDark = effectiveTheme === "dark";
279+
if (isDark && styleSelect.value === "monokailight") {
280+
styleSelect.value = "monokai";
281+
}
198282
update(new Event("change"));
199283
}
200284

0 commit comments

Comments
 (0)