diff --git a/frontend/public/images/icons/dark-mode.svg b/frontend/public/images/icons/dark-mode.svg
new file mode 100644
index 0000000..6de1aee
--- /dev/null
+++ b/frontend/public/images/icons/dark-mode.svg
@@ -0,0 +1,24 @@
+
\ No newline at end of file
diff --git a/frontend/public/images/icons/light-mode.svg b/frontend/public/images/icons/light-mode.svg
new file mode 100644
index 0000000..93058fd
--- /dev/null
+++ b/frontend/public/images/icons/light-mode.svg
@@ -0,0 +1,24 @@
+
\ No newline at end of file
diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx
index ffdb2a4..f4b056d 100644
--- a/frontend/src/components/Layout.tsx
+++ b/frontend/src/components/Layout.tsx
@@ -1,16 +1,56 @@
+import { useEffect, useState } from "react";
import { useLocation } from "react-router-dom";
import { Menu } from "./Menu";
export const Layout = ({ children }: { children: React.ReactNode }) => {
const { pathname } = useLocation();
+ const [isDark, setIsDark] = useState(false);
+
+ useEffect(() => {
+ // set the initial state of the theme based on the user's preference
+ if (
+ window.matchMedia &&
+ window.matchMedia("(prefers-color-scheme: dark)").matches &&
+ localStorage.getItem("theme") !== "light"
+ ) {
+ setIsDark(true);
+ }
+
+ const handleColorSchemeChange = (event: MediaQueryListEvent) => {
+ // if there is a theme set in local storage, don't change the theme
+ if (
+ localStorage.getItem("theme") === "light" ||
+ localStorage.getItem("theme") === "dark"
+ )
+ return;
+
+ // else set the theme based on the event change
+ if (event.matches) {
+ setIsDark(true);
+ } else {
+ setIsDark(false);
+ }
+ };
+
+ window
+ .matchMedia("(prefers-color-scheme: dark)")
+ .addEventListener("change", handleColorSchemeChange);
+
+ // Clean up the event listener
+ return () => {
+ window
+ .matchMedia("(prefers-color-scheme: dark)")
+ .removeEventListener("change", handleColorSchemeChange);
+ };
+ }, []);
return (
- <>
-
+
+
);
};
diff --git a/frontend/src/components/Menu.tsx b/frontend/src/components/Menu.tsx
index 721f2ef..be5c111 100644
--- a/frontend/src/components/Menu.tsx
+++ b/frontend/src/components/Menu.tsx
@@ -11,16 +11,27 @@ const defaultTools = [
{ name: "HWT Path Profiler", url: "https://heywhatsthat.com/profiler.html" },
];
-export const Menu = () => {
+export const Menu = ({
+ isDark,
+ onDarkChange,
+}: {
+ isDark: boolean;
+ onDarkChange: (dark: boolean) => void;
+}) => {
const { data: config } = useGetConfigQuery();
const [showMenu, setShowMenu] = useState(false);
+ const handleDarkChange = (dark: boolean) => {
+ onDarkChange(dark);
+ localStorage.setItem("theme", dark ? "dark" : "light");
+ };
+
return (
<>
-
+
{config?.mesh?.name?.split(" ").map((word, index) => (
@@ -72,6 +85,15 @@ export const Menu = () => {
+
+
diff --git a/frontend/tailwind.config.ts b/frontend/tailwind.config.ts
index 5fef7c2..32dd6c4 100644
--- a/frontend/tailwind.config.ts
+++ b/frontend/tailwind.config.ts
@@ -9,4 +9,5 @@ export default {
corePlugins: {
preflight: true,
},
+ darkMode: "class",
} satisfies Config;
diff --git a/templates/static/layout.html.j2 b/templates/static/layout.html.j2
index 3efb10e..da1a6d0 100644
--- a/templates/static/layout.html.j2
+++ b/templates/static/layout.html.j2
@@ -37,6 +37,10 @@
{% endif %}
+
+