refactor(eidos): share theme color and focus CSS emission

astra
dev 2 weeks ago
parent d92908a8aa
commit e33e186759

@ -355,6 +355,22 @@ y CSS generado pasaron antes y después; los hashes de foundation y palette
permanecieron idénticos. La emisión de foundation y color sigue pendiente de
evaluación independiente.
### Eidos · duplicación en foundation y color (2026-09-22)
`renderThemeCss` y `renderColorPaletteCss` comparten ahora la resolución de tema
y color y el bucle que emite escalas sólidas y alpha. La foundation usa sólo las
escalas referenciadas por roles por defecto; la paleta conserva todas. Las dos
sombras de foco de `renderStaticCss` comparten una construcción parametrizada
por variable de color. El lote reduce `render-css.ts` en 8 líneas netas sin crear
otro módulo. Pasaron 89 pruebas dirigidas tras cada cambio. Los hashes de
foundation y palette y los de un tema personalizado en los modos normal,
`scales: 'all'` y paleta completa coincidieron antes y después.
Se evaluó separar toda la emisión restante de foundation/color, pero el bloque
principal fija un orden extenso de declaraciones y su traslado no elimina
duplicación adicional demostrada. Se conserva en `render-css.ts` hasta hallar
una frontera con beneficio y caracterización propios.
El [ledger de cierre](./LEDGER-cierre-2026-09.md) es la fuente vigente del estado de
trabajos anteriores; los handoffs congelados sirven de procedencia. Este plan
ordena la auditoría, **no crea otro ledger ni considera cerradas sus filas**.
@ -553,8 +569,8 @@ justifica nuevos motores públicos ni trasladar theming a Morfo.
scopes, normalización de recetas y emisión de foundation/color/motion. El orden
de salida es comportamiento CSS y debe permanecer estable. Scopes,
normalización y emisión de movimiento ya se aislaron en `recipe-scopes.ts`,
`recipe-normalization.ts` y `motion-css.ts`; foundation y color permanecen
pendientes.
`recipe-normalization.ts` y `motion-css.ts`; foundation y color conservaron su
orden, con las duplicaciones exactas compartidas y la división amplia diferida.
- [recipes/base.ts](../../src/uix/eidos/lib/recipes/base.ts): dividir el catálogo
por familias o componentes facilita revisión y reduce conflictos. Eso mueve
líneas; no reduce por sí mismo ni CSS generado ni tamaño del bundle.

@ -191,24 +191,8 @@ export function renderStaticCss(options: EidosConfig): string {
// Two-ring focus model: inner `inset` ring (width from token, 0 by
// default → invisible) + the outer ring. Every input component shares
// this so the focus affordance is uniform and theme-parameterised.
declarations.push(
cssVar(
'focus-ring',
'inset 0 0 0 var(--focus-ring-inner-width) var(--focus-ring-color), ' +
'0 0 0 var(--focus-ring-offset) var(--color-surface-default), ' +
'0 0 0 calc(var(--focus-ring-offset) + var(--focus-ring-width)) ' +
'var(--focus-ring-color)'
)
);
declarations.push(
cssVar(
'focus-ring-error',
'inset 0 0 0 var(--focus-ring-inner-width) var(--focus-ring-color-error), ' +
'0 0 0 var(--focus-ring-offset) var(--color-surface-default), ' +
'0 0 0 calc(var(--focus-ring-offset) + var(--focus-ring-width)) ' +
'var(--focus-ring-color-error)'
)
);
declarations.push(cssVar('focus-ring', focusRingShadow('focus-ring-color')));
declarations.push(cssVar('focus-ring-error', focusRingShadow('focus-ring-color-error')));
}
if (primitives.press) {
@ -437,6 +421,15 @@ export function renderStaticCss(options: EidosConfig): string {
return blocks.join('\n\n');
}
function focusRingShadow(colorVar: string): string {
return (
`inset 0 0 0 var(--focus-ring-inner-width) var(--${colorVar}), ` +
'0 0 0 var(--focus-ring-offset) var(--color-surface-default), ' +
'0 0 0 calc(var(--focus-ring-offset) + var(--focus-ring-width)) ' +
`var(--${colorVar})`
);
}
/**
* Accessibility — Windows High Contrast / `forced-colors`. The OS forces colors via
* `forced-color-adjust: auto`, which auto-maps borders / text / backgrounds to system
@ -740,16 +733,7 @@ export function renderThemeCss(
themeId: string,
renderOptions: RenderThemeCssOptions = {}
): string {
const theme = options.themes?.[themeId];
if (!theme) {
throw new EidosThemeNotFoundError(themeId);
}
const color = mergeThemeColor(
options.primitives.color?.scales,
options.semantics.color,
theme.color
);
const { theme, color } = resolveThemeColor(options, themeId);
// The theme SAYS what it is, so every block that paints it declares it:
// `color-scheme` rules the surface the UA paints and eidos cannot style
// (native `<select>` popup, form controls, scrollbars). First declaration,
@ -774,11 +758,7 @@ export function renderThemeCss(
? allScaleNames
: roleReferencedScaleNames(completedRoles, allScaleNames);
for (const scaleName of scaleNames) {
const scale = color.scales[scaleName];
if (scale) appendColorScaleDeclarations(declarations, scaleName, scale);
appendColorAlphaScaleDeclarations(declarations, scaleName, scale, color.alphaScales[scaleName]);
}
appendThemeScaleDeclarations(declarations, color, scaleNames);
for (const role of COLOR_ROLES) {
const definition = completedRoles[role];
@ -895,23 +875,10 @@ export function renderColorPaletteCss(
themeId: string,
renderOptions: RenderThemeCssOptions = {}
): string {
const theme = options.themes?.[themeId];
if (!theme) {
throw new EidosThemeNotFoundError(themeId);
}
const color = mergeThemeColor(
options.primitives.color?.scales,
options.semantics.color,
theme.color
);
const { color } = resolveThemeColor(options, themeId);
const declarations: string[] = [];
const scaleNames = new Set([...Object.keys(color.scales), ...Object.keys(color.alphaScales)]);
for (const scaleName of scaleNames) {
const scale = color.scales[scaleName];
if (scale) appendColorScaleDeclarations(declarations, scaleName, scale);
appendColorAlphaScaleDeclarations(declarations, scaleName, scale, color.alphaScales[scaleName]);
}
appendThemeScaleDeclarations(declarations, color, scaleNames);
return renderBlock(renderOptions.selector ?? getThemeSelector(themeId), declarations);
}
@ -2018,6 +1985,18 @@ function appendThemeColorDeclarations(
}
}
function appendThemeScaleDeclarations(
declarations: string[],
color: ReturnType<typeof mergeThemeColor>,
scaleNames: ReadonlySet<string>
): void {
for (const scaleName of scaleNames) {
const scale = color.scales[scaleName];
if (scale) appendColorScaleDeclarations(declarations, scaleName, scale);
appendColorAlphaScaleDeclarations(declarations, scaleName, scale, color.alphaScales[scaleName]);
}
}
function appendColorScaleDeclarations(
declarations: string[],
scaleName: string,
@ -2233,6 +2212,19 @@ function mergeThemeColor(
};
}
function resolveThemeColor(options: EidosConfig, themeId: string) {
const theme = options.themes?.[themeId];
if (!theme) {
throw new EidosThemeNotFoundError(themeId);
}
const color = mergeThemeColor(
options.primitives.color?.scales,
options.semantics.color,
theme.color
);
return { theme, color };
}
function getThemeSelector(themeId: string): string {
return `[data-theme='${themeId}']`;
}

Loading…
Cancel
Save

Powered by TurnKey Linux.