feat(branding): force color mode (dark or light) site-wide

This commit is contained in:
Luca
2026-05-05 16:06:38 +02:00
parent 114aab5777
commit 5a162fc8be
11 changed files with 211 additions and 21 deletions
@@ -18,7 +18,14 @@ export const GlobalThemeProvider: React.FC<GlobalThemeProviderProps> = ({ childr
if (!themeAppliedRef.current && settingsData?.theme_config && !isGalleryPage) {
themeAppliedRef.current = true;
setTheme(settingsData.theme_config);
// Honor instance-wide force color mode: when set, override the
// theme's own colorMode so legacy themes can't render light against
// a force-dark instance (or vice-versa).
const forced = settingsData.branding_force_color_mode;
const themeWithForce = forced
? { ...settingsData.theme_config, colorMode: forced }
: settingsData.theme_config;
setTheme(themeWithForce);
}
}, [settingsData, setTheme]);
+12 -9
View File
@@ -23,7 +23,7 @@ interface AdminHeaderProps {
export const AdminHeader: React.FC<AdminHeaderProps> = ({ onMenuClick }) => {
const navigate = useNavigate();
const { user, logout } = useAdminAuth();
const { isDark, toggle: toggleDarkMode } = useAdminDarkMode();
const { isDark, toggle: toggleDarkMode, forcedMode } = useAdminDarkMode();
const { t } = useTranslation();
const { format } = useLocalizedDate();
const { formatTimeAgo } = useLocalizedTimeAgo();
@@ -116,14 +116,17 @@ export const AdminHeader: React.FC<AdminHeaderProps> = ({ onMenuClick }) => {
{/* Language Selector */}
<LanguageSelector />
{/* Dark Mode Toggle */}
<button
onClick={toggleDarkMode}
className="p-2 text-neutral-500 dark:text-neutral-400 hover:text-neutral-700 dark:hover:text-neutral-200 hover:bg-neutral-100 dark:hover:bg-neutral-800 rounded-lg transition-colors"
title={isDark ? t('admin.lightMode', 'Switch to light mode') : t('admin.darkMode', 'Switch to dark mode')}
>
{isDark ? <Sun className="w-5 h-5" /> : <Moon className="w-5 h-5" />}
</button>
{/* Dark Mode Toggle — hidden entirely when an admin has locked
the instance to a specific mode via Branding > Force color mode. */}
{!forcedMode && (
<button
onClick={toggleDarkMode}
className="p-2 text-neutral-500 dark:text-neutral-400 hover:text-neutral-700 dark:hover:text-neutral-200 hover:bg-neutral-100 dark:hover:bg-neutral-800 rounded-lg transition-colors"
title={isDark ? t('admin.lightMode', 'Switch to light mode') : t('admin.darkMode', 'Switch to dark mode')}
>
{isDark ? <Sun className="w-5 h-5" /> : <Moon className="w-5 h-5" />}
</button>
)}
{/* Notifications */}
<div className="relative" ref={notificationRef}>
@@ -341,6 +341,13 @@ export const GalleryView: React.FC<GalleryViewProps> = ({ slug, event }) => {
themeToApply = settingsData.theme_config;
}
// Honor instance-wide force color mode (Branding > Force color mode).
// The branding-level lock wins over per-event themes so a force-dark
// instance never accidentally renders a light gallery (and vice-versa).
if (themeToApply && settingsData.branding_force_color_mode) {
themeToApply = { ...themeToApply, colorMode: settingsData.branding_force_color_mode };
}
// Apply theme with a small delay to ensure it overrides any global theme
if (themeToApply) {
// Use setTimeout to ensure this runs after any global theme application