# Tema · Untitled UI · Carbon Demo de theming en vivo para activeUIX sobre el sistema de Eidos. Es a la vez un **tema completo** (`untitled-ui-light` / `untitled-ui-dark`) y un **playground** que muestra todos los elementos del tema: roles de color, rampas físicas de 12 pasos, superficies, tipografía, espaciado, radios, sombras, tamaños y el aspecto real de cada control con sus variantes — más la capa semántica (sema). Ruta: `/temas/untitled-ui`. ## Identidad del tema Fiel a [untitledui.com](https://untitledui.com) (tipografía Inter, sombras en capas, radios de 8px, anillos de foco suaves) pero con una decisión deliberada: el **primario es carbón** (grafito casi negro `#1F2024`), monocromo al estilo Vercel / Linear — **no** el violeta de marca de UUI. El violeta de UUI (`#7F56D9`) se reubica como **secundario**. Los 9 roles canónicos del framework quedan retintados con jerarquía diferenciada (ninguno se repite): | Rol | Escala | Color | Papel | | --- | --- | --- | --- | | `primary` | carbon | `#1F2024` | Acción principal, monocroma. Texto auto-invertido vía `slots.contrast: '1'`. | | `secondary` | violet | `#7F56D9` | Violeta de marca UUI. | | `tertiary` | blue | `#1570EF` | Acento azul. | | `neutral` | gray | `#667085` | Gris frío UUI. | | `affirm` | teal | — | Positivo ligero (baja activación). | | `fulfill` | green | — | Éxito / completado. | | `risk` | amber | `#DC6803` | Aviso. | | `threat` | red | `#D92D20` | Error / peligro. | | `loss` | plum | `#7A3AAD` | Pérdida grave: violeta sombrío, posterior (más oscuro que el secundario). | Otros tokens del tema: - **Tipografía**: Inter (`primary` / `secondary` / `display`), Roboto Mono (`mono`). Letter-spacing negativo en los displays (`hero` / `h1` / `h2` a `-0.02em`, `h3` a `-0.01em`). - **Radios**: escala con `md = 8px` como base UUI (`none` → `full`). - **Foco**: `focusRing` con `offset 1px` + `width 3px`; el halo lavanda se pinta en `untitled-ui.css` (`--uui-ring`, derivado de `--color-secondary-solid`). - **Sombras**: set en capas estilo UUI (light + dark). - **Densidad**: `compact` / `comfortable` / `spacious` — **funcional**, escala el espaciado y la altura de los controles (ver «Correcciones del engine» abajo). El tema vive en `_lib/untitled-ui.ts` como un `EidosConfig` independiente (no parchea el `base`): define escalas light/dark, el `ColorRoleMap` y los dos themes. No inventa variants ni roles nuevos — solo retinta lo canónico. ## Componentes mostrados Foundations (markup propio sobre tokens del tema): roles + rampas + superficies, tipografía, espaciado / radio / sombra / tamaño. Controles eidos reales, todos dirigidos por el tema: - **Button** (6 variants × roles × tamaños × estados) - **Badge** - **Form**: Field, Select, Checkbox, RadioGroup, Switch, Slider - **Feedback**: Banner, Progress, Avatar, Card - **Tabs**, **Accordion** - **Toggle**, **ToggleGroup** (single + multiple, segmentado) - **Toolbar** - **Overlays**: Popover, Dialog (portaleados) - **Menús**: Menubar, DropdownMenu, ContextMenu, Tooltip (portaleados) - **Semántica**: la capa sema (sonido + haptic + reacción visual `data-event-*`) encendida en el layout vía `createActiveUix({ events: { sound, haptic, components } })`. ## Estructura | Archivo | Rol | | --- | --- | | `_lib/untitled-ui.ts` | Config del tema (`EidosConfig`): escalas, roles, themes light/dark. Exporta `THEME_ROLES`, `THEME_SCALES`, `COLOR_STEPS`. | | `+layout@.svelte` | Raíz de composición. Arranca `ActiveUix` + `Soma` + `ActiveEidos` con el tema. Posee modo (claro/oscuro) y densidad como fuentes visuales. El `@` resetea la herencia del chrome de `/uix`. | | `+page.svelte` | La página demo. | | `theme.css` | Chrome del shell + helpers de layout (`[data-temas-*]`). | | `untitled-ui.css` | Capa de pulido scoped a `[data-theme^='untitled-ui']`: anillo lavanda, sombras de control, bordes. | ## Correcciones del engine de theming (dependencias) Este tema fue el primer consumidor que reveló dos bugs del engine de Eidos, corregidos **a nivel engine** (sin deuda en el tema). Documentados en detalle en [`src/uix/eidos/THEMING.md` §20](../../../../src/uix/eidos/THEMING.md): 1. **Densidad inerte** — `data-density` no afectaba a nada. Fix: `renderStaticCss` emite `space` y `control-height` como `calc(value * var(--density-*-scale))`, de modo que `[data-density]` reescala de verdad. (§20.1) 2. **`contrast` ilegible sobre sólidos** — el texto sobre el sólido de color resolvía al paso 12 de la escala (casi invisible). Fix: el slot `contrast` por defecto resuelve a `var(--color-content-on-solid, …)`. Por eso el primario carbón lleva texto casi blanco sin configurar nada extra. (§20.2) ## Hallazgo de autoría: `bind:value` debe respetar el default `$bindable` La página tuvo un síntoma engañoso: bajo SSR renderizaba completa (~1.1 MB) pero la hidratación en cliente **desmontaba** casi todo el árbol (los componentes quedaban como anclas vacías ``), sin error visible en consola. Parecía un fallo de hidratación de los componentes con portal. La causa real era un único `bind:value={menubarValue}` con `menubarValue = $state(undefined)`, mientras `` declara `value = $bindable('')`. Svelte 5 lanza `props_invalid_value` («Cannot do `bind:value={undefined}` when `value` has a fallback value») **durante** la hidratación; el error aborta el render de la página y desmonta el árbol. Lección: al usar `bind:` contra un prop con default `$bindable`, el estado local debe inicializarse con un valor del mismo tipo que el default — nunca `undefined`. Aquí: `menubarValue = $state('')` y `align = $state(['left'])` (`ToggleGroup` usa `value = $bindable([])`, también para `type="single"`). No era un bug del engine de theming ni de los portales: los componentes con portal (Popover / Dialog / Menubar / DropdownMenu / Tooltip) renderizan, hidratan y abren correctamente cuando el `bind:value` es válido.