diff --git a/continue.md b/continue.md index dbf9964bb..1283d3eef 100644 --- a/continue.md +++ b/continue.md @@ -109,9 +109,11 @@ a su `portalTo`. No usar nombres como `somaPortalTo` en `ActiveUix`. 6. `contracts/` ya no se importa desde `src/uix/eidos/index.css` ni desde `src/uix/eidos/tokens/index.css`; queda como archivo de consulta/tooling hasta decidir si se borra. -7. Pendiente: retirar `tokens/` o `themes/base/` del entrypoint estatico cuando - las recipes ya no dependan de aliases historicos. -8. No reintroducir engine visual separado, carpeta `engine`, ni wrapper `Eidos` sin +7. `themes/base/` ya no se importa desde el entrypoint estatico; los aliases + necesarios para las recipes viven en el CSS generado. +8. Pendiente: retirar `tokens/` del entrypoint estatico cuando las recipes ya + no dependan de aliases historicos. +9. No reintroducir engine visual separado, carpeta `engine`, ni wrapper `Eidos` sin contrato propio. ## Pendiente de limpieza diff --git a/src/uix/active_architecture.md b/src/uix/active_architecture.md index 9f277aed4..4a3adefc1 100644 --- a/src/uix/active_architecture.md +++ b/src/uix/active_architecture.md @@ -395,7 +395,7 @@ src/uix/eidos/ ├── generated/base.css foundation CSS generado desde EidosConfig base ├── contracts/ CSS contract archivado; no import runtime ├── tokens/ valores CSS legacy hasta generarlos desde ActiveEidos -├── themes/base/ CSS legacy de base hasta sustituirlo por salida generada +├── themes/base/ CSS base archivado; no import runtime ├── lib/ soporte de config, contrato CSS, tokens y tipos compartidos └── components/{x}/ recipe + wrapper Svelte + tipos por componente ├── {x}.css recipe (selectores [data-{x}], variants) @@ -833,10 +833,9 @@ Para evitar mission creep, conviene fijar lo que UIX **no quiere ser**: **Pendiente**: -- Sustituir progresivamente `tokens/` y `themes/base/`. El primer artefacto - generado ya existe en `src/uix/eidos/generated/base.css`; `contracts/` ya no - se importa en runtime ni desde `tokens/index.css` y queda como archivo de - consulta/tooling. +- Sustituir progresivamente `tokens/`. El primer artefacto generado ya existe + en `src/uix/eidos/generated/base.css`; `contracts/` y `themes/base/` ya no se + importan en runtime y quedan como archivos de consulta/tooling. - Reparar demos/rutas y componentes eidos que sigan en una mezcla historica de `Provider`/flat snippets para que todo compile contra la opcion C disciplinada. diff --git a/src/uix/auditoria-kimi.md b/src/uix/auditoria-kimi.md index 085e6efb4..8a54a6895 100644 --- a/src/uix/auditoria-kimi.md +++ b/src/uix/auditoria-kimi.md @@ -329,9 +329,9 @@ y de theme desde la configuracion viva. Mantenerlos: `src/uix/eidos/generated/base.css`, producido con `npm run generate:eidos-css` desde `EidosConfig` base. `contracts/` ya no se importa desde `index.css`; ni desde `tokens/index.css`; queda como archivo de consulta/tooling. Aun no se -borran `tokens/` ni `themes/base/` porque varias recipes y aliases historicos -dependen de ellos. El siguiente paso es separar recipes estables de aliases -retirables. +borran `tokens/` ni `themes/base/`, pero `themes/base/` tampoco se importa ya +en runtime: sus aliases necesarios viven ahora en el CSS generado. El siguiente +paso es separar recipes estables de aliases retirables en `tokens/`. --- diff --git a/src/uix/eidos/README.md b/src/uix/eidos/README.md index dbb6c296d..4714863ca 100644 --- a/src/uix/eidos/README.md +++ b/src/uix/eidos/README.md @@ -56,7 +56,7 @@ src/uix/eidos/ generated/base.css → salida estatica generada desde EidosConfig base contracts/ → contratos CSS archivados; no se importan en runtime tokens/ → valores CSS legacy hasta generarlos desde ActiveEidos - themes/base/ → CSS legacy de base hasta sustituirlo por salida generada + themes/base/ → CSS base archivado; no se importa en runtime lib/ → soporte de config, tokens, contrato CSS y tipos compartidos components/{x}/ → recipe + wrapper + tipos por componente {x}.css recipe CSS (selectores [data-{x}], etc.) @@ -68,10 +68,10 @@ src/uix/eidos/ `lib/` contiene el soporte puro de configuracion visual: primitivas, semantica visual, themes, contrato CSS y render. `generated/base.css` es el primer artefacto estatico generado desde esa configuracion (`npm run generate:eidos-css`). -`contracts/` queda como material archivado para consulta y tooling, pero ya no -se importa desde `index.css` ni desde `tokens/index.css`. `tokens/` y -`themes/base/` siguen existiendo como material de transicion, pero no son el -modelo que debe crecer. +`contracts/` y `themes/base/` quedan como material archivado para consulta y +tooling, pero ya no se importan desde `index.css` ni desde `tokens/index.css`. +`tokens/` sigue existiendo como material de transicion, pero no es el modelo que +debe crecer. ## Runtime activo @@ -654,11 +654,10 @@ podrá retirarse. modulo general. - **CSS generado/legacy**: `generated/base.css` ya se genera desde la config base de Eidos con `npm run generate:eidos-css` y se importa como foundation - estatica. `contracts/` ya no entra en el runtime CSS ni por el entrypoint - general ni por `tokens/index.css`; queda archivado para consulta/tooling. - `tokens/` y `themes/base/` siguen presentes como transicion porque aun hay - recipes y aliases historicos que dependen de ellos. El siguiente paso es - decidir que queda como recipe estable y que se elimina. + estatica. `contracts/` y `themes/base/` ya no entran en el runtime CSS; + quedan archivados para consulta/tooling. `tokens/` sigue presente como + transicion porque aun hay recipes y aliases historicos que dependen de ellos. + El siguiente paso es decidir que queda como recipe estable y que se elimina. - **Primitivas pendientes de cerrar**: decision final sobre que CSS legacy queda como recipe frente a lo generado. - **Check del repo**: `npm run check` no reporta errores en este punto; quedan diff --git a/src/uix/eidos/generated/base.css b/src/uix/eidos/generated/base.css index e493acb9d..70e726b3b 100644 --- a/src/uix/eidos/generated/base.css +++ b/src/uix/eidos/generated/base.css @@ -229,6 +229,55 @@ --size-xxl-padding-block: var(--space-3); --size-xxl-gap: var(--space-4); --size-xxl-radius: var(--radius-xxl); + --control-height-2xs: var(--control-height-xxs); + --radius-xs: var(--radius-sm); + --font-sans: var(--font-family-primary); + --font-ui: var(--font-family-primary); + --font-serif: var(--font-family-secondary); + --font-prose: var(--font-family-secondary); + --font-heading: var(--font-family-display); + --font-display: var(--font-family-display); + --font-mono: var(--font-family-mono); + --font-code: var(--font-family-mono); + --text-1-size: var(--font-size-xxs); + --text-1-lh: var(--font-line-height-xxs); + --text-1-ls: var(--font-letter-spacing-xxs); + --text-2-size: var(--font-size-xs); + --text-2-lh: var(--font-line-height-xs); + --text-2-ls: var(--font-letter-spacing-xs); + --text-3-size: var(--font-size-sm); + --text-3-lh: var(--font-line-height-sm); + --text-3-ls: var(--font-letter-spacing-sm); + --text-4-size: var(--font-size-md); + --text-4-lh: var(--font-line-height-md); + --text-4-ls: var(--font-letter-spacing-md); + --text-5-size: var(--font-size-lg); + --text-5-lh: var(--font-line-height-lg); + --text-5-ls: var(--font-letter-spacing-lg); + --text-6-size: var(--font-size-xl); + --text-6-lh: var(--font-line-height-xl); + --text-6-ls: var(--font-letter-spacing-xl); + --font-size-base: var(--font-size-md); + --font-size-2xl: var(--font-size-xxl); + --font-size-3xl: var(--font-size-xxxl); + --font-weight-normal: var(--font-weight-regular); + --leading-ui: 1.25; + --leading-prose: 1.6; + --leading-text: var(--leading-prose); + --leading-heading: 1.2; + --leading-display: 1.05; + --tracking-badge: 0; + --tracking-label: 0; + --tracking-ui: 0; + --tracking-prose: 0; + --tracking-heading: 0; + --tracking-display: 0; + --tracking-tight: 0; + --tracking-normal: 0; + --tracking-wide: 0; + --tracking-wider: 0; + --motion-spin-duration: 800ms; + --color-focus-ring: var(--focus-ring-color); } [data-density='compact'] { diff --git a/src/uix/eidos/index.css b/src/uix/eidos/index.css index 2b2d6ad7c..1c216f378 100644 --- a/src/uix/eidos/index.css +++ b/src/uix/eidos/index.css @@ -12,8 +12,8 @@ * * generated/ foundation CSS generated from EidosConfig * contracts/ archived public token interfaces (not imported at runtime) - * tokens/ per-component internal values that reference contracts - * themes/ implementations: light / dark / brand + * tokens/ per-component internal values / transition aliases + * themes/ archived base CSS plus font assets * components/ recipes per component (compose tokens + selectors) * archetypes transversal selectors keyed by `data-archetype` * events reactions to `data-event-*` signals (sema visual channel) @@ -42,27 +42,25 @@ * independent of visual rendering. */ -/* ── Foundation: legacy static fallback ──────────────────────────────────── +/* ── Foundation: generated static fallback ───────────────────────────────── * - * ActiveEidos is the canonical source for generated foundation CSS, but this - * entrypoint is still used by SSR/static docs with `ActiveEidos.create({ - * applyDom: false })`. `contracts/` is intentionally not imported: those - * files are archived empty declarations, not runtime values. + * ActiveEidos is the canonical source for generated foundation CSS. This + * entrypoint imports the generated artifact for SSR/static docs where + * `ActiveEidos.create({ applyDom: false })` does not inject runtime styles. + * `contracts/` and `themes/base/` are intentionally not imported: they are + * archived transition material, not runtime values. */ -/* ── Foundation: per-component token values / legacy recipe aliases ─────── */ -@import './tokens/motion.css'; -@import './tokens/components/index.css'; - /* ── Theme assets: font faces used by the base static theme tokens ───────── */ @import './themes/fonts.css'; -/* ── Theme: actual values filling the contract slots (default = base) ───── */ -@import './themes/base/index.css'; - /* ── Generated foundation: ActiveEidos base config static export ────────── */ @import './generated/base.css'; +/* ── Foundation: per-component token values / legacy recipe aliases ─────── */ +@import './tokens/motion.css'; +@import './tokens/components/index.css'; + /* ── Eidos-specific layers (no equivalent in air) ───────────────────────── */ @import './archetypes.css'; @import './events.css'; diff --git a/src/uix/eidos/lib/render-css.ts b/src/uix/eidos/lib/render-css.ts index 0a7ede266..13c17baee 100644 --- a/src/uix/eidos/lib/render-css.ts +++ b/src/uix/eidos/lib/render-css.ts @@ -22,6 +22,7 @@ import { type FontFamily, type FocusColorRoles, type LayoutPrimitiveSet, + type PrimitiveSet, type SizePrimitiveSet, type SurfaceColorRoles, type ThemeColorSet, @@ -152,6 +153,8 @@ export function renderStaticCss(options: EidosConfig): string { appendSizeDeclarations(declarations, primitives.size) } + appendTransitionAliasDeclarations(declarations, options) + const blocks = [renderBlock(':root', declarations)] if (primitives.density) blocks.push(renderDensityBlocks()) @@ -358,6 +361,113 @@ function appendSizeDeclarations(declarations: string[], sizes: SizePrimitiveSet) } } +function appendTransitionAliasDeclarations( + declarations: string[], + options: EidosConfig +): void { + const { primitives } = options + + if (primitives.controlHeight?.xxs) { + declarations.push(cssVar('control-height-2xs', 'var(--control-height-xxs)')) + } + + if (primitives.radius?.sm) { + declarations.push(cssVar('radius-xs', 'var(--radius-sm)')) + } + + appendFontFamilyAliases(declarations, primitives.typography?.families) + appendTypographyAliases(declarations, primitives.typography) + + if (primitives.motion) { + declarations.push(cssVar('motion-spin-duration', '800ms')) + } + + if (hasFocusColor(options)) { + declarations.push(cssVar('color-focus-ring', 'var(--focus-ring-color)')) + } +} + +function appendFontFamilyAliases( + declarations: string[], + families: TypographyPrimitiveSet['families'] | undefined +): void { + if (!families) return + + if (families.primary) { + declarations.push(cssVar('font-sans', 'var(--font-family-primary)')) + declarations.push(cssVar('font-ui', 'var(--font-family-primary)')) + } + + if (families.secondary) { + declarations.push(cssVar('font-serif', 'var(--font-family-secondary)')) + declarations.push(cssVar('font-prose', 'var(--font-family-secondary)')) + } + + if (families.display) { + declarations.push(cssVar('font-heading', 'var(--font-family-display)')) + declarations.push(cssVar('font-display', 'var(--font-family-display)')) + } + + if (families.mono) { + declarations.push(cssVar('font-mono', 'var(--font-family-mono)')) + declarations.push(cssVar('font-code', 'var(--font-family-mono)')) + } +} + +function appendTypographyAliases( + declarations: string[], + typography: PrimitiveSet['typography'] +): void { + if (!typography) return + + const textAliasMap = { + '1': 'xxs', + '2': 'xs', + '3': 'sm', + '4': 'md', + '5': 'lg', + '6': 'xl' + } as const + + for (const [alias, size] of Object.entries(textAliasMap)) { + if (!typography.sizes[size]) continue + + declarations.push(cssVar(`text-${alias}-size`, `var(--font-size-${size})`)) + declarations.push(cssVar(`text-${alias}-lh`, `var(--font-line-height-${size})`)) + declarations.push(cssVar(`text-${alias}-ls`, `var(--font-letter-spacing-${size})`)) + } + + if (typography.sizes.md) declarations.push(cssVar('font-size-base', 'var(--font-size-md)')) + if (typography.sizes.xxl) declarations.push(cssVar('font-size-2xl', 'var(--font-size-xxl)')) + if (typography.sizes.xxxl) declarations.push(cssVar('font-size-3xl', 'var(--font-size-xxxl)')) + + if (typography.weights.regular) { + declarations.push(cssVar('font-weight-normal', 'var(--font-weight-regular)')) + } + + declarations.push(cssVar('leading-ui', '1.25')) + declarations.push(cssVar('leading-prose', '1.6')) + declarations.push(cssVar('leading-text', 'var(--leading-prose)')) + declarations.push(cssVar('leading-heading', '1.2')) + declarations.push(cssVar('leading-display', '1.05')) + declarations.push(cssVar('tracking-badge', '0')) + declarations.push(cssVar('tracking-label', '0')) + declarations.push(cssVar('tracking-ui', '0')) + declarations.push(cssVar('tracking-prose', '0')) + declarations.push(cssVar('tracking-heading', '0')) + declarations.push(cssVar('tracking-display', '0')) + declarations.push(cssVar('tracking-tight', '0')) + declarations.push(cssVar('tracking-normal', '0')) + declarations.push(cssVar('tracking-wide', '0')) + declarations.push(cssVar('tracking-wider', '0')) +} + +function hasFocusColor(options: EidosConfig): boolean { + if (options.semantics.color.focus) return true + + return Object.values(options.themes ?? {}).some((theme) => theme.color?.focus) +} + function appendThemeColorDeclarations( declarations: string[], color: ReturnType