--- title: Eidos Theming — Authoring guides type: guide audience: human + agent status: current source: migrated from src/uix/eidos/THEMING_GUIDE.md (2026-07-02, docs-book F7.3; originally extracted from THEMING §8 + §9) --- # Eidos Theming — Authoring guides > The two theming how-tos: add a new component and define a theme. Extracted > from [`THEMING.md`](./reference.md) (the E1 reference) to > live as an E4 guide. The token contract holding all of this together is > [`canon/tsc.md`](../canon/tsc.md); the transversal systems every recipe > must consume are [`canon/recipe-contract.md`](../canon/recipe-contract.md). --- ## How to add a new component Assuming you already have the morfo, the soma and the eidos wrapper scaffolding (`src/uix/eidos/components/{name}/`): ### Step 1 — Decide which tokens you need Look at similar components (`button`, `toggle`, `switch`). Identify which dimensions your component exposes: - Does it have `data-color`? → palette tokens. - Does it have `data-variant`? → variant tokens. - Does it have `data-size`? → size tokens. - How many parts does it have? → per-part tokens. ### Step 2 — Add the recipe in `lib/recipes/base.ts` ```ts // Within THEME_BASE_RECIPE_TOKENS: 'my-component': { // size tokens — scope 'root' (stable) 'height-md': '36px', 'padding-inline-md': 'var(--space-3)', 'gap': 'var(--space-2)', 'radius': 'var(--radius-md)', // per-color literal definitions — scope 'root' 'primary-solid': 'var(--color-primary-solid)', 'affirm-solid': 'var(--color-affirm-solid)', 'threat-solid': 'var(--color-threat-solid)', // dynamic palette — 'host' default + per-color overrides 'palette-solid': { declarations: [ { value: 'var(--my-component-primary-solid)', scope: 'host' }, { value: 'var(--my-component-affirm-solid)', scope: 'color:affirm' }, { value: 'var(--my-component-threat-solid)', scope: 'color:threat' } ] }, // derived tokens — scope 'host' (deps inferred) 'solid-bg': { value: 'var(--my-component-palette-solid)', scope: 'host' } } ``` ### Step 3 — Regenerate ```bash npm run generate:eidos-css ``` If your config violates the TSC, the regeneration tells you: ``` Eidos recipe scope contract violations: - my-component.solid-bg (scope root): dependency 'palette-solid' is only declared at scopes [host], none of which is reachable from the consumer's scope. ``` ### Step 4 — Write the recipe CSS `src/uix/eidos/components/my-component/my-component.css`: ```css [data-my-component] { /* Private tokens — only this recipe reads them */ --_my-component-bg: var(--my-component-solid-bg); --_my-component-radius: var(--my-component-radius); display: inline-flex; align-items: center; padding-inline: var(--my-component-padding-inline-md); height: var(--my-component-height-md); border-radius: var(--_my-component-radius); background: var(--_my-component-bg); gap: var(--my-component-gap); } [data-my-component][data-disabled] { opacity: var(--opacity-disabled); pointer-events: none; } ``` ### Step 5 — Wire the CSS Code-split components import their own CSS from the wrapper `.svelte`; layout primitives and shared visuals aggregate in `index.css`: ```css @import './components/my-component/my-component.css'; ``` ### Step 6 — Verify ```bash npm run generate:eidos-css npm test -- src/uix/eidos npm run morfo:check node --import tsx/esm scripts/eidos-lint.ts my-component ``` ### Common anti-pattern: declaring composed tokens at `:root` ```ts // ❌ WRONG — the TSC will fail 'my-component': { 'palette-solid': { value: 'var(--color-neutral-solid)', scope: 'host' }, 'solid-bg': 'var(--my-component-palette-solid)' // ← implicit 'root' scope, deps live in 'host' } // ✓ CORRECT 'my-component': { 'palette-solid': { value: 'var(--color-neutral-solid)', scope: 'host' }, 'solid-bg': { value: 'var(--my-component-palette-solid)', scope: 'host' } } ``` --- ## How to define a theme Every theme declares **what it is** — `appearance: 'light' | 'dark'`. It is mandatory, never inferred: it governs `color-scheme`, the first declaration of the theme block, which rules the surface the UA paints and eidos cannot style (the native `