+
+
+ {label}
+
+
+ {themes.map((theme) => (
+
+ ))}
+
+
+ );
+};
diff --git a/frontend/src/modules/theme-changer/utils/apply.theme.ts b/frontend/src/modules/theme-changer/utils/apply.theme.ts
new file mode 100644
index 0000000..3d3fb26
--- /dev/null
+++ b/frontend/src/modules/theme-changer/utils/apply.theme.ts
@@ -0,0 +1,105 @@
+import { themes } from "../config/theme.config";
+
+export const applyTheme = (themeId: string) => {
+ const theme = themes.find((t) => t.id === themeId);
+ const root = document.documentElement;
+
+ if (theme) {
+ try {
+ root.setAttribute("data-theme", themeId);
+ localStorage.setItem("theme", themeId);
+ localStorage.setItem("theme-type", theme.type);
+
+ window.dispatchEvent(
+ new CustomEvent("themechange", {
+ detail: { theme: themeId, type: theme.type },
+ }),
+ );
+ } catch (error) {
+ console.error("❌ Error applying theme:", error);
+ }
+ } else {
+ console.warn(`⚠️ Theme not found: ${themeId}, falling back to light theme`);
+ applyTheme("light");
+ }
+};
+
+export const getSavedTheme = () => {
+ try {
+ return localStorage.getItem("theme") || "light";
+ } catch (error) {
+ console.error("Error reading theme from localStorage:", error);
+ return "light";
+ }
+};
+
+export const initializeTheme = () => {
+ const savedTheme = getSavedTheme();
+
+ const themeExists = themes.some((t) => t.id === savedTheme);
+ const themeToApply = themeExists ? savedTheme : "light";
+
+ applyTheme(themeToApply);
+ return themeToApply;
+};
+
+export const getCurrentTheme = () => {
+ return document.documentElement.getAttribute("data-theme") || "light";
+};
+
+export const getCurrentThemeType = () => {
+ const currentTheme = getCurrentTheme();
+ const theme = themes.find((t) => t.id === currentTheme);
+ return theme ? theme.type : "light";
+};
+
+export const toggleDarkLight = () => {
+ const currentTheme = getCurrentTheme();
+ const currentThemeData = themes.find((t) => t.id === currentTheme);
+
+ if (currentThemeData) {
+ const oppositeThemes = themes.filter(
+ (t) => t.type !== currentThemeData.type,
+ );
+ if (oppositeThemes.length > 0) {
+ applyTheme(oppositeThemes[0].id);
+ return oppositeThemes[0].id;
+ }
+ }
+
+ const newTheme = currentTheme === "light" ? "dark" : "light";
+ applyTheme(newTheme);
+ return newTheme;
+};
+
+export const getNextTheme = () => {
+ const currentTheme = getCurrentTheme();
+ const currentIndex = themes.findIndex((t) => t.id === currentTheme);
+ const nextIndex = (currentIndex + 1) % themes.length;
+ return themes[nextIndex].id;
+};
+
+export const applySystemTheme = () => {
+ if (
+ window.matchMedia &&
+ window.matchMedia("(prefers-color-scheme: dark)").matches
+ ) {
+ applyTheme("dark");
+ } else {
+ applyTheme("light");
+ }
+};
+
+export const watchSystemTheme = () => {
+ if (window.matchMedia) {
+ window
+ .matchMedia("(prefers-color-scheme: dark)")
+ .addEventListener("change", (e) => {
+ if (e.matches) {
+ applyTheme("dark");
+ } else {
+ applyTheme("light");
+ }
+ });
+ }
+};
diff --git a/frontend/src/pages/themes.page.tsx b/frontend/src/pages/themes.page.tsx
new file mode 100644
index 0000000..fa02cc9
--- /dev/null
+++ b/frontend/src/pages/themes.page.tsx
@@ -0,0 +1,9 @@
+import { ThemeChanger } from "@/modules/theme-changer";
+
+export const ThemesPage = () => {
+ return (
+