Hey folks, I’m working on a white-label UI where product keeps asking for “just one more theme,” and I’m trying to avoid shipping 12 hand-tuned palettes that drift over time. The failure mode I keep hitting is gradients that look nice but end up with unreadable text on one stop.
const clamp = (n, a, b) => Math.min(b, Math.max(a, n));
export function themeFromHue(hue, mode = "light") {
const bgL = mode === "light" ? 97 : 10;
const fgL = mode === "light" ? 12 : 92;
const bg = `hsl(${hue} 25% ${bgL}%)`;
const fg = `hsl(${hue} 15% ${fgL}%)`;
const accentS = mode === "light" ? 75 : 70;
const accentL = mode === "light" ? 45 : 60;
const accent = `hsl(${hue} ${accentS}% ${accentL}%)`;
const accent2 = `hsl(${(hue + 35) % 360} ${clamp(accentS - 10, 0, 100)}% ${clamp(accentL + (mode === "light" ? 8 : -8), 0, 100)}%)`;
const gradient = `linear-gradient(135deg, ${accent}, ${accent2})`;
return { bg, fg, accent, accent2, gradient };
}
Neat part is it gives design a “palette knob” (hue) while keeping the rest predictable, so the product tradeoff is fewer bespoke themes vs slightly less brand-perfect colors on edge cases.