import type { WidgetTheme } from "./public-contracts.js"; /** Applies only validated theme tokens to a single isolated surface. */ export function applyWidgetTheme( target: HTMLElement, theme: WidgetTheme, ): void { const [firstColor, secondColor, thirdColor] = fillBorderColors( theme.borderColors, ); target.style.setProperty("--akintu-border-color-1", firstColor); target.style.setProperty("--akintu-border-color-2", secondColor); target.style.setProperty("--akintu-border-color-3", thirdColor); target.style.setProperty("--akintu-button-color", theme.buttonColor); target.style.setProperty( "--akintu-button-text", getContrastingTextColor(theme.buttonColor), ); target.style.setProperty("--akintu-primary-color", theme.primaryColor); target.style.setProperty( "--akintu-primary-text", getContrastingTextColor(theme.primaryColor), ); target.style.setProperty( "--akintu-primary-soft", mixColorWithWhite(theme.primaryColor, 0.08), ); target.style.setProperty( "--akintu-primary-border", mixColorWithWhite(theme.primaryColor, 0.3), ); } function fillBorderColors(colors: string[]): [string, string, string] { if (colors.length === 1) return [colors[0]!, colors[0]!, colors[0]!]; if (colors.length === 2) return [colors[0]!, colors[1]!, colors[0]!]; return [colors[0]!, colors[1]!, colors[2]!]; } function getContrastingTextColor(hexColor: string): "#07111f" | "#ffffff" { const red = Number.parseInt(hexColor.slice(1, 3), 16); const green = Number.parseInt(hexColor.slice(3, 5), 16); const blue = Number.parseInt(hexColor.slice(5, 7), 16); const luminance = (red * 299 + green * 587 + blue * 114) / 1000; return luminance >= 150 ? "#07111f" : "#ffffff"; } function mixColorWithWhite(hexColor: string, colorWeight: number): string { const channels = [1, 3, 5].map((offset) => Math.round( Number.parseInt(hexColor.slice(offset, offset + 2), 16) * colorWeight + 255 * (1 - colorWeight), ), ); return `#${channels.map((channel) => channel.toString(16).padStart(2, "0")).join("")}`; }