You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/web/routes/temas/estudio/_lib/theme-draft.ts

105 lines
4.2 KiB

/**
* Theme studio — the `ThemeDraft` model + its application glue.
*
* A `ThemeDraft` is a superset of the framework's `ThemeSeed`: the six
* perceptual axes `applyTheme` composes (color · type · shape · depth · space ·
* gradient) PLUS the non-seed knobs a theme author still wants — font families,
* global roundness, and a raw escape hatch for advanced tokens (focus-ring /
* state-layer / border widths …).
*
* It maps onto the three application mechanisms `ActiveEidos` already exposes,
* which touch DISJOINT token sets (no cascade conflict):
*
* - `seed` → `eidos.applyTheme(seed, { selector })`
* - `fonts` · `radiusFactor` · `advanced`
* → `eidos.setCssVariables(map, { selector, strict: false })`
* - structure (mode / density / scaling) is driven by the page's reactive
* sources, NOT here — it lives on `data-*` attrs / `modeSource`.
*
* Route-level glue (single consumer, per the "ruta-herramienta" decision). If a
* reusable `<ThemeBuilder>` is extracted later, this graduates to `src/` with a
* unit test; today its only real risk is DOM integration, verified live in the
* studio preview.
*/
import type { ActiveEidos, ThemeSeed } from '$uix/eidos'
import type { EidosCssVariableMap } from '$uix/eidos/lib/config-types'
export type ThemeMode = 'light' | 'dark'
/** Font-family overrides → `--font-family-{key}`. */
export interface ThemeFonts {
readonly primary?: string
readonly display?: string
readonly mono?: string
}
/** The CSS selector the whole draft is scoped to (the preview region). */
export const PREVIEW_SELECTOR = '[data-estudio-preview]'
/**
* Everything the studio edits. The `seed` axes go through `applyTheme`; the
* rest are token overrides through `setCssVariables`.
*/
export interface ThemeDraft {
readonly seed: ThemeSeed
readonly fonts?: ThemeFonts
/** Global roundness knob → `--radius-factor` (foundation default 1). */
readonly radiusFactor?: number
/** Raw token → value escape hatch (focus-ring, state-layer, border, opacity…). */
readonly advanced?: Readonly<Record<string, string>>
}
/** Map the non-seed axes of a draft to a `--token` map for `setCssVariables`. */
export function draftToVariables(draft: ThemeDraft): EidosCssVariableMap {
const out: Record<string, string> = {}
if (draft.fonts?.primary) out['--font-family-primary'] = draft.fonts.primary
if (draft.fonts?.display) out['--font-family-display'] = draft.fonts.display
if (draft.fonts?.mono) out['--font-family-mono'] = draft.fonts.mono
if (draft.radiusFactor !== undefined) out['--radius-factor'] = String(draft.radiusFactor)
if (draft.advanced) Object.assign(out, draft.advanced)
return out
}
export interface ApplyDraftOptions {
readonly selector?: string
/** Force the color axis donor mode (light/dark) for the scoped scheme. */
readonly mode?: ThemeMode
}
/**
* Apply a whole draft to the (scoped) preview: the seed via `applyTheme`, the
* font / radius / advanced overrides via `setCssVariables`. Both target the same
* selector so the preview region — and nothing else — re-skins.
*/
export function applyDraft(
eidos: ActiveEidos,
draft: ThemeDraft,
options: ApplyDraftOptions = {}
): void {
const selector = options.selector ?? PREVIEW_SELECTOR
eidos.applyTheme(draft.seed, {
selector,
color: options.mode ? { mode: options.mode } : undefined
})
// `strict: false` — some knobs (`--radius-factor`) and every advanced token
// (`--state-*`, hand-written in archetypes.css) live outside the generated
// contract, so a contract-validated write would reject them.
eidos.setCssVariables(draftToVariables(draft), { selector, strict: false })
}
/** Revert every mechanism the draft touched. */
export function clearDraft(eidos: ActiveEidos): void {
eidos.clearTheme()
eidos.clearCssVariables()
}
/** A copy-pasteable `applyTheme(…)` (+ overrides) snippet for the current draft. */
export function draftToCode(draft: ThemeDraft): string {
let code = `eidos.applyTheme(${JSON.stringify(draft.seed, null, 2)})`
const vars = draftToVariables(draft)
if (Object.keys(vars).length > 0) {
code += `\n\neidos.setCssVariables(\n ${JSON.stringify(vars, null, 2).replace(/\n/g, '\n ')},\n { strict: false }\n)`
}
return code
}

Powered by TurnKey Linux.