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

+63 -7
View File
@@ -1,4 +1,66 @@
(() => {
function parseHex(value) {
const trimmed = String(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),
Number.parseInt(match[1].slice(2, 4), 16),
Number.parseInt(match[1].slice(4, 6), 16),
];
}
function relativeLuminance(rgb) {
const channels = rgb.map((channel) => {
const value = channel / 255;
return value <= 0.04045
? value / 12.92
: ((value + 0.055) / 1.055) ** 2.4;
});
return 0.2126 * channels[0] + 0.7152 * channels[1] + 0.0722 * channels[2];
}
function contrastRatio(foreground, background) {
const fg = parseHex(foreground);
const bg = parseHex(background);
if (!fg || !bg) return null;
const light = Math.max(relativeLuminance(fg), relativeLuminance(bg));
const dark = Math.min(relativeLuminance(fg), relativeLuminance(bg));
return (light + 0.05) / (dark + 0.05);
}
function readableColor(preferred, background) {
const preferredRatio = contrastRatio(preferred, background);
if (preferredRatio != null && preferredRatio >= 4.5) return preferred;
const black = contrastRatio("#000000", background) ?? 0;
const white = contrastRatio("#ffffff", background) ?? 0;
return white >= black ? "#ffffff" : "#000000";
}
function applyNavbarColors(navbarColor, navbarTextColor) {
const root = document.documentElement.style;
if (navbarColor) root.setProperty("--color-navbar", navbarColor);
const text =
navbarTextColor ||
(navbarColor ? readableColor("#1e293b", navbarColor) : null);
if (text) {
root.setProperty("--color-navbar-text", text);
root.setProperty("--color-navbar-text-readable", text);
} else if (navbarColor) {
const readable = readableColor("#ffffff", navbarColor);
root.setProperty("--color-navbar-text-readable", readable);
}
}
let theme = null;
let defaultDark = false;
let navbarColor = null;
@@ -12,11 +74,5 @@
} catch (_e) {}
if (theme === "dark" || (!theme && defaultDark))
document.documentElement.classList.add("dark");
if (navbarColor)
document.documentElement.style.setProperty("--color-navbar", navbarColor);
if (navbarTextColor)
document.documentElement.style.setProperty(
"--color-navbar-text",
navbarTextColor,
);
applyNavbarColors(navbarColor, navbarTextColor);
})();