import colors from "./colors.json"; import { hslToHex, hexToHsl, contrastRatio } from "./colors"; import { clamp } from "./math"; type Pallette = { bg: string; fg: string; red: string; green: string; shade1: string; shade2: string; shade3: string; shade4: string; }; const MIN_CONTRAST = 3.0; const FALLBACK_WHITE = "#ffffff"; const bg = colors.bg ?? "#0f0f10"; const fg = colors.fg ?? "#e6e6e6"; const redHue = colors.accentRedHue ?? 0; const greenHue = colors.accentGreenHue ?? 140; const sat = colors.accentSaturation ?? 65; const light = colors.accentLightness ?? 60; const steps = colors.shadeSteps ?? 4; // generate accents // ensure reasonable contrast with bg; if too low, nudge lightness const fixContrast = ( ref: string, hue: number, sat: number, lightness: number, ) => { let cur = hslToHex(hue, sat, lightness); let ctr = contrastRatio(cur, ref); let l = lightness; let tries = 0; while (ctr < MIN_CONTRAST && tries < 10) { l = clamp(l + 6, 0, 100); // make lighter cur = hslToHex(hue, sat, l); ctr = contrastRatio(cur, ref); tries++; } return cur; }; const red = fixContrast(bg, redHue, sat, light); const green = fixContrast(bg, greenHue, sat, light); // generate 4 darker shades from fg towards bg by lightness interpolation const { h: fgH, s: fgS, l: fgL } = hexToHsl(fg); const { s: bgS, l: bgL } = hexToHsl(bg); const shades: string[] = []; for (let i = 1; i <= steps; i++) { const t = i / (steps + 1); // fraction towards bg const l = fgL + (bgL - fgL) * t; const s = fgS + (bgS - fgS) * t; const h = fgH; // keep fg hue shades.push(hslToHex(h, s, l)); } const pallette: Pallette = { bg, fg, red, green, shade1: shades[0] ?? FALLBACK_WHITE, shade2: shades[1] ?? FALLBACK_WHITE, shade3: shades[2] ?? FALLBACK_WHITE, shade4: shades[3] ?? FALLBACK_WHITE, }; function* createPalletteGenerator() { const keys = Object.keys(pallette) as (keyof Pallette)[]; for (let key of keys) { yield [key, pallette[key]]; } } const palletteGenerator = createPalletteGenerator(); export { pallette, palletteGenerator }; export type { Pallette };