2026-10-16 09:35:39 +03:00
|
|
|
import { Theme } from "@mancala/core";
|
|
|
|
|
|
|
|
|
|
// The web app's themes. Pages are coloured by the CSS variables of the theme's class (styles/app.css); the colours
|
|
|
|
|
// here are for code that still reads core's Theme, such as PitAnimator.
|
|
|
|
|
export type WebTheme = Theme & {
|
|
|
|
|
// CSS class on <html>.
|
|
|
|
|
className: string;
|
|
|
|
|
// Translation key of the theme's name.
|
|
|
|
|
labelKey: string;
|
|
|
|
|
// Colours of the theme's swatch.
|
|
|
|
|
swatch: { board: string, accent: string };
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const walnut: WebTheme = {
|
|
|
|
|
id: "ceviz",
|
|
|
|
|
name: "Ceviz",
|
|
|
|
|
className: "t-ceviz",
|
|
|
|
|
labelKey: "ThemeWalnut",
|
|
|
|
|
swatch: { board: "#6e4a30", accent: "#f2b84b" },
|
|
|
|
|
themePreviewColor: "#6e4a30",
|
|
|
|
|
background: "#1f1712",
|
|
|
|
|
appBarBgColor: "#2a2019",
|
|
|
|
|
textColor: "#2a1a0a",
|
|
|
|
|
textLightColor: "#f6ead8",
|
|
|
|
|
playerTurnColor: "#f2b84b",
|
|
|
|
|
boardColor: "#6e4a30",
|
|
|
|
|
pitColor: "#2c1d13",
|
|
|
|
|
pitSelectedColor: "#f2b84b",
|
|
|
|
|
stoneColor: "#e39a32",
|
|
|
|
|
stoneLightColor: "#fff1c9",
|
|
|
|
|
pitGameMoveAnimateColor: "#f2b84b",
|
|
|
|
|
pitEmptyPitAnimateColor: "#ff8a73",
|
|
|
|
|
pitLastStoneInBankPitAnimateColor: "#f2b84b",
|
|
|
|
|
pitGetRivalStonePitAnimateColor: "#ff8a73",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const night: WebTheme = {
|
|
|
|
|
id: "gece",
|
|
|
|
|
name: "Gece",
|
|
|
|
|
className: "t-gece",
|
|
|
|
|
labelKey: "ThemeNight",
|
|
|
|
|
swatch: { board: "#1d2a4d", accent: "#e2b36a" },
|
|
|
|
|
themePreviewColor: "#1d2a4d",
|
|
|
|
|
background: "#0c1220",
|
|
|
|
|
appBarBgColor: "#141c30",
|
|
|
|
|
textColor: "#241707",
|
|
|
|
|
textLightColor: "#eef1f8",
|
|
|
|
|
playerTurnColor: "#e2b36a",
|
|
|
|
|
boardColor: "#1d2a4d",
|
|
|
|
|
pitColor: "#0b1020",
|
|
|
|
|
pitSelectedColor: "#e2b36a",
|
|
|
|
|
stoneColor: "#dfe3ee",
|
|
|
|
|
stoneLightColor: "#ffffff",
|
|
|
|
|
pitGameMoveAnimateColor: "#e2b36a",
|
|
|
|
|
pitEmptyPitAnimateColor: "#ff8a7a",
|
|
|
|
|
pitLastStoneInBankPitAnimateColor: "#e2b36a",
|
|
|
|
|
pitGetRivalStonePitAnimateColor: "#ff8a7a",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const jade: WebTheme = {
|
|
|
|
|
id: "yesim",
|
|
|
|
|
name: "Yeşim",
|
|
|
|
|
className: "t-yesim",
|
|
|
|
|
labelKey: "ThemeJade",
|
|
|
|
|
swatch: { board: "#cfd2c3", accent: "#2a7650" },
|
|
|
|
|
themePreviewColor: "#cfd2c3",
|
|
|
|
|
background: "#e9ebe3",
|
|
|
|
|
appBarBgColor: "#f7f8f3",
|
|
|
|
|
textColor: "#1d2721",
|
|
|
|
|
textLightColor: "#ffffff",
|
|
|
|
|
playerTurnColor: "#2a7650",
|
|
|
|
|
boardColor: "#cfd2c3",
|
|
|
|
|
pitColor: "#c3c7b6",
|
|
|
|
|
pitSelectedColor: "#2a7650",
|
|
|
|
|
stoneColor: "#3f9f6f",
|
|
|
|
|
stoneLightColor: "#e2ffee",
|
|
|
|
|
pitGameMoveAnimateColor: "#2a7650",
|
|
|
|
|
pitEmptyPitAnimateColor: "#b43a26",
|
|
|
|
|
pitLastStoneInBankPitAnimateColor: "#2a7650",
|
|
|
|
|
pitGetRivalStonePitAnimateColor: "#b43a26",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// The first one is the default.
|
|
|
|
|
export const webThemes: WebTheme[] = [walnut, night, jade];
|
|
|
|
|
|
|
|
|
|
export function getWebTheme(theme: Theme): WebTheme {
|
|
|
|
|
return webThemes.find((webTheme) => webTheme.id === theme.id) ?? walnut;
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-17 02:40:07 +03:00
|
|
|
// Puts the theme's class on <html> and remembers the theme for the login pages.
|
2026-10-16 09:35:39 +03:00
|
|
|
export function applyThemeClass(theme: Theme) {
|
|
|
|
|
const root = document.documentElement;
|
|
|
|
|
webThemes.forEach((webTheme) => root.classList.remove(webTheme.className));
|
|
|
|
|
root.classList.add(getWebTheme(theme).className);
|
2026-10-17 02:40:07 +03:00
|
|
|
saveThemeCookie(theme);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const THEME_COOKIE = "mancala_theme";
|
|
|
|
|
const COOKIE_MAX_AGE_SECONDS = 365 * 24 * 60 * 60;
|
|
|
|
|
|
|
|
|
|
// The Keycloak login pages are on another origin and read the theme from this cookie (docs/specs/theme-polish).
|
|
|
|
|
// On localhost a host-only cookie reaches every port; on a domain the cookie is set for its last two labels, so
|
|
|
|
|
// playmancala.app shares it with auth.playmancala.app.
|
|
|
|
|
export function saveThemeCookie(theme: Theme) {
|
|
|
|
|
const { hostname, protocol } = window.location;
|
|
|
|
|
const isDomain = hostname.includes(".") && !/^[\d.]+$/.test(hostname);
|
|
|
|
|
const domain = isDomain ? `; domain=${hostname.split(".").slice(-2).join(".")}` : "";
|
|
|
|
|
const secure = protocol === "https:" ? "; Secure" : "";
|
|
|
|
|
document.cookie = `${THEME_COOKIE}=${getWebTheme(theme).id}; path=/; max-age=${COOKIE_MAX_AGE_SECONDS}; SameSite=Lax${domain}${secure}`;
|
2026-10-16 09:35:39 +03:00
|
|
|
}
|