diff --git a/public/app.js b/public/app.js index 183d6809..f80e43f9 100644 --- a/public/app.js +++ b/public/app.js @@ -510,8 +510,11 @@ window.addEventListener('DOMContentLoaded', () => { fetch('/api/config/theme').then(r => r.json()).then(cfg => { window.SITE_CONFIG = cfg; - // Apply CSS variable overrides from theme.* - if (cfg.theme) { + // User's localStorage preferences take priority over server config + const userTheme = (() => { try { return JSON.parse(localStorage.getItem('meshcore-user-theme') || '{}'); } catch { return {}; } })(); + + // Apply CSS variable overrides from theme.* (server config, skipped if user has local overrides) + if (cfg.theme && !userTheme.theme && !userTheme.themeDark) { const root = document.documentElement.style; const varMap = { accent: '--accent', accentHover: '--accent-hover', @@ -533,16 +536,16 @@ window.addEventListener('DOMContentLoaded', () => { } } - // Apply node color overrides to ROLE_COLORS and ROLE_STYLE - if (cfg.nodeColors) { + // Apply node color overrides (skip if user has local preferences) + if (cfg.nodeColors && !userTheme.nodeColors) { for (const [role, color] of Object.entries(cfg.nodeColors)) { if (window.ROLE_COLORS && role in window.ROLE_COLORS) window.ROLE_COLORS[role] = color; if (window.ROLE_STYLE && window.ROLE_STYLE[role]) window.ROLE_STYLE[role].color = color; } } - // Apply branding - if (cfg.branding) { + // Apply branding (skip if user has local preferences) + if (cfg.branding && !userTheme.branding) { if (cfg.branding.siteName) { document.title = cfg.branding.siteName; const brandText = document.querySelector('.brand-text'); diff --git a/public/index.html b/public/index.html index c1c2ebe3..5f331f91 100644 --- a/public/index.html +++ b/public/index.html @@ -82,11 +82,11 @@ - + - +