import type { CSSProperties } from "react"; export const DEFAULT_PROJECT_COLOR = "#4C8DFF"; export function validThemeColor(value: string | null | undefined): string | null { return value && /^#[0-9A-Fa-f]{6}$/.test(value) ? value.toUpperCase() : null; } function rgb(hex: string): [number, number, number] { return [1, 3, 5].map((index) => Number.parseInt(hex.slice(index, index + 2), 16)) as [ number, number, number, ]; } function luminance(hex: string): number { const channels = rgb(hex).map((channel) => { const value = channel / 255; return value <= 0.03928 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4; }); return channels[0] * 0.2126 + channels[1] * 0.7152 + channels[2] * 0.0722; } function contrast(a: string, b: string): number { const [light, dark] = [luminance(a), luminance(b)].sort((x, y) => y - x); return (light + 0.05) / (dark + 0.05); } /** Lightens very dark user colours until accent text remains WCAG-AA on canvas. */ export function accessibleProjectAccent(raw: string): string { const color = validThemeColor(raw) ?? DEFAULT_PROJECT_COLOR; const [r, g, b] = rgb(color); for (let amount = 0; amount <= 1; amount += 0.02) { const next = `#${[r, g, b] .map((channel) => Math.round(channel + (255 - channel) * amount).toString(16).padStart(2, "0")) .join("")}`; if (contrast(next, "#0E1116") >= 4.5) return next.toUpperCase(); } return "#FFFFFF"; } export function projectThemeStyle(themeColor: string | null | undefined): CSSProperties { const raw = validThemeColor(themeColor); if (!raw) return {}; const accent = accessibleProjectAccent(raw); return { "--project-theme-color": raw, "--color-primary": accent, "--color-primary-hover": `color-mix(in srgb, ${accent} 86%, white)`, "--color-on-primary": contrast(accent, "#0E1116") >= 4.5 ? "#0E1116" : "#FFFFFF", "--color-canvas": `color-mix(in srgb, #0e1116 96%, ${raw})`, "--color-surface": `color-mix(in srgb, #161b22 95%, ${raw})`, "--color-raised": `color-mix(in srgb, #1c2430 94%, ${raw})`, "--color-overlay": `color-mix(in srgb, #222c3a 94%, ${raw})`, "--color-border": `color-mix(in srgb, #2b3440 88%, ${accent})`, "--color-border-strong": accent, } as CSSProperties; }