Guard Eidos recipe CSS contract

active-uix
dev 5 months ago
parent 306c371aef
commit a8045658aa

@ -128,6 +128,35 @@ Actualizacion Eidos auditoria Kimi 2026-05-16:
- `npx vitest run src/uix/eidos` -> 4 archivos, 56 tests OK.
- `npm run test` -> 233 archivos, 2270 tests OK.
Actualizacion Eidos recipe contract 2026-05-16:
- Cerrado el barrido recipe/CSS con una guardia versionada:
`src/uix/eidos/recipe-css-contract.test.ts`.
- Si un CSS de componente consume `--{component}-*`, el alias debe existir
en `THEME_BASE_RECIPE_TOKENS`.
- Si el theme base declara un alias publico, debe estar consumido por el
componente, por sus fuentes Svelte/TS o por otro token compuesto.
- Limpieza aplicada:
- `Toggle` consume sus tokens publicos de variante (`solid`, `outline`,
`ghost`) en vez de duplicar formulas dentro del CSS. Se retira el naming
residual `surface-*` porque la variante publica es `solid`.
- `Checkbox`, `RadioGroup` y `Switch` ya no publican tokens de intents que
sus tipos no pueden activar.
- `Toast` elimina los tokens tipograficos de `message`, parte que la propia
recipe documenta como retirada; conserva `message-color` porque lo usa el
item como color base.
- `Accordion`, `Tabs`, `Tooltip` y `RadioGroup` conectan tokens que estaban
declarados pero no consumidos (`indicator-*`, `list-bg`, `content-z`,
`indicator-bg-checked-disabled`).
- `Icon` mantiene `--icon-size` y `--icon-stroke-width`: son consumidos por
`icon.svelte`, no por CSS.
- Regenerado `src/uix/eidos/generated/base.css` con
`npm run generate:eidos-css`.
- Validacion:
- `npx vitest run src/uix/eidos/recipe-css-contract.test.ts` -> 3 tests OK.
- `npx vitest run src/uix/eidos` -> 5 archivos, 59 tests OK.
- `npm run check` -> 0 errores, 0 warnings.
Actualizacion 2026-05-16:
- Barrido de wrappers publicos Soma frente a providers cerrado:

@ -161,6 +161,13 @@ las recipes CSS consumen. Por ejemplo, `recipes.tooltip['content-z']` genera
`--tooltip-content-z`; `recipes.dialog['overlay-bg']` genera
`--dialog-overlay-bg`. Asi los componentes mantienen su CSS estable y el tema
puede persistir/editar esos valores desde el mismo objeto de authoring.
El contrato recipe/CSS se valida en `recipe-css-contract.test.ts`: si una
recipe CSS consume `--{component}-*`, ese alias debe existir en
`EidosConfig.recipes`; y si el theme base declara un alias publico, debe estar
consumido por el componente o por otro token compuesto. No hay reservas
fantasma ni paletas que el componente no pueda activar desde su API real.
Para editores de theme, `ActiveEidos.listRecipes()` enumera los componentes
con recipe tokens y `ActiveEidos.getRecipeTokens(component)` devuelve una
copia defensiva del mapa de aliases. No muta el config interno.
@ -706,6 +713,8 @@ podrá retirarse.
estables.
- **Recipes**: quedan deliberadamente como `RecipeTokenSet` plano. No se crea
jerarquia estructurada hasta que una recipe tenga un builder/consumer real
que necesite mas semantica que aliases CSS.
que necesite mas semantica que aliases CSS. La guardia
`recipe-css-contract.test.ts` evita que el theme base declare aliases no
consumidos o que un CSS de componente use variables fuera del contrato.
- **Check del repo**: `npm run check` no reporta errores ni warnings en este
punto.

@ -286,11 +286,14 @@ ni `--soma-`.
| Forma | Uso | Ejemplo |
| ------------------ | ----------------------------------------------------- | ---------------------------------------------- |
| `--{component}-…` | tokens públicos (sobreescribibles por el consumer) | `--dialog-content-bg`, `--toggle-radius-md` |
| `--_{component}-…` | tokens internos del recipe (no parte del API público) | `--_dialog-padding`, `--_toggle-palette-track` |
| `--_{component}-…` | tokens internos del recipe (no parte del API público) | `--_tabs-trigger-height`, `--_toggle-bg` |
Los `[data-{component}]` y `[data-{component}-{part}]` selectors son la
única vía pública para que el recipe se acople al runtime — toda
información cross-layer pasa por data-attrs declarados en el morfo.
Cada `--{component}-*` consumido por CSS debe salir de `EidosConfig.recipes`;
si un alias publico queda sin consumidor real, el test
`src/uix/eidos/recipe-css-contract.test.ts` falla.
---

@ -182,8 +182,8 @@
block-size: var(--_accordion-indicator-size);
flex: 0 0 auto;
color: var(--accordion-indicator-color);
transition: transform var(--accordion-trigger-transition-duration)
var(--accordion-trigger-transition-ease);
transition: transform var(--accordion-indicator-transition-duration)
var(--accordion-indicator-transition-ease);
}
[data-accordion-trigger][data-size='sm'] [data-accordion-indicator],
@ -205,7 +205,7 @@
[data-accordion-trigger][data-state='open'] [data-accordion-indicator] {
color: var(--accordion-indicator-color-open);
transform: rotate(180deg);
transform: rotate(var(--accordion-indicator-rotation));
}
[data-accordion-content] {

@ -192,6 +192,10 @@
opacity: var(--radio-group-disabled-opacity);
}
[data-radio-group-item][data-disabled][data-state='checked'] {
background: var(--radio-group-indicator-bg-checked-disabled);
}
/* ── Indicator (the dot) ────────────────────────────────────────────────── */
[data-radio-group-indicator] {

@ -49,6 +49,7 @@
flex-wrap: wrap;
gap: var(--tabs-list-gap);
min-width: 0;
background: var(--tabs-list-bg);
}
[data-tabs-list][data-orientation='vertical'] {

@ -5,33 +5,24 @@
--_toggle-font-size: var(--toggle-font-size-md);
--_toggle-font-weight: var(--toggle-font-weight-md);
--_toggle-radius: var(--toggle-radius-md);
--_toggle-palette-track: var(--toggle-color-neutral-track);
--_toggle-palette-element: var(--toggle-color-neutral-element);
--_toggle-palette-hover: var(--toggle-color-neutral-hover);
--_toggle-palette-active: var(--toggle-color-neutral-active);
--_toggle-palette-border: var(--toggle-color-neutral-border);
--_toggle-palette-solid: var(--toggle-color-neutral-solid);
--_toggle-palette-solid-hover: var(--toggle-color-neutral-solid-hover);
--_toggle-palette-text: var(--toggle-color-neutral-text);
--_toggle-palette-contrast: var(--toggle-color-neutral-contrast);
--_toggle-bg: var(--color-surface-raised);
--_toggle-fg: var(--color-content-primary);
--_toggle-border: var(--color-border-default);
--_toggle-hover-bg: color-mix(
in srgb,
var(--color-surface-overlay) 68%,
var(--_toggle-palette-track)
);
--_toggle-hover-border: color-mix(
in srgb,
var(--_toggle-palette-border) 45%,
var(--color-border-strong)
);
--_toggle-on-bg: var(--_toggle-palette-element);
--_toggle-on-fg: var(--_toggle-palette-text);
--_toggle-on-border: var(--_toggle-palette-border);
--_toggle-on-hover-bg: var(--_toggle-palette-hover);
--_toggle-on-hover-border: var(--_toggle-palette-border);
--toggle-palette-track: var(--toggle-color-neutral-track);
--toggle-palette-element: var(--toggle-color-neutral-element);
--toggle-palette-hover: var(--toggle-color-neutral-hover);
--toggle-palette-border: var(--toggle-color-neutral-border);
--toggle-palette-solid: var(--toggle-color-neutral-solid);
--toggle-palette-solid-hover: var(--toggle-color-neutral-solid-hover);
--toggle-palette-text: var(--toggle-color-neutral-text);
--toggle-palette-contrast: var(--toggle-color-neutral-contrast);
--_toggle-bg: var(--toggle-solid-bg);
--_toggle-fg: var(--toggle-solid-fg);
--_toggle-border: var(--toggle-solid-border);
--_toggle-hover-bg: var(--toggle-solid-hover-bg);
--_toggle-hover-border: var(--toggle-solid-hover-border);
--_toggle-on-bg: var(--toggle-solid-on-bg);
--_toggle-on-fg: var(--toggle-solid-on-fg);
--_toggle-on-border: var(--toggle-solid-on-border);
--_toggle-on-hover-bg: var(--toggle-solid-on-hover-bg);
--_toggle-on-hover-border: var(--toggle-solid-on-hover-border);
display: inline-flex;
align-items: center;
@ -59,84 +50,77 @@
}
/* ──────────────────────────────────────────────────────────────────────
* data-color: 6 doctrinal values for toggle (subset of the 8 system
* tokens). Maps to existing theme primitives until the global tokens
* are renamed (success → fulfill, warning → risk, danger → threat,
* add secondary + affirm + loss).
* data-color: primary | secondary | neutral | affirm | risk | threat.
* Toggle intentionally excludes fulfill/loss: it is neither completion
* nor irreversible loss; those interactions need a different control.
* ────────────────────────────────────────────────────────────────────── */
/* Hierarchy (non-evaluative — applies when intent='neutral') */
[data-toggle][data-color='primary'] {
--_toggle-palette-track: var(--toggle-color-primary-track);
--_toggle-palette-element: var(--toggle-color-primary-element);
--_toggle-palette-hover: var(--toggle-color-primary-hover);
--_toggle-palette-active: var(--toggle-color-primary-active);
--_toggle-palette-border: var(--toggle-color-primary-border);
--_toggle-palette-solid: var(--toggle-color-primary-solid);
--_toggle-palette-solid-hover: var(--toggle-color-primary-solid-hover);
--_toggle-palette-text: var(--toggle-color-primary-text);
--_toggle-palette-contrast: var(--toggle-color-primary-contrast);
--toggle-palette-track: var(--toggle-color-primary-track);
--toggle-palette-element: var(--toggle-color-primary-element);
--toggle-palette-hover: var(--toggle-color-primary-hover);
--toggle-palette-border: var(--toggle-color-primary-border);
--toggle-palette-solid: var(--toggle-color-primary-solid);
--toggle-palette-solid-hover: var(--toggle-color-primary-solid-hover);
--toggle-palette-text: var(--toggle-color-primary-text);
--toggle-palette-contrast: var(--toggle-color-primary-contrast);
}
[data-toggle][data-color='secondary'] {
--_toggle-palette-track: var(--toggle-color-secondary-track);
--_toggle-palette-element: var(--toggle-color-secondary-element);
--_toggle-palette-hover: var(--toggle-color-secondary-hover);
--_toggle-palette-active: var(--toggle-color-secondary-active);
--_toggle-palette-border: var(--toggle-color-secondary-border);
--_toggle-palette-solid: var(--toggle-color-secondary-solid);
--_toggle-palette-solid-hover: var(--toggle-color-secondary-solid-hover);
--_toggle-palette-text: var(--toggle-color-secondary-text);
--_toggle-palette-contrast: var(--toggle-color-secondary-contrast);
--toggle-palette-track: var(--toggle-color-secondary-track);
--toggle-palette-element: var(--toggle-color-secondary-element);
--toggle-palette-hover: var(--toggle-color-secondary-hover);
--toggle-palette-border: var(--toggle-color-secondary-border);
--toggle-palette-solid: var(--toggle-color-secondary-solid);
--toggle-palette-solid-hover: var(--toggle-color-secondary-solid-hover);
--toggle-palette-text: var(--toggle-color-secondary-text);
--toggle-palette-contrast: var(--toggle-color-secondary-contrast);
}
[data-toggle][data-color='neutral'] {
--_toggle-palette-track: var(--toggle-color-neutral-track);
--_toggle-palette-element: var(--toggle-color-neutral-element);
--_toggle-palette-hover: var(--toggle-color-neutral-hover);
--_toggle-palette-active: var(--toggle-color-neutral-active);
--_toggle-palette-border: var(--toggle-color-neutral-border);
--_toggle-palette-solid: var(--toggle-color-neutral-solid);
--_toggle-palette-solid-hover: var(--toggle-color-neutral-solid-hover);
--_toggle-palette-text: var(--toggle-color-neutral-text);
--_toggle-palette-contrast: var(--toggle-color-neutral-contrast);
--toggle-palette-track: var(--toggle-color-neutral-track);
--toggle-palette-element: var(--toggle-color-neutral-element);
--toggle-palette-hover: var(--toggle-color-neutral-hover);
--toggle-palette-border: var(--toggle-color-neutral-border);
--toggle-palette-solid: var(--toggle-color-neutral-solid);
--toggle-palette-solid-hover: var(--toggle-color-neutral-solid-hover);
--toggle-palette-text: var(--toggle-color-neutral-text);
--toggle-palette-contrast: var(--toggle-color-neutral-contrast);
}
/* Intent (evaluative — wins over hierarchy) */
[data-toggle][data-color='affirm'] {
--_toggle-palette-track: var(--toggle-color-affirm-track);
--_toggle-palette-element: var(--toggle-color-affirm-element);
--_toggle-palette-hover: var(--toggle-color-affirm-hover);
--_toggle-palette-active: var(--toggle-color-affirm-active);
--_toggle-palette-border: var(--toggle-color-affirm-border);
--_toggle-palette-solid: var(--toggle-color-affirm-solid);
--_toggle-palette-solid-hover: var(--toggle-color-affirm-solid-hover);
--_toggle-palette-text: var(--toggle-color-affirm-text);
--_toggle-palette-contrast: var(--toggle-color-affirm-contrast);
--toggle-palette-track: var(--toggle-color-affirm-track);
--toggle-palette-element: var(--toggle-color-affirm-element);
--toggle-palette-hover: var(--toggle-color-affirm-hover);
--toggle-palette-border: var(--toggle-color-affirm-border);
--toggle-palette-solid: var(--toggle-color-affirm-solid);
--toggle-palette-solid-hover: var(--toggle-color-affirm-solid-hover);
--toggle-palette-text: var(--toggle-color-affirm-text);
--toggle-palette-contrast: var(--toggle-color-affirm-contrast);
}
[data-toggle][data-color='risk'] {
--_toggle-palette-track: var(--toggle-color-risk-track);
--_toggle-palette-element: var(--toggle-color-risk-element);
--_toggle-palette-hover: var(--toggle-color-risk-hover);
--_toggle-palette-active: var(--toggle-color-risk-active);
--_toggle-palette-border: var(--toggle-color-risk-border);
--_toggle-palette-solid: var(--toggle-color-risk-solid);
--_toggle-palette-solid-hover: var(--toggle-color-risk-solid-hover);
--_toggle-palette-text: var(--toggle-color-risk-text);
--_toggle-palette-contrast: var(--toggle-color-risk-contrast);
--toggle-palette-track: var(--toggle-color-risk-track);
--toggle-palette-element: var(--toggle-color-risk-element);
--toggle-palette-hover: var(--toggle-color-risk-hover);
--toggle-palette-border: var(--toggle-color-risk-border);
--toggle-palette-solid: var(--toggle-color-risk-solid);
--toggle-palette-solid-hover: var(--toggle-color-risk-solid-hover);
--toggle-palette-text: var(--toggle-color-risk-text);
--toggle-palette-contrast: var(--toggle-color-risk-contrast);
}
[data-toggle][data-color='threat'] {
--_toggle-palette-track: var(--toggle-color-threat-track);
--_toggle-palette-element: var(--toggle-color-threat-element);
--_toggle-palette-hover: var(--toggle-color-threat-hover);
--_toggle-palette-active: var(--toggle-color-threat-active);
--_toggle-palette-border: var(--toggle-color-threat-border);
--_toggle-palette-solid: var(--toggle-color-threat-solid);
--_toggle-palette-solid-hover: var(--toggle-color-threat-solid-hover);
--_toggle-palette-text: var(--toggle-color-threat-text);
--_toggle-palette-contrast: var(--toggle-color-threat-contrast);
--toggle-palette-track: var(--toggle-color-threat-track);
--toggle-palette-element: var(--toggle-color-threat-element);
--toggle-palette-hover: var(--toggle-color-threat-hover);
--toggle-palette-border: var(--toggle-color-threat-border);
--toggle-palette-solid: var(--toggle-color-threat-solid);
--toggle-palette-solid-hover: var(--toggle-color-threat-solid-hover);
--toggle-palette-text: var(--toggle-color-threat-text);
--toggle-palette-contrast: var(--toggle-color-threat-contrast);
}
[data-toggle][data-size='sm'] {
@ -158,37 +142,29 @@
}
[data-toggle][data-variant='outline'] {
--_toggle-bg: transparent;
--_toggle-fg: var(--_toggle-palette-text);
--_toggle-border: color-mix(
in srgb,
var(--_toggle-palette-border) 88%,
var(--color-border-default)
);
--_toggle-hover-bg: var(--_toggle-palette-track);
--_toggle-hover-border: var(--_toggle-palette-border);
--_toggle-on-bg: var(--_toggle-palette-element);
--_toggle-on-fg: var(--_toggle-palette-text);
--_toggle-on-border: var(--_toggle-palette-border);
--_toggle-on-hover-bg: var(--_toggle-palette-hover);
--_toggle-on-hover-border: var(--_toggle-palette-border);
--_toggle-bg: var(--toggle-outline-bg);
--_toggle-fg: var(--toggle-outline-fg);
--_toggle-border: var(--toggle-outline-border);
--_toggle-hover-bg: var(--toggle-outline-hover-bg);
--_toggle-hover-border: var(--toggle-outline-hover-border);
--_toggle-on-bg: var(--toggle-outline-on-bg);
--_toggle-on-fg: var(--toggle-outline-on-fg);
--_toggle-on-border: var(--toggle-outline-on-border);
--_toggle-on-hover-bg: var(--toggle-outline-on-hover-bg);
--_toggle-on-hover-border: var(--toggle-outline-on-hover-border);
}
[data-toggle][data-variant='ghost'] {
--_toggle-bg: transparent;
--_toggle-fg: var(--_toggle-palette-text);
--_toggle-border: transparent;
--_toggle-hover-bg: color-mix(in srgb, var(--_toggle-palette-track) 72%, transparent);
--_toggle-hover-border: transparent;
--_toggle-on-bg: var(--_toggle-palette-track);
--_toggle-on-fg: var(--_toggle-palette-text);
--_toggle-on-border: color-mix(in srgb, var(--_toggle-palette-border) 72%, transparent);
--_toggle-on-hover-bg: var(--_toggle-palette-element);
--_toggle-on-hover-border: color-mix(
in srgb,
var(--_toggle-palette-border) 88%,
transparent
);
--_toggle-bg: var(--toggle-ghost-bg);
--_toggle-fg: var(--toggle-ghost-fg);
--_toggle-border: var(--toggle-ghost-border);
--_toggle-hover-bg: var(--toggle-ghost-hover-bg);
--_toggle-hover-border: var(--toggle-ghost-hover-border);
--_toggle-on-bg: var(--toggle-ghost-on-bg);
--_toggle-on-fg: var(--toggle-ghost-on-fg);
--_toggle-on-border: var(--toggle-ghost-on-border);
--_toggle-on-hover-bg: var(--toggle-ghost-on-hover-bg);
--_toggle-on-hover-border: var(--toggle-ghost-on-hover-border);
}
[data-toggle]:hover {

@ -1,5 +1,9 @@
/* ── Content (variant-shared base) ──────────────────────────────────────── */
[data-soma-floating-wrapper]:has(> [data-tooltip-content]) {
z-index: var(--tooltip-content-z);
}
[data-tooltip-content] {
--_tooltip-py: var(--tooltip-padding-y-md);
--_tooltip-px: var(--tooltip-padding-x-md);

@ -418,26 +418,19 @@
--checkbox-size-sm-box-size: 16px;
--checkbox-size-sm-indicator-size: 10px;
--checkbox-size-sm-indicator-stroke: 2px;
--checkbox-size-sm-indeterminate-width: 8px;
--checkbox-size-sm-indeterminate-height: 2px;
--checkbox-size-md-box-size: 18px;
--checkbox-size-md-indicator-size: 12px;
--checkbox-size-md-indicator-stroke: 2px;
--checkbox-size-md-indeterminate-width: 10px;
--checkbox-size-md-indeterminate-height: 2px;
--checkbox-size-lg-box-size: 22px;
--checkbox-size-lg-indicator-size: 14px;
--checkbox-size-lg-indicator-stroke: 2px;
--checkbox-size-lg-indeterminate-width: 12px;
--checkbox-size-lg-indeterminate-height: 2px;
--checkbox-radius: var(--radius-sm);
--checkbox-border-width: var(--border-width);
--checkbox-indeterminate-radius: var(--radius-full);
--checkbox-focus-ring-offset: var(--focus-ring-offset);
--checkbox-focus-ring-width: var(--focus-ring-width);
--checkbox-focus-ring-color: var(--focus-ring-color);
--checkbox-focus-ring-surface: var(--color-surface-default);
--checkbox-focus-ring: var(--focus-ring);
--checkbox-focus-ring: 0 0 0 var(--checkbox-focus-ring-offset) var(--checkbox-focus-ring-surface), 0 0 0 calc(var(--checkbox-focus-ring-offset) + var(--checkbox-focus-ring-width)) var(--checkbox-focus-ring-color);
--checkbox-transition-duration: var(--duration-fast);
--checkbox-transition-ease: var(--ease-default);
--checkbox-stroke-duration: 220ms;
@ -455,18 +448,10 @@
--checkbox-color-primary-solid-hover: var(--color-primary-solid-hover);
--checkbox-color-neutral-solid: var(--color-neutral-solid);
--checkbox-color-neutral-solid-hover: var(--color-neutral-solid-hover);
--checkbox-color-fulfill-solid: var(--color-fulfill-solid);
--checkbox-color-fulfill-solid-hover: var(--color-fulfill-solid-hover);
--checkbox-color-risk-solid: var(--color-risk-solid);
--checkbox-color-risk-solid-hover: var(--color-risk-solid-hover);
--checkbox-color-threat-solid: var(--color-threat-solid);
--checkbox-color-secondary-solid: var(--color-secondary-solid);
--checkbox-color-secondary-solid-hover: var(--color-secondary-solid-hover);
--checkbox-color-affirm-solid: var(--color-affirm-solid);
--checkbox-color-affirm-solid-hover: var(--color-affirm-solid-hover);
--checkbox-color-loss-solid: var(--color-loss-solid);
--checkbox-color-loss-solid-hover: var(--color-loss-solid-hover);
--checkbox-color-threat-solid-hover: var(--color-threat-solid-hover);
--checkbox-group-gap: var(--space-3);
--checkbox-group-label-font-family: var(--font-ui);
--checkbox-group-label-font-size: var(--font-size-sm);
@ -645,9 +630,6 @@
--radio-group-item-size-sm: 16px;
--radio-group-item-size-md: 18px;
--radio-group-item-size-lg: 22px;
--radio-group-indicator-size-sm: 16px;
--radio-group-indicator-size-md: 18px;
--radio-group-indicator-size-lg: 22px;
--radio-group-dot-size-sm: 6px;
--radio-group-dot-size-md: 7px;
--radio-group-dot-size-lg: 9px;
@ -668,7 +650,6 @@
--radio-group-indicator-bg-disabled: color-mix( in srgb, var(--color-surface-overlay) 78%, var(--color-surface-default) );
--radio-group-indicator-border-disabled: color-mix( in srgb, var(--color-border-default) 72%, var(--color-border-subtle) );
--radio-group-indicator-bg-checked-disabled: color-mix( in srgb, var(--color-content-disabled) 40%, var(--color-surface-overlay) );
--radio-group-dot-radius: var(--radius-full);
--radio-group-dot-color: var(--color-content-on-solid);
--radio-group-dot-color-disabled: color-mix( in srgb, var(--color-content-on-solid) 82%, var(--color-surface-default) );
--radio-group-focus-ring-offset: var(--focus-ring-offset);
@ -683,18 +664,10 @@
--radio-group-color-primary-solid-hover: var(--color-primary-solid-hover);
--radio-group-color-neutral-solid: var(--color-neutral-solid);
--radio-group-color-neutral-solid-hover: var(--color-neutral-solid-hover);
--radio-group-color-fulfill-solid: var(--color-fulfill-solid);
--radio-group-color-fulfill-solid-hover: var(--color-fulfill-solid-hover);
--radio-group-color-risk-solid: var(--color-risk-solid);
--radio-group-color-risk-solid-hover: var(--color-risk-solid-hover);
--radio-group-color-threat-solid: var(--color-threat-solid);
--radio-group-color-secondary-solid: var(--color-secondary-solid);
--radio-group-color-secondary-solid-hover: var(--color-secondary-solid-hover);
--radio-group-color-affirm-solid: var(--color-affirm-solid);
--radio-group-color-affirm-solid-hover: var(--color-affirm-solid-hover);
--radio-group-color-loss-solid: var(--color-loss-solid);
--radio-group-color-loss-solid-hover: var(--color-loss-solid-hover);
--radio-group-color-threat-solid-hover: var(--color-threat-solid-hover);
--radio-group-label-font-family: var(--font-ui);
--radio-group-label-font-size: var(--font-size-sm);
--radio-group-label-font-weight: var(--font-weight-medium);
@ -743,10 +716,6 @@
--switch-color-neutral-border: var(--color-neutral-border);
--switch-color-neutral-solid: var(--color-neutral-solid);
--switch-color-neutral-solid-hover: var(--color-neutral-solid-hover);
--switch-color-fulfill-track: var(--color-fulfill-track);
--switch-color-fulfill-border: var(--color-fulfill-border);
--switch-color-fulfill-solid: var(--color-fulfill-solid);
--switch-color-fulfill-solid-hover: var(--color-fulfill-solid-hover);
--switch-color-risk-track: var(--color-risk-track);
--switch-color-risk-border: var(--color-risk-border);
--switch-color-risk-solid: var(--color-risk-solid);
@ -763,10 +732,6 @@
--switch-color-affirm-border: var(--color-affirm-border);
--switch-color-affirm-solid: var(--color-affirm-solid);
--switch-color-affirm-solid-hover: var(--color-affirm-solid-hover);
--switch-color-loss-track: var(--color-loss-track);
--switch-color-loss-border: var(--color-loss-border);
--switch-color-loss-solid: var(--color-loss-solid);
--switch-color-loss-solid-hover: var(--color-loss-solid-hover);
--tabs-stack-gap: var(--space-4);
--tabs-line-stack-gap: var(--space-3);
--tabs-provider-radius: var(--radius-xl);
@ -863,10 +828,6 @@
--toast-title-font-weight: var(--font-weight-semibold);
--toast-title-line-height: var(--leading-ui);
--toast-title-color: var(--color-content-primary);
--toast-message-font-family: var(--font-ui);
--toast-message-font-size: var(--font-size-sm);
--toast-message-font-weight: var(--font-weight-medium);
--toast-message-line-height: var(--leading-prose);
--toast-message-color: var(--color-content-primary);
--toast-description-font-family: var(--font-ui);
--toast-description-font-size: var(--font-size-xs);
@ -967,7 +928,6 @@
--toggle-color-primary-track: var(--color-primary-track);
--toggle-color-primary-element: var(--color-primary-element);
--toggle-color-primary-hover: var(--color-primary-hover);
--toggle-color-primary-active: var(--color-primary-active);
--toggle-color-primary-border: var(--color-primary-border);
--toggle-color-primary-solid: var(--color-primary-solid);
--toggle-color-primary-solid-hover: var(--color-primary-solid-hover);
@ -976,25 +936,14 @@
--toggle-color-neutral-track: var(--color-neutral-track);
--toggle-color-neutral-element: var(--color-neutral-element);
--toggle-color-neutral-hover: var(--color-neutral-hover);
--toggle-color-neutral-active: var(--color-neutral-active);
--toggle-color-neutral-border: var(--color-neutral-border);
--toggle-color-neutral-solid: var(--color-neutral-solid);
--toggle-color-neutral-solid-hover: var(--color-neutral-solid-hover);
--toggle-color-neutral-text: var(--color-neutral-text);
--toggle-color-neutral-contrast: var(--color-neutral-contrast);
--toggle-color-fulfill-track: var(--color-fulfill-track);
--toggle-color-fulfill-element: var(--color-fulfill-element);
--toggle-color-fulfill-hover: var(--color-fulfill-hover);
--toggle-color-fulfill-active: var(--color-fulfill-active);
--toggle-color-fulfill-border: var(--color-fulfill-border);
--toggle-color-fulfill-solid: var(--color-fulfill-solid);
--toggle-color-fulfill-solid-hover: var(--color-fulfill-solid-hover);
--toggle-color-fulfill-text: var(--color-fulfill-text);
--toggle-color-fulfill-contrast: var(--color-fulfill-contrast);
--toggle-color-risk-track: var(--color-risk-track);
--toggle-color-risk-element: var(--color-risk-element);
--toggle-color-risk-hover: var(--color-risk-hover);
--toggle-color-risk-active: var(--color-risk-active);
--toggle-color-risk-border: var(--color-risk-border);
--toggle-color-risk-solid: var(--color-risk-solid);
--toggle-color-risk-solid-hover: var(--color-risk-solid-hover);
@ -1003,7 +952,6 @@
--toggle-color-threat-track: var(--color-threat-track);
--toggle-color-threat-element: var(--color-threat-element);
--toggle-color-threat-hover: var(--color-threat-hover);
--toggle-color-threat-active: var(--color-threat-active);
--toggle-color-threat-border: var(--color-threat-border);
--toggle-color-threat-solid: var(--color-threat-solid);
--toggle-color-threat-solid-hover: var(--color-threat-solid-hover);
@ -1012,7 +960,6 @@
--toggle-color-secondary-track: var(--color-secondary-track);
--toggle-color-secondary-element: var(--color-secondary-element);
--toggle-color-secondary-hover: var(--color-secondary-hover);
--toggle-color-secondary-active: var(--color-secondary-active);
--toggle-color-secondary-border: var(--color-secondary-border);
--toggle-color-secondary-solid: var(--color-secondary-solid);
--toggle-color-secondary-solid-hover: var(--color-secondary-solid-hover);
@ -1021,40 +968,29 @@
--toggle-color-affirm-track: var(--color-affirm-track);
--toggle-color-affirm-element: var(--color-affirm-element);
--toggle-color-affirm-hover: var(--color-affirm-hover);
--toggle-color-affirm-active: var(--color-affirm-active);
--toggle-color-affirm-border: var(--color-affirm-border);
--toggle-color-affirm-solid: var(--color-affirm-solid);
--toggle-color-affirm-solid-hover: var(--color-affirm-solid-hover);
--toggle-color-affirm-text: var(--color-affirm-text);
--toggle-color-affirm-contrast: var(--color-affirm-contrast);
--toggle-color-loss-track: var(--color-loss-track);
--toggle-color-loss-element: var(--color-loss-element);
--toggle-color-loss-hover: var(--color-loss-hover);
--toggle-color-loss-active: var(--color-loss-active);
--toggle-color-loss-border: var(--color-loss-border);
--toggle-color-loss-solid: var(--color-loss-solid);
--toggle-color-loss-solid-hover: var(--color-loss-solid-hover);
--toggle-color-loss-text: var(--color-loss-text);
--toggle-color-loss-contrast: var(--color-loss-contrast);
--toggle-palette-track: var(--toggle-color-neutral-track);
--toggle-palette-element: var(--toggle-color-neutral-element);
--toggle-palette-hover: var(--toggle-color-neutral-hover);
--toggle-palette-active: var(--toggle-color-neutral-active);
--toggle-palette-border: var(--toggle-color-neutral-border);
--toggle-palette-solid: var(--toggle-color-neutral-solid);
--toggle-palette-solid-hover: var(--toggle-color-neutral-solid-hover);
--toggle-palette-text: var(--toggle-color-neutral-text);
--toggle-palette-contrast: var(--toggle-color-neutral-contrast);
--toggle-surface-bg: var(--color-surface-raised);
--toggle-surface-fg: var(--color-content-primary);
--toggle-surface-border: var(--color-border-default);
--toggle-surface-hover-bg: color-mix( in srgb, var(--color-surface-overlay) 68%, var(--toggle-palette-track) );
--toggle-surface-hover-border: color-mix( in srgb, var(--toggle-palette-border) 45%, var(--color-border-strong) );
--toggle-surface-on-bg: var(--toggle-palette-element);
--toggle-surface-on-fg: var(--toggle-palette-text);
--toggle-surface-on-border: var(--toggle-palette-border);
--toggle-surface-on-hover-bg: var(--toggle-palette-hover);
--toggle-surface-on-hover-border: var(--toggle-palette-border);
--toggle-solid-bg: var(--color-surface-raised);
--toggle-solid-fg: var(--color-content-primary);
--toggle-solid-border: var(--color-border-default);
--toggle-solid-hover-bg: color-mix( in srgb, var(--color-surface-overlay) 68%, var(--toggle-palette-track) );
--toggle-solid-hover-border: color-mix( in srgb, var(--toggle-palette-border) 45%, var(--color-border-strong) );
--toggle-solid-on-bg: var(--toggle-palette-solid);
--toggle-solid-on-fg: var(--toggle-palette-contrast);
--toggle-solid-on-border: var(--toggle-palette-solid);
--toggle-solid-on-hover-bg: var(--toggle-palette-solid-hover);
--toggle-solid-on-hover-border: var(--toggle-palette-solid-hover);
--toggle-outline-bg: transparent;
--toggle-outline-fg: var(--toggle-palette-text);
--toggle-outline-border: color-mix( in srgb, var(--toggle-palette-border) 88%, var(--color-border-default) );

@ -146,26 +146,20 @@ export const THEME_BASE_RECIPE_TOKENS = {
'size-sm-box-size': '16px',
'size-sm-indicator-size': '10px',
'size-sm-indicator-stroke': '2px',
'size-sm-indeterminate-width': '8px',
'size-sm-indeterminate-height': '2px',
'size-md-box-size': '18px',
'size-md-indicator-size': '12px',
'size-md-indicator-stroke': '2px',
'size-md-indeterminate-width': '10px',
'size-md-indeterminate-height': '2px',
'size-lg-box-size': '22px',
'size-lg-indicator-size': '14px',
'size-lg-indicator-stroke': '2px',
'size-lg-indeterminate-width': '12px',
'size-lg-indeterminate-height': '2px',
radius: 'var(--radius-sm)',
'border-width': 'var(--border-width)',
'indeterminate-radius': 'var(--radius-full)',
'focus-ring-offset': 'var(--focus-ring-offset)',
'focus-ring-width': 'var(--focus-ring-width)',
'focus-ring-color': 'var(--focus-ring-color)',
'focus-ring-surface': 'var(--color-surface-default)',
'focus-ring': 'var(--focus-ring)',
'focus-ring':
'0 0 0 var(--checkbox-focus-ring-offset) var(--checkbox-focus-ring-surface), 0 0 0 calc(var(--checkbox-focus-ring-offset) + var(--checkbox-focus-ring-width)) var(--checkbox-focus-ring-color)',
'transition-duration': 'var(--duration-fast)',
'transition-ease': 'var(--ease-default)',
'stroke-duration': '220ms',
@ -187,18 +181,10 @@ export const THEME_BASE_RECIPE_TOKENS = {
'color-primary-solid-hover': 'var(--color-primary-solid-hover)',
'color-neutral-solid': 'var(--color-neutral-solid)',
'color-neutral-solid-hover': 'var(--color-neutral-solid-hover)',
'color-fulfill-solid': 'var(--color-fulfill-solid)',
'color-fulfill-solid-hover': 'var(--color-fulfill-solid-hover)',
'color-risk-solid': 'var(--color-risk-solid)',
'color-risk-solid-hover': 'var(--color-risk-solid-hover)',
'color-threat-solid': 'var(--color-threat-solid)',
'color-secondary-solid': 'var(--color-secondary-solid)',
'color-secondary-solid-hover': 'var(--color-secondary-solid-hover)',
'color-affirm-solid': 'var(--color-affirm-solid)',
'color-affirm-solid-hover': 'var(--color-affirm-solid-hover)',
'color-loss-solid': 'var(--color-loss-solid)',
'color-loss-solid-hover': 'var(--color-loss-solid-hover)',
'color-threat-solid-hover': 'var(--color-threat-solid-hover)',
'group-gap': 'var(--space-3)',
'group-label-font-family': 'var(--font-ui)',
'group-label-font-size': 'var(--font-size-sm)',
@ -390,9 +376,6 @@ export const THEME_BASE_RECIPE_TOKENS = {
'item-size-sm': '16px',
'item-size-md': '18px',
'item-size-lg': '22px',
'indicator-size-sm': '16px',
'indicator-size-md': '18px',
'indicator-size-lg': '22px',
'dot-size-sm': '6px',
'dot-size-md': '7px',
'dot-size-lg': '9px',
@ -416,7 +399,6 @@ export const THEME_BASE_RECIPE_TOKENS = {
'color-mix( in srgb, var(--color-border-default) 72%, var(--color-border-subtle) )',
'indicator-bg-checked-disabled':
'color-mix( in srgb, var(--color-content-disabled) 40%, var(--color-surface-overlay) )',
'dot-radius': 'var(--radius-full)',
'dot-color': 'var(--color-content-on-solid)',
'dot-color-disabled':
'color-mix( in srgb, var(--color-content-on-solid) 82%, var(--color-surface-default) )',
@ -433,18 +415,10 @@ export const THEME_BASE_RECIPE_TOKENS = {
'color-primary-solid-hover': 'var(--color-primary-solid-hover)',
'color-neutral-solid': 'var(--color-neutral-solid)',
'color-neutral-solid-hover': 'var(--color-neutral-solid-hover)',
'color-fulfill-solid': 'var(--color-fulfill-solid)',
'color-fulfill-solid-hover': 'var(--color-fulfill-solid-hover)',
'color-risk-solid': 'var(--color-risk-solid)',
'color-risk-solid-hover': 'var(--color-risk-solid-hover)',
'color-threat-solid': 'var(--color-threat-solid)',
'color-secondary-solid': 'var(--color-secondary-solid)',
'color-secondary-solid-hover': 'var(--color-secondary-solid-hover)',
'color-affirm-solid': 'var(--color-affirm-solid)',
'color-affirm-solid-hover': 'var(--color-affirm-solid-hover)',
'color-loss-solid': 'var(--color-loss-solid)',
'color-loss-solid-hover': 'var(--color-loss-solid-hover)',
'color-threat-solid-hover': 'var(--color-threat-solid-hover)',
'label-font-family': 'var(--font-ui)',
'label-font-size': 'var(--font-size-sm)',
'label-font-weight': 'var(--font-weight-medium)',
@ -501,10 +475,6 @@ export const THEME_BASE_RECIPE_TOKENS = {
'color-neutral-border': 'var(--color-neutral-border)',
'color-neutral-solid': 'var(--color-neutral-solid)',
'color-neutral-solid-hover': 'var(--color-neutral-solid-hover)',
'color-fulfill-track': 'var(--color-fulfill-track)',
'color-fulfill-border': 'var(--color-fulfill-border)',
'color-fulfill-solid': 'var(--color-fulfill-solid)',
'color-fulfill-solid-hover': 'var(--color-fulfill-solid-hover)',
'color-risk-track': 'var(--color-risk-track)',
'color-risk-border': 'var(--color-risk-border)',
'color-risk-solid': 'var(--color-risk-solid)',
@ -520,11 +490,7 @@ export const THEME_BASE_RECIPE_TOKENS = {
'color-affirm-track': 'var(--color-affirm-track)',
'color-affirm-border': 'var(--color-affirm-border)',
'color-affirm-solid': 'var(--color-affirm-solid)',
'color-affirm-solid-hover': 'var(--color-affirm-solid-hover)',
'color-loss-track': 'var(--color-loss-track)',
'color-loss-border': 'var(--color-loss-border)',
'color-loss-solid': 'var(--color-loss-solid)',
'color-loss-solid-hover': 'var(--color-loss-solid-hover)'
'color-affirm-solid-hover': 'var(--color-affirm-solid-hover)'
},
tabs: {
'stack-gap': 'var(--space-4)',
@ -627,10 +593,6 @@ export const THEME_BASE_RECIPE_TOKENS = {
'title-font-weight': 'var(--font-weight-semibold)',
'title-line-height': 'var(--leading-ui)',
'title-color': 'var(--color-content-primary)',
'message-font-family': 'var(--font-ui)',
'message-font-size': 'var(--font-size-sm)',
'message-font-weight': 'var(--font-weight-medium)',
'message-line-height': 'var(--leading-prose)',
'message-color': 'var(--color-content-primary)',
'description-font-family': 'var(--font-ui)',
'description-font-size': 'var(--font-size-xs)',
@ -745,7 +707,6 @@ export const THEME_BASE_RECIPE_TOKENS = {
'color-primary-track': 'var(--color-primary-track)',
'color-primary-element': 'var(--color-primary-element)',
'color-primary-hover': 'var(--color-primary-hover)',
'color-primary-active': 'var(--color-primary-active)',
'color-primary-border': 'var(--color-primary-border)',
'color-primary-solid': 'var(--color-primary-solid)',
'color-primary-solid-hover': 'var(--color-primary-solid-hover)',
@ -754,25 +715,14 @@ export const THEME_BASE_RECIPE_TOKENS = {
'color-neutral-track': 'var(--color-neutral-track)',
'color-neutral-element': 'var(--color-neutral-element)',
'color-neutral-hover': 'var(--color-neutral-hover)',
'color-neutral-active': 'var(--color-neutral-active)',
'color-neutral-border': 'var(--color-neutral-border)',
'color-neutral-solid': 'var(--color-neutral-solid)',
'color-neutral-solid-hover': 'var(--color-neutral-solid-hover)',
'color-neutral-text': 'var(--color-neutral-text)',
'color-neutral-contrast': 'var(--color-neutral-contrast)',
'color-fulfill-track': 'var(--color-fulfill-track)',
'color-fulfill-element': 'var(--color-fulfill-element)',
'color-fulfill-hover': 'var(--color-fulfill-hover)',
'color-fulfill-active': 'var(--color-fulfill-active)',
'color-fulfill-border': 'var(--color-fulfill-border)',
'color-fulfill-solid': 'var(--color-fulfill-solid)',
'color-fulfill-solid-hover': 'var(--color-fulfill-solid-hover)',
'color-fulfill-text': 'var(--color-fulfill-text)',
'color-fulfill-contrast': 'var(--color-fulfill-contrast)',
'color-risk-track': 'var(--color-risk-track)',
'color-risk-element': 'var(--color-risk-element)',
'color-risk-hover': 'var(--color-risk-hover)',
'color-risk-active': 'var(--color-risk-active)',
'color-risk-border': 'var(--color-risk-border)',
'color-risk-solid': 'var(--color-risk-solid)',
'color-risk-solid-hover': 'var(--color-risk-solid-hover)',
@ -781,7 +731,6 @@ export const THEME_BASE_RECIPE_TOKENS = {
'color-threat-track': 'var(--color-threat-track)',
'color-threat-element': 'var(--color-threat-element)',
'color-threat-hover': 'var(--color-threat-hover)',
'color-threat-active': 'var(--color-threat-active)',
'color-threat-border': 'var(--color-threat-border)',
'color-threat-solid': 'var(--color-threat-solid)',
'color-threat-solid-hover': 'var(--color-threat-solid-hover)',
@ -790,7 +739,6 @@ export const THEME_BASE_RECIPE_TOKENS = {
'color-secondary-track': 'var(--color-secondary-track)',
'color-secondary-element': 'var(--color-secondary-element)',
'color-secondary-hover': 'var(--color-secondary-hover)',
'color-secondary-active': 'var(--color-secondary-active)',
'color-secondary-border': 'var(--color-secondary-border)',
'color-secondary-solid': 'var(--color-secondary-solid)',
'color-secondary-solid-hover': 'var(--color-secondary-solid-hover)',
@ -799,42 +747,31 @@ export const THEME_BASE_RECIPE_TOKENS = {
'color-affirm-track': 'var(--color-affirm-track)',
'color-affirm-element': 'var(--color-affirm-element)',
'color-affirm-hover': 'var(--color-affirm-hover)',
'color-affirm-active': 'var(--color-affirm-active)',
'color-affirm-border': 'var(--color-affirm-border)',
'color-affirm-solid': 'var(--color-affirm-solid)',
'color-affirm-solid-hover': 'var(--color-affirm-solid-hover)',
'color-affirm-text': 'var(--color-affirm-text)',
'color-affirm-contrast': 'var(--color-affirm-contrast)',
'color-loss-track': 'var(--color-loss-track)',
'color-loss-element': 'var(--color-loss-element)',
'color-loss-hover': 'var(--color-loss-hover)',
'color-loss-active': 'var(--color-loss-active)',
'color-loss-border': 'var(--color-loss-border)',
'color-loss-solid': 'var(--color-loss-solid)',
'color-loss-solid-hover': 'var(--color-loss-solid-hover)',
'color-loss-text': 'var(--color-loss-text)',
'color-loss-contrast': 'var(--color-loss-contrast)',
'palette-track': 'var(--toggle-color-neutral-track)',
'palette-element': 'var(--toggle-color-neutral-element)',
'palette-hover': 'var(--toggle-color-neutral-hover)',
'palette-active': 'var(--toggle-color-neutral-active)',
'palette-border': 'var(--toggle-color-neutral-border)',
'palette-solid': 'var(--toggle-color-neutral-solid)',
'palette-solid-hover': 'var(--toggle-color-neutral-solid-hover)',
'palette-text': 'var(--toggle-color-neutral-text)',
'palette-contrast': 'var(--toggle-color-neutral-contrast)',
'surface-bg': 'var(--color-surface-raised)',
'surface-fg': 'var(--color-content-primary)',
'surface-border': 'var(--color-border-default)',
'surface-hover-bg':
'solid-bg': 'var(--color-surface-raised)',
'solid-fg': 'var(--color-content-primary)',
'solid-border': 'var(--color-border-default)',
'solid-hover-bg':
'color-mix( in srgb, var(--color-surface-overlay) 68%, var(--toggle-palette-track) )',
'surface-hover-border':
'solid-hover-border':
'color-mix( in srgb, var(--toggle-palette-border) 45%, var(--color-border-strong) )',
'surface-on-bg': 'var(--toggle-palette-element)',
'surface-on-fg': 'var(--toggle-palette-text)',
'surface-on-border': 'var(--toggle-palette-border)',
'surface-on-hover-bg': 'var(--toggle-palette-hover)',
'surface-on-hover-border': 'var(--toggle-palette-border)',
'solid-on-bg': 'var(--toggle-palette-solid)',
'solid-on-fg': 'var(--toggle-palette-contrast)',
'solid-on-border': 'var(--toggle-palette-solid)',
'solid-on-hover-bg': 'var(--toggle-palette-solid-hover)',
'solid-on-hover-border': 'var(--toggle-palette-solid-hover)',
'outline-bg': 'transparent',
'outline-fg': 'var(--toggle-palette-text)',
'outline-border':

@ -0,0 +1,113 @@
import { existsSync, readdirSync, readFileSync } from 'node:fs'
import { join } from 'node:path'
import { describe, expect, it } from 'vitest'
import { THEME_BASE_RECIPE_TOKENS } from './lib/recipes/base'
const COMPONENTS_DIR = 'src/uix/eidos/components'
const CSS_CUSTOM_PROPERTY = /--([a-z][a-z0-9]*-[a-z0-9-]+)/g
const COMPONENT_SOURCE_FILE = /\.(css|svelte|ts)$/
const RECIPE_TOKENS = THEME_BASE_RECIPE_TOKENS as Readonly<
Record<string, Readonly<Record<string, string>>>
>
function stripCssComments(css: string): string {
return css.replace(/\/\*[\s\S]*?\*\//g, '')
}
function recipeTokenName(component: string, key: string): string {
return `--${component}-${key}`
}
function readComponentCssFiles(): readonly { component: string; css: string }[] {
return readdirSync(COMPONENTS_DIR, { withFileTypes: true })
.filter((entry) => entry.isDirectory())
.map((entry) => {
const file = join(COMPONENTS_DIR, entry.name, `${entry.name}.css`)
return { component: entry.name, file }
})
.filter(({ file }) => existsSync(file))
.map(({ component, file }) => ({
component,
css: stripCssComments(readFileSync(file, 'utf8'))
}))
}
function readTextFilesRecursive(dir: string): readonly string[] {
return readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
const path = join(dir, entry.name)
if (entry.isDirectory()) return readTextFilesRecursive(path)
if (!COMPONENT_SOURCE_FILE.test(entry.name)) return []
return [readFileSync(path, 'utf8')]
})
}
function readComponentSource(component: string): string {
const dir = join(COMPONENTS_DIR, component)
return existsSync(dir) ? readTextFilesRecursive(dir).join('\n') : ''
}
function readRecipeValueReferences(): string {
return Object.values(RECIPE_TOKENS)
.flatMap((tokens) => Object.values(tokens))
.join('\n')
}
function collectOwnPublicVariables(component: string, css: string): readonly string[] {
const ownPrefix = `--${component}-`
return [
...new Set(
[...css.matchAll(CSS_CUSTOM_PROPERTY)]
.map((match) => `--${match[1]}`)
.filter((name) => name.startsWith(ownPrefix))
)
].sort()
}
describe('Eidos recipe CSS contract', () => {
it('declares every public component variable consumed by component CSS', () => {
const missing: string[] = []
for (const { component, css } of readComponentCssFiles()) {
const declared = new Set(
Object.keys(RECIPE_TOKENS[component] ?? {}).map((key) =>
recipeTokenName(component, key)
)
)
for (const name of collectOwnPublicVariables(component, css)) {
if (!declared.has(name)) missing.push(`${component}: ${name}`)
}
}
expect(missing).toEqual([])
})
it('does not declare recipes for missing component CSS files', () => {
const cssComponents = new Set(
readComponentCssFiles().map(({ component }) => component)
)
const extra = Object.keys(THEME_BASE_RECIPE_TOKENS)
.filter((component) => !cssComponents.has(component))
expect(extra).toEqual([])
})
it('does not leave declared public recipe variables orphaned', () => {
const recipeValueReferences = readRecipeValueReferences()
const orphaned: string[] = []
for (const [component, tokens] of Object.entries(RECIPE_TOKENS)) {
const source = `${readComponentSource(component)}\n${recipeValueReferences}`
for (const key of Object.keys(tokens)) {
const name = recipeTokenName(component, key)
if (!source.includes(name)) orphaned.push(`${component}: ${name}`)
}
}
expect(orphaned).toEqual([])
})
})
Loading…
Cancel
Save

Powered by TurnKey Linux.