From d3c4fdc6d67a043e52d459a167999587d6a5104f Mon Sep 17 00:00:00 2001 From: you Date: Mon, 23 Mar 2026 04:53:57 +0000 Subject: [PATCH] fix: server theme config actually applies on the client - Dark mode: now merges theme + themeDark and applies correctly - Added missing CSS var mappings: navText, navTextMuted, background, sectionBg, font, mono - Fixed 'background' key mapping (was 'surface0', never matched) - Derived vars (content-bg, card-bg) set from server config - Type colors from server config now applied to TYPE_COLORS global - syncBadgeColors called after type color override --- public/app.js | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/public/app.js b/public/app.js index f80e43f9..096ea8bd 100644 --- a/public/app.js +++ b/public/app.js @@ -513,26 +513,33 @@ window.addEventListener('DOMContentLoaded', () => { // 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) { + // Apply CSS variable overrides from theme config (skipped if user has local overrides) + if (!userTheme.theme && !userTheme.themeDark) { + const dark = document.documentElement.getAttribute('data-theme') === 'dark' || + (document.documentElement.getAttribute('data-theme') !== 'light' && window.matchMedia('(prefers-color-scheme: dark)').matches); + const themeData = dark ? { ...(cfg.theme || {}), ...(cfg.themeDark || {}) } : (cfg.theme || {}); const root = document.documentElement.style; const varMap = { accent: '--accent', accentHover: '--accent-hover', - navBg: '--nav-bg', navBg2: '--nav-bg2', + navBg: '--nav-bg', navBg2: '--nav-bg2', navText: '--nav-text', navTextMuted: '--nav-text-muted', + background: '--surface-0', text: '--text', textMuted: '--text-muted', border: '--border', statusGreen: '--status-green', statusYellow: '--status-yellow', statusRed: '--status-red', - text: '--text', textMuted: '--text-muted', border: '--border', - surface0: '--surface-0', surface1: '--surface-1', surface2: '--surface-2', surface3: '--surface-3', + surface1: '--surface-1', surface2: '--surface-2', surface3: '--surface-3', cardBg: '--card-bg', contentBg: '--content-bg', inputBg: '--input-bg', rowStripe: '--row-stripe', rowHover: '--row-hover', detailBg: '--detail-bg', - selectedBg: '--selected-bg' + selectedBg: '--selected-bg', sectionBg: '--section-bg', + font: '--font', mono: '--mono' }; for (const [key, cssVar] of Object.entries(varMap)) { - if (cfg.theme[key]) root.setProperty(cssVar, cfg.theme[key]); + if (themeData[key]) root.setProperty(cssVar, themeData[key]); } - // Also update nav gradient if navBg is customized - if (cfg.theme.navBg) { + // Derived vars + if (themeData.background) root.setProperty('--content-bg', themeData.contentBg || themeData.background); + if (themeData.surface1) root.setProperty('--card-bg', themeData.cardBg || themeData.surface1); + // Nav gradient + if (themeData.navBg) { const nav = document.querySelector('.top-nav'); - if (nav) nav.style.background = `linear-gradient(135deg, ${cfg.theme.navBg} 0%, ${cfg.theme.navBg2 || cfg.theme.navBg} 50%, ${cfg.theme.navBg} 100%)`; + if (nav) nav.style.background = `linear-gradient(135deg, ${themeData.navBg} 0%, ${themeData.navBg2 || themeData.navBg} 50%, ${themeData.navBg} 100%)`; } } @@ -544,6 +551,14 @@ window.addEventListener('DOMContentLoaded', () => { } } + // Apply type color overrides (skip if user has local preferences) + if (cfg.typeColors && !userTheme.typeColors) { + for (const [type, color] of Object.entries(cfg.typeColors)) { + if (window.TYPE_COLORS && type in window.TYPE_COLORS) window.TYPE_COLORS[type] = color; + } + if (window.syncBadgeColors) window.syncBadgeColors(); + } + // Apply branding (skip if user has local preferences) if (cfg.branding && !userTheme.branding) { if (cfg.branding.siteName) {