import type { ThemeCssVarMap, ThemeStyleConfig, ThemeStylePresetId } from './types'; import { THEME_STYLE_PRESETS } from './presets'; function mergeLayer(base: ThemeCssVarMap | undefined, over: ThemeCssVarMap | undefined): ThemeCssVarMap { return { ...base, ...over }; } /** * Tailwind v4 `rounded-*` utilities are backed by `--radius-*` scale variables. * When semantic `radius` is present, emit the derived scale too. */ function withTailwindRadiusScale(vars: ThemeCssVarMap): Array<[string, string]> { const entries = Object.entries(vars); const radius = vars.radius; if (!radius) return entries; const r = String(radius).trim(); if (!r) return entries; const scale: Array<[string, string]> = [ ['radius-xs', `calc(${r} - 6px)`], ['radius-sm', `calc(${r} - 4px)`], ['radius-md', `calc(${r} - 2px)`], ['radius-lg', r], ['radius-xl', `calc(${r} + 4px)`], ['radius-2xl', `calc(${r} + 8px)`], ['radius-3xl', `calc(${r} + 12px)`], ['radius-4xl', `calc(${r} + 16px)`], ]; return [...entries, ...scale]; } /** * Build a stylesheet fragment: preset → vars.light / vars.dark. * Inject this after globals (later rule wins). */ export function buildThemeStyleSheet(style?: ThemeStyleConfig): string { if (!style) return ''; const presetId: ThemeStylePresetId = style.preset ?? 'default'; const preset = THEME_STYLE_PRESETS[presetId] ?? THEME_STYLE_PRESETS.default; const light = mergeLayer(preset.light, style.vars?.light); const dark = mergeLayer(preset.dark, style.vars?.dark); const blocks: string[] = []; if (Object.keys(light).length > 0) { const body = withTailwindRadiusScale(light) .map(([k, v]) => ` --${k}: ${v};`) .join('\n'); blocks.push(`:root {\n${body}\n}`); } if (Object.keys(dark).length > 0) { const body = withTailwindRadiusScale(dark) .map(([k, v]) => ` --${k}: ${v};`) .join('\n'); blocks.push(`.dark {\n${body}\n}`); } return blocks.join('\n\n'); }