since3-portal/modules/templates/lib/theme.ts
2026-10-10 02:38:50 +02:00

64 lines
2.5 KiB
TypeScript

import type { CSSProperties } from "react"
import { getTemplate, templates } from "./registry"
import type { Palette, TemplateContent, TemplateDefinition } from "./types"
// Bibliothek: Jede Variante darf Paletten und Schriftpaare aller Vorlagen nutzen ("vorlage:schlüssel")
export const paletteLibrary = templates.flatMap((template) =>
template.palettes.map((palette) => ({ key: `${template.key}:${palette.key}`, template: template.name, palette }))
)
export const fontLibrary = templates.flatMap((template) =>
template.fonts.map((pair) => ({ key: `${template.key}:${pair.key}`, template: template.name, label: pair.label }))
)
export function resolvePalette(def: TemplateDefinition, key: string): Palette {
const own = def.palettes.find((palette) => palette.key === key)
if (own) return own
const [templateKey, paletteKey] = key.split(":")
return getTemplate(templateKey)?.palettes.find((palette) => palette.key === paletteKey) ?? def.palettes[0]
}
function luminance(hex: string) {
const [r, g, b] = [1, 3, 5].map((i) => {
const channel = parseInt(hex.slice(i, i + 2), 16) / 255
return channel <= 0.03928 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4
})
return 0.2126 * r + 0.7152 * g + 0.0722 * b
}
// Schwarz oder Weiß, je nachdem was auf der Farbe besser lesbar ist (WCAG-Luminanz)
const readableOn = (hex: string) => (luminance(hex) > 0.179 ? "#111111" : "#ffffff")
export function themeColors(def: TemplateDefinition, content: TemplateContent) {
const palette = resolvePalette(def, content.palette)
const { colors } = content
const bg = colors.bg || palette.bg
return {
bg,
fg: colors.fg || palette.fg,
muted: colors.muted || palette.muted,
surface: colors.surface || palette.surface,
accent: colors.accent || palette.accent,
dark: colors.bg ? luminance(bg) < 0.2 : Boolean(palette.dark),
}
}
export function accentColors(def: TemplateDefinition, content: TemplateContent) {
const { accent } = themeColors(def, content)
return { accent, accentFg: readableOn(accent) }
}
export function themeStyle(def: TemplateDefinition, content: TemplateContent): CSSProperties {
const theme = themeColors(def, content)
return {
"--t-bg": theme.bg,
"--t-fg": theme.fg,
"--t-muted": theme.muted,
"--t-surface": theme.surface,
"--t-accent": theme.accent,
"--t-accent-fg": readableOn(theme.accent),
colorScheme: theme.dark ? "dark" : "light",
} as CSSProperties
}