--- title: Eidos Theming — Architecture Reference type: reference audience: human + agent authority: E1 reference — the theming system: mental model, token layers, roles, sizes, naming, tooling status: current source: migrated from src/uix/eidos/THEMING.md (2026-07-02, docs-book F7.3) --- # Eidos Theming — Architecture Reference > **Audience**: any dev opening the repo who needs to understand how theming > works in UIX. It covers the mental model, the contracts, the tooling and > the traps. If after reading it you still don't know where a new token > goes, this doc failed — open an issue. **TL;DR**: - **9 canonical color roles** (`primary`, `secondary`, `tertiary`, `neutral`, `affirm`, `fulfill`, `risk`, `threat`, `loss`). - **7 canonical sizes** + `full` (`xxs..xxl`). - **3 public token levels**: foundation (stable), per-component recipe (overrideable), private (`--_*`, no external contract). - The **Token Scope Contract (TSC)** decides WHERE each token is emitted (`:root` / `[data-{c}]` / `[data-{c}][data-color='X']` / etc.) and validates transitivity at generation time. - **226 KB raw / 25 KB gzip** of foundation CSS by default. Use `npm run eidos:purge` for production apps → −46 to −55%. - **The color model**: a palette of 33 scales (designable) → hierarchy roles (explicit aliases) → intents (auto-derived by the book's convention, identity = step 9). See §25. - **Compatible with** versioned persistence, CSS-only themes, runtime overrides, dark/light, density (compact/comfortable/spacious), scaling (zoom 90–110, a separate axis), reduced motion, multi-axis breakpoints. --- ## Table of contents 1. [Mental model](#1-mental-model) 1bis. [Theming lives in Eidos, not in Morfo (by design)](#1bis-theming-lives-in-eidos-not-in-morfo-by-design) 2. [The layers of Eidos's CSS](#2-the-layers-of-eidoss-css) 3. [The 7 token layers](#3-the-7-token-layers) 4. [The 9 canonical color roles](#4-the-9-canonical-color-roles) 5. [The size canon](#5-the-size-canon) 6. [Naming conventions](#6-naming-conventions) 7. [Token Scope Contract (TSC)](#7-token-scope-contract-tsc) 8. [How to add a new component](#8-how-to-add-a-new-component) 9. [How to define a theme](#9-how-to-define-a-theme) 10. [How to override tokens at runtime](#10-how-to-override-tokens-at-runtime) 11. [Bundle strategy + `eidos:purge`](#11-bundle-strategy--eidospurge) 12. [Validation tooling](#12-validation-tooling) 13. [Sema integration (`event:*` scope)](#13-sema-integration-event-scope) 14. [Motion](#14-motion) 15. [Comparison with reference libraries](#15-comparison-with-reference-libraries) 16. [Anti-patterns you must NOT commit](#16-anti-patterns-you-must-not-commit) 17. [FAQ — controversial decisions](#17-faq--controversial-decisions) 18. [Universal TSC coverage](#18-universal-tsc-coverage) 19. [Variants are eidos canon, NOT the theme's](#19-variants-are-eidos-canon-not-the-themes) 20. [Theming-engine corrections (2026-06-01)](#20-theming-engine-corrections-2026-06-01) 21. [Two-level color model (RFC — RESOLVED in §25)](#21-two-level-color-model-rfc--resolved-in-25) 22. [Pending theming improvements](#22-pending-theming-improvements) 23. [The `scaling` axis (global zoom)](#23-the-scaling-axis-global-zoom--2026-06-02) 24. [P2 engine corrections (2026-06-02)](#24-p2-engine-corrections-2026-06-02) 25. [The color model — palette + derived roles/intents](#25-the-color-model--palette--derived-rolesintents-2026-06-02) 26. [Runtime theme builder — `eidos.applyColorScheme`](#26-runtime-theme-builder--eidosapplycolorscheme-2026-06-04) 27. [Wide-gamut OKLCH output (default-on)](#27-wide-gamut-oklch-output-default-on-2026-06-04) 28. [Forced-colors accessibility + the border ramp](#28-forced-colors-accessibility--the-border-ramp-2026-06-05) 29. [Depth — the unified, eventful channel](#29-depth--the-unified-eventful-channel-2026-06-05) 30. [Shape — continuity + families + nesting](#30-shape--continuity--families--nesting--eventful-2026-06-05) 31. [Structure (space · density · scale)](#31-structure-space--density--scale--space-as-rhythm-2026-06-05) 32. [Focus ring — the parameterized two-ring model](#32-focus-ring--the-parameterized-two-ring-model-2026-06-11) 33. [Themeable stepper glyphs (`spin-field`)](#33-themeable-stepper-glyphs-spin-field--2026-06-11) 34. [`spin-field` — the stepper-field's shared visual](#34-spin-field--the-stepper-fields-shared-visual-number-field--css-field--2026-06-11) 35. [The scale canon — the theming audit (2026-06-15)](#35-the-scale-canon--the-theming-audit-2026-06-15) 36. [The canonical trigger→panel gap — a token-driven offset (2026-06-22)](#36-the-canonical-triggerpanel-gap--a-token-driven-offset-2026-06-22) 37. [Touch-target — 44px on touch, pointer-gated (2026-06-28)](#37-touch-target--44px-on-touch-pointer-gated-2026-06-28) 38. [The state layer — unified neutral feedback (2026-06-28)](#38-the-state-layer--unified-neutral-feedback-2026-06-28) --- ## 1. Mental model Eidos is UIX's **visual layer**. It owns NO behavior and NO state. It reads from the DOM what the previous layers wrote, and applies styles. ``` Morfo declares the genetics (which attrs / events / parts exist) ↓ Soma transcribes behavior (data-state, data-color, aria-*, focus, …) ↓ Sema emits signals (data-event-* during the perceptual hold) ↓ Eidos applies the visual (tokens, themes, recipes, archetypes, motion) ``` **What Eidos owns**: - The `--*` custom-property namespace. - The entrypoint's CSS layers (§2: generated foundation, archetypes, events, recipes) + the theme blocks `ActiveEidos` injects. - The `ActiveEidos` runtime that injects foundation + theme CSS. - Tooling: generation, validation, purge, lint. **What Eidos does NOT own**: - Components' logical state (that's soma). - The definition of which events exist (that's morfo). - Firing perceptual signals (that's sema). **The 2-of-3 rule**: a system extension (an attribute, a token, a convention) is only justified when **at least two of the three layers** (soma, sema, eidos) consume it. The extensions that entered with eidos's vote: `archetype`, `events[].semantic.{family,intent}`, `events[].prewrite[]`, `data-starting-style` / `data-ending-style`. --- ## 1.bis Theming lives in Eidos, not in Morfo (by design) > **This is the most frequent architectural question — and the most > important answer for not breaking the system.** A new dev's reasonable intuition is: *"if morfo is the cross-layer source of truth, visual tokens should live in morfo too"*. **NO.** UIX's explicit design says the opposite. This section exists to close the case with citations, before the confusion drags a PR into violating the architecture. ### The two canonical quotes in the repo **[`architecture/active-architecture.md`](../architecture/active-architecture.md) §9 (What this architecture is NOT)**: > **Not a classic design system.** Tokens, themes and recipes belong to > Eidos, not to the core. **[`architecture/overview.md`](../architecture/overview.md) §2 (Eidos)**: > The visual layer: **tokens, themes, per-component CSS recipes**, archetype > rules, event reactions and Svelte wrappers over soma's headless > providers… > > Eidos reads from the DOM what the other layers write — **it never imports > soma or sema internals**. Those two sentences, by themselves, close any debate about where theming lives. If a future proposal contradicts them, the proposal must be rejected or the canonical doc must be updated first — not after. ### The 2-of-3 rule derives it mechanically **`active-architecture.md` §7 #12** and **`overview.md` §5** say the same thing: > A morfo extension is only justified when **at least two of the three > layers** (soma, sema, eidos) consume it. Applied to theming: | Who consumes the visual tokens? | | |---|---| | Soma (the behavior runtime) | ❌ no | | Sema (perceptual signals) | ❌ no | | Eidos (the visual layer) | ✅ yes | | **Count** | **1-of-3** | **1-of-3 ≠ 2-of-3 → tokens do NOT go in morfo, by rule**. The visual integration falls into eidos automatically through the 2-of-3 discipline, with nobody having to decide it case by case. ### What is the morfo↔theming relationship, then? Morfo is the source of truth of the **cross-layer contract**: - Parts (which parts exist) - Events (which events it may fire) - Attrs and their enumerated values (which attrs appear in the DOM, with which values) - Archetypes (the transversal classification) - Declarative states **Theming integrates with morfo in ONE PRECISE SENSE**: eidos recipes target DOM attrs that morfo declares. Without morfo, the attrs wouldn't exist in the DOM and the eidos selectors would be dead. ``` MORFO declares data-color.values = ['primary', 'affirm', 'threat', ...] ↓ SOMA emits