Fix automatic readable text contrast for public and admin themes.
Local Build and Deploy / deploy (push) Successful in 55s

Derive admin/public text colors from WCAG contrast, unify ThemeVars CSS emission, and keep navbar overrides in sync with readable vars.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
SimoandCursor committed 2026-07-15 20:41:34 +02:00
1 parent 9d4d409b77
commit d7278c77f9
9 files changed
+205 -176

No files matched your search

+38 -22
View File
@@ -31,7 +31,7 @@ export function deriveAdminPalette(
colors: PublicThemeColors,
overrides?: Partial<AdminPalette>,
): AdminPalette {
const accent = colors.color_primary ?? "#f59e0b";
const accent = overrides?.accent ?? colors.color_primary ?? "#f59e0b";
const defaults: AdminPalette = {
canvas: "#0b0f19",
surface: "#151b27",
@@ -40,8 +40,8 @@ export function deriveAdminPalette(
muted: "#94a3b8",
border: "#2a3448",
accent,
accentText: readableColor(accent, ["#0b0f19", "#151b27", "#1e2635"]),
accentForeground: readableColor("#ffffff", [accent]),
accentText: "#f59e0b",
accentForeground: "#ffffff",
success: "#22c55e",
warning: "#eab308",
error: "#ef4444",
@@ -51,29 +51,35 @@ export function deriveAdminPalette(
overlay: "rgba(2, 6, 23, 0.72)",
focusRing: accent,
};
if (!overrides) return defaults;
return {
const merged: AdminPalette = {
...defaults,
...overrides,
accent,
accentText: readableColor(accent, [
overrides.canvas ?? defaults.canvas,
overrides.surface ?? defaults.surface,
overrides.surfaceElevated ?? defaults.surfaceElevated,
]),
accentForeground: readableColor("#ffffff", [accent]),
success: overrides.success ?? defaults.success,
warning: overrides.warning ?? defaults.warning,
error: overrides.error ?? defaults.error,
info: overrides.info ?? defaults.info,
sidebarText: readableColor(overrides.text ?? defaults.text, [
overrides.sidebar ?? defaults.sidebar,
overrides.canvas ?? defaults.canvas,
overrides.surface ?? defaults.surface,
]),
overlay: defaults.overlay,
overlay: overrides?.overlay ?? defaults.overlay,
focusRing: accent,
};
const surfaces = [
merged.canvas,
merged.surface,
merged.surfaceElevated,
] as const;
const preferredText = merged.text;
const preferredMuted = merged.muted;
return {
...merged,
text: readableColor(preferredText, surfaces),
muted: readableColor(preferredMuted, [merged.canvas, merged.surface]),
accentText: readableColor(accent, surfaces),
accentForeground: readableColor("#ffffff", [accent]),
sidebarText: readableColor(preferredText, [
merged.sidebar,
merged.canvas,
merged.surface,
]),
};
}
export function derivePublicForegrounds(c: PublicThemeColors) {
@@ -181,7 +187,17 @@ export function readableColor(
}
function parseHex(value: string): Rgb | null {
const match = /^#([0-9a-f]{6})$/i.exec(value.trim());
const trimmed = value.trim();
const short = /^#([0-9a-f]{3})$/i.exec(trimmed);
if (short) {
const [r, g, b] = short[1].split("");
return [
Number.parseInt(r + r, 16),
Number.parseInt(g + g, 16),
Number.parseInt(b + b, 16),
];
}
const match = /^#([0-9a-f]{6})$/i.exec(trimmed);
if (!match) return null;
return [
Number.parseInt(match[1].slice(0, 2), 16),