62 lines
2.4 KiB
TypeScript
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;
|