diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index 5042dab..b0647da 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -1,6 +1,7 @@
import React, { useState, useEffect } from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { AuthProvider, useAuth } from './context/AuthContext';
+import { ThemeProvider } from './context/ThemeContext';
import { ToastProvider, useToast } from './components/Toast';
import { setGlobalErrorHandler } from './utils/api';
import AuthModal from './components/AuthModal';
@@ -139,11 +140,13 @@ function AppContent() {
export default function App() {
return (
-
-
-
-
-
+
+
+
+
+
+
+
);
}
diff --git a/frontend/src/components/Sidebar.jsx b/frontend/src/components/Sidebar.jsx
index 87c295f..aa14d07 100644
--- a/frontend/src/components/Sidebar.jsx
+++ b/frontend/src/components/Sidebar.jsx
@@ -1,5 +1,6 @@
import React, { useState } from 'react';
import { useAuth } from '../context/AuthContext';
+import { useTheme } from '../context/ThemeContext';
// Punkt 5: SVG Icons für jeden Tab
const tabIcons = {
@@ -22,6 +23,7 @@ const tabIcons = {
export default function Sidebar({ activeTab, onTabChange }) {
const { user, logout } = useAuth();
+ const { isDark, toggleTheme } = useTheme();
// Punkt 19: Collapsible Sidebar State
const [collapsed, setCollapsed] = useState(false);
@@ -101,6 +103,26 @@ export default function Sidebar({ activeTab, onTabChange }) {
)}
+ {/* Theme-Toggle: Dark/Light-Mode, individuelle Einstellung pro Nutzer (localStorage) */}
+ {!collapsed && (
+
+ )}
+ {collapsed && (
+
+ )}
diff --git a/frontend/src/context/ThemeContext.jsx b/frontend/src/context/ThemeContext.jsx
new file mode 100644
index 0000000..c637962
--- /dev/null
+++ b/frontend/src/context/ThemeContext.jsx
@@ -0,0 +1,53 @@
+import React, { createContext, useContext, useEffect, useState } from 'react';
+
+/**
+ * Theme-Context: Dark/Light-Mode-Umschaltung für daisyUI (data-theme auf ).
+ * - Persistenz in localStorage ('workflow_theme': 'dark' | 'light')
+ * - Ohne gespeicherte Wahl folgt das Theme der Systemeinstellung (prefers-color-scheme)
+ * - FOUC-Schutz: initiales data-theme wird synchron vor dem ersten Render gesetzt
+ */
+
+const STORAGE_KEY = 'workflow_theme';
+
+// Synchron beim Modul-Load ausführen — verhindert einen Theme-Flash beim Start
+function resolveInitialTheme() {
+ try {
+ const saved = localStorage.getItem(STORAGE_KEY);
+ if (saved === 'dark' || saved === 'light') return saved;
+ } catch (e) { /* localStorage nicht verfügbar — Fallback unten */ }
+ if (typeof window !== 'undefined' && window.matchMedia?.('(prefers-color-scheme: dark)').matches) {
+ return 'dark';
+ }
+ return 'light';
+}
+
+// Direkt beim Laden das Theme setzen (vor React-Render)
+const initialTheme = resolveInitialTheme();
+document.documentElement.setAttribute('data-theme', initialTheme);
+
+const ThemeContext = createContext(null);
+
+export function ThemeProvider({ children }) {
+ const [theme, setTheme] = useState(initialTheme);
+
+ useEffect(() => {
+ document.documentElement.setAttribute('data-theme', theme);
+ try {
+ localStorage.setItem(STORAGE_KEY, theme);
+ } catch (e) { /* Speichern fehlgeschlagen — Theme bleibt für diese Session aktiv */ }
+ }, [theme]);
+
+ const toggleTheme = () => setTheme((t) => (t === 'dark' ? 'light' : 'dark'));
+
+ return (
+
+ {children}
+
+ );
+}
+
+export function useTheme() {
+ const ctx = useContext(ThemeContext);
+ if (!ctx) throw new Error('useTheme must be used within ThemeProvider');
+ return ctx;
+}
\ No newline at end of file