diff --git a/src/frontend/src/App.tsx b/src/frontend/src/App.tsx
index c2a64a7..35421a8 100644
--- a/src/frontend/src/App.tsx
+++ b/src/frontend/src/App.tsx
@@ -1,6 +1,7 @@
import { Routes, Route, Navigate } from 'react-router-dom';
import { Sidebar } from './components/Sidebar';
import { ToastProvider } from './components/Toast';
+import { useTheme } from './hooks/useTheme';
import { Channels } from './pages/Channels';
import { ChannelDetail } from './pages/ChannelDetail';
import { Library } from './pages/Library';
@@ -10,6 +11,8 @@ import { SettingsPage } from './pages/Settings';
import { SystemPage } from './pages/System';
function AuthenticatedLayout() {
+ // Apply theme from settings to documentElement at the app root
+ useTheme();
return (
diff --git a/src/frontend/src/hooks/useTheme.ts b/src/frontend/src/hooks/useTheme.ts
new file mode 100644
index 0000000..6a94dbd
--- /dev/null
+++ b/src/frontend/src/hooks/useTheme.ts
@@ -0,0 +1,21 @@
+import { useEffect } from 'react';
+import { useAppSettings } from '../api/hooks/useSystem';
+
+/**
+ * Reads the user-configured theme from app settings and applies it
+ * to the document element. Falls back to localStorage (set by the
+ * inline script in index.html) then 'dark'.
+ *
+ * Call once near the app root so theme stays in sync across all pages.
+ */
+export function useTheme(): 'dark' | 'light' {
+ const { data: settings } = useAppSettings();
+ const theme: 'dark' | 'light' = settings?.theme ?? (localStorage.getItem('tubearr-theme') as 'dark' | 'light') ?? 'dark';
+
+ useEffect(() => {
+ document.documentElement.dataset.theme = theme;
+ localStorage.setItem('tubearr-theme', theme);
+ }, [theme]);
+
+ return theme;
+}
diff --git a/src/frontend/src/pages/ChannelDetail.tsx b/src/frontend/src/pages/ChannelDetail.tsx
index 99dafb1..9e96b53 100644
--- a/src/frontend/src/pages/ChannelDetail.tsx
+++ b/src/frontend/src/pages/ChannelDetail.tsx
@@ -1891,12 +1891,12 @@ export function ChannelDetail() {
alignItems: 'center',
gap: 'var(--space-3)',
padding: 'var(--space-3) var(--space-5)',
- backgroundColor: 'rgba(30, 32, 40, 0.75)',
+ backgroundColor: 'var(--glass-bg)',
backdropFilter: 'blur(16px) saturate(1.4)',
WebkitBackdropFilter: 'blur(16px) saturate(1.4)',
- border: '1px solid rgba(255, 255, 255, 0.08)',
+ border: '1px solid var(--glass-border)',
borderRadius: 'var(--radius-xl)',
- boxShadow: '0 8px 32px rgba(0, 0, 0, 0.35), inset 0 0.5px 0 rgba(255, 255, 255, 0.06)',
+ boxShadow: 'var(--shadow-md)',
}}
>
{
document.documentElement.dataset.theme = theme;
localStorage.setItem('tubearr-theme', theme);
diff --git a/src/frontend/src/styles/global.css b/src/frontend/src/styles/global.css
index eb1bc1f..704be1c 100644
--- a/src/frontend/src/styles/global.css
+++ b/src/frontend/src/styles/global.css
@@ -60,12 +60,12 @@ a:hover {
}
::-webkit-scrollbar-thumb {
- background: rgba(255, 255, 255, 0.08);
+ background: var(--bg-selected);
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
- background: rgba(255, 255, 255, 0.15);
+ background: var(--border-light);
}
/* ── Buttons base ── */
@@ -158,7 +158,7 @@ tr:hover {
background: linear-gradient(
90deg,
var(--bg-input) 25%,
- rgba(255, 255, 255, 0.04) 50%,
+ var(--bg-hover) 50%,
var(--bg-input) 75%
);
background-size: 200% 100%;