Files
famdone/frontend/src/lib/theme.ts
T
2026-09-10 13:23:55 +01:00

62 lines
2.4 KiB
TypeScript

// Theme helpers — derive subtle pattern shades from a user's chosen color
// swatch. Patterns in theme-patterns.css read --c1..--c4; we override them
// with hsl variations of the user's hue so the background stays subtle.
export function hexToHsl(hex: string): { h: number; s: number; l: number } {
const m = /^#?([0-9a-f]{6})$/i.exec(hex.trim());
if (!m) return { h: 243, s: 75, l: 66 }; // indigo fallback
const n = parseInt(m[1], 16);
const r = ((n >> 16) & 255) / 255;
const g = ((n >> 8) & 255) / 255;
const b = (n & 255) / 255;
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
const l = (max + min) / 2;
if (max === min) return { h: 0, s: 0, l: Math.round(l * 100) };
const d = max - min;
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
let h = 0;
if (max === r) h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
else if (max === g) h = ((b - r) / d + 2) / 6;
else h = ((r - g) / d + 4) / 6;
return { h: Math.round(h * 360), s: Math.round(s * 100), l: Math.round(l * 100) };
}
// Four subtle shades built on the swatch's hue — pastel tints distinct
// enough that the pattern motif reads against the near-white app shell,
// but soft enough to stay a background wash rather than a loud print.
// `alpha` (0..1) fades the whole motif toward the shell background — the
// app-shell keeps `background: whitesmoke` underneath, so lowering it
// blends the pattern out.
export function themeShades(
hex: string,
alpha = 1
): { c1: string; c2: string; c3: string; c4: string } {
const { h } = hexToHsl(hex);
return {
c1: `hsl(${h} 45% 93% / ${alpha})`,
c2: `hsl(${h} 50% 85% / ${alpha})`,
c3: `hsl(${h} 55% 74% / ${alpha})`,
c4: `hsl(${(h + 40) % 360} 50% 87% / ${alpha})`
};
}
// Pattern ids: 'p1'..'p60', '' = none. Currently showing the first 19;
// bump this to include more (the CSS file still ships all 60).
export const PATTERN_COUNT = 19;
export function patternId(n: number): string {
return `p${n}`;
}
// Pattern size slider — the value is the pattern's `--s` in vw units, so the
// motif scales with the viewport. E.g. 7 at a 1400px screen ≈ 98px tiles.
export const THEME_SIZE_MIN = 2;
export const THEME_SIZE_MAX = 16;
export const THEME_SIZE_DEFAULT = 7;
// Pattern opacity slider — 1 = current full visibility, down to a faint hint.
export const THEME_OPACITY_MIN = 0.05;
export const THEME_OPACITY_MAX = 1;
export const THEME_OPACITY_STEP = 0.05;
export const THEME_OPACITY_DEFAULT = 1;