From 662c5f1b7e66c061e2fae845783e5b1027108984 Mon Sep 17 00:00:00 2001 From: arnef Date: Thu, 10 Sep 2026 18:59:05 +0200 Subject: [PATCH] feat: add customizable accent color --- apps/frontend/index.html | 14 +++++ apps/frontend/src/pages/Settings.tsx | 73 ++++++++++++++++++++++++++ apps/frontend/src/stores/themeStore.ts | 60 +++++++++++++++++++++ 3 files changed, 147 insertions(+) diff --git a/apps/frontend/index.html b/apps/frontend/index.html index 00cbd67..ef055dd 100644 --- a/apps/frontend/index.html +++ b/apps/frontend/index.html @@ -24,6 +24,20 @@ var root = document.documentElement; root.classList.toggle("dark", dark); root.style.colorScheme = dark ? "dark" : "light"; + + var accent = localStorage.getItem("accent"); + if (accent && /^#[0-9a-fA-F]{6}$/.test(accent)) { + var hex = accent.slice(1); + var r = parseInt(hex.slice(0, 2), 16) / 255; + var g = parseInt(hex.slice(2, 4), 16) / 255; + var b = parseInt(hex.slice(4, 6), 16) / 255; + var lin = function (c) { + return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4); + }; + var lum = 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b); + root.style.setProperty("--c-accent", accent); + root.style.setProperty("--c-accent-contrast", lum > 0.45 ? "#0b1220" : "#ffffff"); + } } catch (e) { /* ignore */ } diff --git a/apps/frontend/src/pages/Settings.tsx b/apps/frontend/src/pages/Settings.tsx index 7ceacae..adbc455 100644 --- a/apps/frontend/src/pages/Settings.tsx +++ b/apps/frontend/src/pages/Settings.tsx @@ -7,6 +7,17 @@ const MODES: { value: ThemeMode; label: string; icon: string }[] = [ { value: "auto", label: "Auto", icon: "auto" }, ]; +const PRESETS = [ + "#047857", + "#2563eb", + "#e11d48", + "#d97706", + "#7c3aed", + "#0284c7", + "#16a34a", + "#db2777", +]; + function Icon({ name }: { name: string }) { const common = { class: "h-4 w-4", @@ -43,6 +54,7 @@ function Icon({ name }: { name: string }) { export function SettingsPage() { const current = () => themeStore.mode; + const customAccent = () => themeStore.accent; return (
@@ -86,6 +98,67 @@ export function SettingsPage() { +
+
+

Akzentfarbe

+

+ Wähle die Hauptfarbe der App. +

+
+ +
+ + + + {(color) => ( + + )} + +
+ +
+ Eigene: + + +
+
+

Kickern – deine Kicker-Liga auf einen Blick. diff --git a/apps/frontend/src/stores/themeStore.ts b/apps/frontend/src/stores/themeStore.ts index e280383..bd45927 100644 --- a/apps/frontend/src/stores/themeStore.ts +++ b/apps/frontend/src/stores/themeStore.ts @@ -3,6 +3,7 @@ import { createSignal, createEffect } from "solid-js"; export type ThemeMode = "light" | "dark" | "auto"; const STORAGE_KEY = "theme"; +const ACCENT_KEY = "accent"; const media = window.matchMedia("(prefers-color-scheme: dark)"); const root = document.documentElement; @@ -46,6 +47,59 @@ createEffect(() => { apply(mode); }); +export type AccentColor = string | null; + +function readableText(color: string): string { + const hex = color.replace("#", ""); + const full = + hex.length === 3 + ? hex + .split("") + .map((c) => c + c) + .join("") + : hex; + const r = parseInt(full.slice(0, 2), 16) / 255; + const g = parseInt(full.slice(2, 4), 16) / 255; + const b = parseInt(full.slice(4, 6), 16) / 255; + const lin = (c: number) => (c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4); + const lum = 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b); + return lum > 0.45 ? "#0b1220" : "#ffffff"; +} + +function readAccent(): string | null { + try { + const raw = localStorage.getItem(ACCENT_KEY); + if (raw && /^#[0-9a-fA-F]{6}$/.test(raw)) return raw; + } catch { + /* ignore */ + } + return null; +} + +const [getAccent, setAccentSignal] = createSignal(readAccent()); + +// Apply a custom accent (or fall back to the theme token) + readable contrast. +function applyAccent(color: string | null) { + if (color) { + root.style.setProperty("--c-accent", color); + root.style.setProperty("--c-accent-contrast", readableText(color)); + } else { + root.style.removeProperty("--c-accent"); + root.style.removeProperty("--c-accent-contrast"); + } +} + +createEffect(() => { + const color = getAccent(); + try { + if (color) localStorage.setItem(ACCENT_KEY, color); + else localStorage.removeItem(ACCENT_KEY); + } catch { + /* ignore */ + } + applyAccent(color); +}); + export const themeStore = { get mode() { return getMode(); @@ -54,4 +108,10 @@ export const themeStore = { setModeSignal(mode); }, isDark: () => isDark(getMode()), + get accent() { + return getAccent(); + }, + setAccent(color: string | null) { + setAccentSignal(color); + }, };