diff --git a/frontend-prep/src/context/ThemeContext.tsx b/frontend-prep/src/context/ThemeContext.tsx index ca50e7b4..1ed0e76a 100644 --- a/frontend-prep/src/context/ThemeContext.tsx +++ b/frontend-prep/src/context/ThemeContext.tsx @@ -1,4 +1,4 @@ -import { createContext, useContext, useEffect, useState } from "react"; +import { createContext, useContext, useEffect, useRef, useState } from "react"; import type { ReactNode } from "react"; import { getPublicSettings } from "../api/api"; @@ -22,12 +22,43 @@ function hexToRgba(hex: string, alpha: number): string { return `rgba(${r}, ${g}, ${b}, ${alpha})`; } -function applyClientColors(s: { client_color_primary: string; client_color_secondary: string; client_color_success: string; client_color_danger: string; client_color_warning: string }) { +// Mélange une couleur de teinte sur un fond noir +function tintOnBlack(tintHex: string, ratio: number): string { + const h = tintHex.replace("#", ""); + const r = Math.min(255, Math.round(parseInt(h.substring(0, 2), 16) * ratio)); + const g = Math.min(255, Math.round(parseInt(h.substring(2, 4), 16) * ratio)); + const b = Math.min(255, Math.round(parseInt(h.substring(4, 6), 16) * ratio)); + return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`; +} + +type ColorSettings = { + client_color_primary: string; + client_color_secondary: string; + client_color_success: string; + client_color_danger: string; + client_color_warning: string; +}; + +function applyClientColors(s: ColorSettings) { const root = document.documentElement; + const isDark = root.getAttribute("data-theme") !== "light"; + if (s.client_color_primary) { - root.style.setProperty("--primary", s.client_color_primary); - root.style.setProperty("--primary-soft", hexToRgba(s.client_color_primary, 0.12)); - root.style.setProperty("--primary-glow", hexToRgba(s.client_color_primary, 0.25)); + const p = s.client_color_primary; + root.style.setProperty("--primary", p); + root.style.setProperty("--primary-soft", hexToRgba(p, 0.12)); + root.style.setProperty("--primary-glow", hexToRgba(p, 0.25)); + if (isDark) { + // Teinter le fond avec la couleur primaire + root.style.setProperty("--bg", tintOnBlack(p, 0.20)); + root.style.setProperty("--surface", tintOnBlack(p, 0.25)); + root.style.setProperty("--surface-2", tintOnBlack(p, 0.32)); + } else { + // En mode clair, réinitialiser pour laisser le CSS prendre le contrôle + root.style.removeProperty("--bg"); + root.style.removeProperty("--surface"); + root.style.removeProperty("--surface-2"); + } } if (s.client_color_secondary) { root.style.setProperty("--cyan", s.client_color_secondary); @@ -44,16 +75,25 @@ export function ThemeProvider({ children }: { children: ReactNode }) { if (stored === "light" || stored === "dark") return stored; return "dark"; }); + const lastSettings = useRef(null); useEffect(() => { document.documentElement.setAttribute("data-theme", theme); localStorage.setItem("theme", theme); }, [theme]); + // Re-appliquer les couleurs quand le thème change (le tinting bg dépend du mode) + useEffect(() => { + if (lastSettings.current) { + applyClientColors(lastSettings.current); + } + }, [theme]); + useEffect(() => { const fetchColors = async () => { try { const settings = await getPublicSettings(); + lastSettings.current = settings; applyClientColors(settings); } catch { /* ignore */ } };