From d3f9be4ab05e320248c3f84d3bf35b4589ac780e Mon Sep 17 00:00:00 2001 From: dev Date: Wed, 27 May 2026 13:16:34 +0200 Subject: [PATCH] fix(toggle): add missing per-color track/hover/border forwarders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The toggle palette cascade (palette-track / palette-hover / palette-border declarations in TSC) referenced --toggle-{X}-track, --toggle-{X}-hover, --toggle-{X}-border for X in {primary, neutral, secondary, affirm, risk, threat} — but the recipe only declared 5 forwarders per color (element / solid / solid-hover / text / contrast). The 3 missing slots × 6 colors = 18 unresolved tokens made --toggle-palette-{track,hover,border} resolve to empty for any non-neutral color (the host default DID declare them). Visible effect, only when a non-neutral color was active: - ghost-on-bg = var(--toggle-palette-track) → empty → transparent fallback - ghost-on-border = color-mix(... palette-border ...) → invalid → property dropped, border falls through to base layer - outline-on-bg = color-mix(... palette-element ...) was the one that worked because palette-element forwarders DID exist for all 6 colors Surfaced today while verifying the TSC v2.2 toggle-group color cascade fix in the browser. The toggle-group composition correctly overrode --toggle-palette-* on items, but the empty track/hover/border slots collapsed ghost-state styling downstream. Verified with browser probe (preview_inspect): - ghost + affirm: paletteTrack #0d1514, paletteHover #023b37, paletteBorder #145750 (was "") - ghost + threat: paletteTrack #191111, paletteHover #500f1c, paletteBorder #72232d (was "") - outline + primary: paletteTrack #18111b, paletteHover #3d224e, paletteBorder #54346b (was "") Tests: 100/100 pass in src/uix/eidos. Recipe contract guard accepts the new forwarders (kebab-case, valid var() values). Co-Authored-By: Claude Opus 4.7 (1M context) --- src/uix/eidos/generated/base.css | 18 ++++++++++++++++++ src/uix/eidos/lib/recipes/base.ts | 25 +++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/src/uix/eidos/generated/base.css b/src/uix/eidos/generated/base.css index 57f501317..bf6cc127f 100644 --- a/src/uix/eidos/generated/base.css +++ b/src/uix/eidos/generated/base.css @@ -2818,32 +2818,50 @@ --toggle-shadow: none; --toggle-transition-duration: var(--duration-fast); --toggle-transition-ease: var(--ease-default); + --toggle-primary-track: var(--color-primary-track); --toggle-primary-element: var(--color-primary-element); + --toggle-primary-hover: var(--color-primary-hover); + --toggle-primary-border: var(--color-primary-border); --toggle-primary-solid: var(--color-primary-solid); --toggle-primary-solid-hover: var(--color-primary-solid-hover); --toggle-primary-text: var(--color-primary-text); --toggle-primary-contrast: var(--color-primary-contrast); + --toggle-neutral-track: var(--color-neutral-track); --toggle-neutral-element: var(--color-neutral-element); + --toggle-neutral-hover: var(--color-neutral-hover); + --toggle-neutral-border: var(--color-neutral-border); --toggle-neutral-solid: var(--color-neutral-solid); --toggle-neutral-solid-hover: var(--color-neutral-solid-hover); --toggle-neutral-text: var(--color-neutral-text); --toggle-neutral-contrast: var(--color-neutral-contrast); + --toggle-risk-track: var(--color-risk-track); --toggle-risk-element: var(--color-risk-element); + --toggle-risk-hover: var(--color-risk-hover); + --toggle-risk-border: var(--color-risk-border); --toggle-risk-solid: var(--color-risk-solid); --toggle-risk-solid-hover: var(--color-risk-solid-hover); --toggle-risk-text: var(--color-risk-text); --toggle-risk-contrast: var(--color-risk-contrast); + --toggle-threat-track: var(--color-threat-track); --toggle-threat-element: var(--color-threat-element); + --toggle-threat-hover: var(--color-threat-hover); + --toggle-threat-border: var(--color-threat-border); --toggle-threat-solid: var(--color-threat-solid); --toggle-threat-solid-hover: var(--color-threat-solid-hover); --toggle-threat-text: var(--color-threat-text); --toggle-threat-contrast: var(--color-threat-contrast); + --toggle-secondary-track: var(--color-secondary-track); --toggle-secondary-element: var(--color-secondary-element); + --toggle-secondary-hover: var(--color-secondary-hover); + --toggle-secondary-border: var(--color-secondary-border); --toggle-secondary-solid: var(--color-secondary-solid); --toggle-secondary-solid-hover: var(--color-secondary-solid-hover); --toggle-secondary-text: var(--color-secondary-text); --toggle-secondary-contrast: var(--color-secondary-contrast); + --toggle-affirm-track: var(--color-affirm-track); --toggle-affirm-element: var(--color-affirm-element); + --toggle-affirm-hover: var(--color-affirm-hover); + --toggle-affirm-border: var(--color-affirm-border); --toggle-affirm-solid: var(--color-affirm-solid); --toggle-affirm-solid-hover: var(--color-affirm-solid-hover); --toggle-affirm-text: var(--color-affirm-text); diff --git a/src/uix/eidos/lib/recipes/base.ts b/src/uix/eidos/lib/recipes/base.ts index cd5938373..748559e4d 100644 --- a/src/uix/eidos/lib/recipes/base.ts +++ b/src/uix/eidos/lib/recipes/base.ts @@ -3459,32 +3459,57 @@ export const THEME_BASE_RECIPE_TOKENS = { shadow: 'none', 'transition-duration': 'var(--duration-fast)', 'transition-ease': 'var(--ease-default)', + // Per-color forwarders consumed by the palette-* cascades below. + // 8 slots per color (track / element / hover / border / solid / + // solid-hover / text / contrast) matching the 8 palette-* tokens + // the cascades target. Missing any forwarder makes the + // corresponding palette-{slot} resolve to empty for that color, + // which downstream collapses any derived token consuming it + // (e.g. ghost-on-bg uses palette-track). + 'primary-track': 'var(--color-primary-track)', 'primary-element': 'var(--color-primary-element)', + 'primary-hover': 'var(--color-primary-hover)', + 'primary-border': 'var(--color-primary-border)', 'primary-solid': 'var(--color-primary-solid)', 'primary-solid-hover': 'var(--color-primary-solid-hover)', 'primary-text': 'var(--color-primary-text)', 'primary-contrast': 'var(--color-primary-contrast)', + 'neutral-track': 'var(--color-neutral-track)', 'neutral-element': 'var(--color-neutral-element)', + 'neutral-hover': 'var(--color-neutral-hover)', + 'neutral-border': 'var(--color-neutral-border)', 'neutral-solid': 'var(--color-neutral-solid)', 'neutral-solid-hover': 'var(--color-neutral-solid-hover)', 'neutral-text': 'var(--color-neutral-text)', 'neutral-contrast': 'var(--color-neutral-contrast)', + 'risk-track': 'var(--color-risk-track)', 'risk-element': 'var(--color-risk-element)', + 'risk-hover': 'var(--color-risk-hover)', + 'risk-border': 'var(--color-risk-border)', 'risk-solid': 'var(--color-risk-solid)', 'risk-solid-hover': 'var(--color-risk-solid-hover)', 'risk-text': 'var(--color-risk-text)', 'risk-contrast': 'var(--color-risk-contrast)', + 'threat-track': 'var(--color-threat-track)', 'threat-element': 'var(--color-threat-element)', + 'threat-hover': 'var(--color-threat-hover)', + 'threat-border': 'var(--color-threat-border)', 'threat-solid': 'var(--color-threat-solid)', 'threat-solid-hover': 'var(--color-threat-solid-hover)', 'threat-text': 'var(--color-threat-text)', 'threat-contrast': 'var(--color-threat-contrast)', + 'secondary-track': 'var(--color-secondary-track)', 'secondary-element': 'var(--color-secondary-element)', + 'secondary-hover': 'var(--color-secondary-hover)', + 'secondary-border': 'var(--color-secondary-border)', 'secondary-solid': 'var(--color-secondary-solid)', 'secondary-solid-hover': 'var(--color-secondary-solid-hover)', 'secondary-text': 'var(--color-secondary-text)', 'secondary-contrast': 'var(--color-secondary-contrast)', + 'affirm-track': 'var(--color-affirm-track)', 'affirm-element': 'var(--color-affirm-element)', + 'affirm-hover': 'var(--color-affirm-hover)', + 'affirm-border': 'var(--color-affirm-border)', 'affirm-solid': 'var(--color-affirm-solid)', 'affirm-solid-hover': 'var(--color-affirm-solid-hover)', 'affirm-text': 'var(--color-affirm-text)',