packages/widget/src/utils/generateTheme.ts
import { CustomThemeVariables } from 'types'
export function generateTheme(theme: CustomThemeVariables = {}) {
function str2hsl(color: string) {
// TODO: Support hex short codes
if (/^#?[\da-fA-F]{6,8}$/.test(color)) {
let [r, g, b, a] = color
.match(/[\da-fA-F]{2}/g)
.map((a) => parseInt(a, 16) / 255)
return rgb2hsl({ r, g, b, a })
} else if (/^rgb/.test(color)) {
let [r, g, b, a] = color.match(/\d+/g).map((a) => +a / 255)
return rgb2hsl({ r, g, b, a })
} else if (/^hsl/.test(color)) {
let [h, s, l, a] = color.match(/\d+/g).map((a) => +a)
return { h, s, l, a }
} else {
return color === 'dark'
? { h: 0, s: 0, l: 0, a: 100 }
: { h: 0, s: 0, l: 100, a: 100 }
}
}
function rgb2hsl({ r, g, b, a = 1 }: any) {
// in: r,g,b in [0,1], out: h in [0,360) and s,l in [0,100] // https://stackoverflow.com/a/54071699
let v = Math.max(r, g, b),
c = v - Math.min(r, g, b),
f = 1 - Math.abs(v + v - c - 1)
let h =
c && (v === r ? (g - b) / c : v === g ? 2 + (b - r) / c : 4 + (r - g) / c)
return {
h: 60 * (h < 0 ? h + 6 : h),
s: f ? (100 * c) / f : 0,
l: (100 * (v + v - c)) / 2,
a: 100 * a,
}
}
const hslString = (h: number, s: number, l: number, a: number, x: number) =>
colorMode === 'dark' || l < 50
? `hsl(${h}deg ${(s * l) / 50 + x * 10}% ${x * 100 + l * x}%)`
: `hsl(${h}deg ${s}% ${Math.min(100, l * (1 + x)) * x}%)`
const colorMode = theme.bgColor === 'dark' ? 'dark' : 'light'
const { h, s, l, a } = str2hsl(theme.bgColor)
const generatedTheme =
colorMode === 'dark' || l < 50
? {
'--synapse-text': hslString(h, s, l, a, 0.96),
'--synapse-secondary': hslString(h, s / 2, l, a, 0.6),
'--synapse-border': hslString(h, s, l, a, 0.12),
'--synapse-object': hslString(h, s, l, a, 0.25),
'--synapse-surface': hslString(h, s, l, a, 0.13),
'--synapse-root': hslString(h, s, l, a, 0.08),
'--synapse-focus': 'var(--synapse-secondary)',
'--synapse-select-bg': 'var(--synapse-object)',
'--synapse-select-text': 'var(--synapse-text)',
'--synapse-select-border': 'var(--synapse-object)',
'--synapse-button-bg': 'var(--synapse-surface)',
'--synapse-button-text': 'var(--synapse-text)',
'--synapse-button-border': 'var(--synapse-border)',
'--synapse-progress': 'hsl(265deg 100% 75%)',
'--synapse-progress-flash': 'hsl(185deg 100% 45%)',
'--synapse-progress-success': 'hsl(105deg 100% 60%)',
'--synapse-progress-error': 'hsl(15deg 100% 65%)',
}
: {
'--synapse-text': hslString(h, s, l, a, 0.04),
'--synapse-secondary': hslString(h, s / 2, l, a, 0.41),
'--synapse-border': hslString(h, s, l, a, 0.86),
'--synapse-object': hslString(h, s, l, a, 0.5),
'--synapse-surface': hslString(h, s, l, a, 1.0),
'--synapse-root': hslString(h, s, l, a, 0.96),
'--synapse-focus': 'var(--synapse-secondary)',
'--synapse-select-bg': 'var(--synapse-root)',
'--synapse-select-text': 'var(--synapse-text)',
'--synapse-select-border': 'var(--synapse-border)',
'--synapse-button-bg': 'var(--synapse-surface)',
'--synapse-button-text': 'var(--synapse-text)',
'--synapse-button-border': 'var(--synapse-border)',
'--synapse-progress': 'hsl(265deg 100% 65%)',
'--synapse-progress-flash': 'hsl(215deg 100% 65%)',
'--synapse-progress-success': 'hsl(120deg 100% 30%)',
'--synapse-progress-error': 'hsl(15deg 100% 65%)',
}
for (const key in theme) if (/^--/.test(key)) generatedTheme[key] = theme[key]
return generatedTheme as React.CSSProperties
}