# CONTINUE — eje Theming «theme-reach» (handoff, act. 2026-08-20) **Estado: EN EJECUCIÓN, TODO FIRMADO.** 7 componentes corregidos (auditados contra la gramática nueva: 6 limpios, 1 corrección), 20 fichas revisadas, el informe de auditoría vivo y regenerable. **El 2026-08-20 el autor firmó las 14 decisiones pendientes**: las D-TH.1…8 enteras (§4 del plan, con dos enmiendas) más familia calendar, mandato Field y hover→capa de estado — el acta está en §«Firmas del 2026-08-20». Ya no hay bloqueo de firma: se ejecuta. - Fuente viva del método: [`PLAN-theming.md`](./PLAN-theming.md) (§7 el protocolo, sin excepciones). - **Informe por componente**: [`docs/audit/theming/`](../audit/theming/) — un README de conjunto + 170 fichas (162 recetas + 8 sin receta). Cada ficha lleva su análisis, su propuesta y, en §5, el **veredicto** escrito a mano, que sobrevive a la regeneración. ## MAÑANA, LO PRIMERO — vocabulario de tokens: D-TH.6 (codemod) + R-5.3 (guard) **Por qué antes que nada** (conversación 2026-08-20): el canon de nombres existe (theming §6.7 slots + recipe-contract §1 dimensionales) pero **no tiene guard**, y la medición del 2026-07-01 ya demostró que canon sin guard deriva 30-85 %. Está derivando: el backfill de ayer escribió la forma DOCUMENTADA (modificador delante, 36 claves) mientras el catálogo habla al revés — cada componente que se corrija sin firmar esto ensancha la brecha. El guard de eventos (`eidos-event-vocabulary.ts`) es el precedente: nació de un `commit-resize` muerto tres meses en una receta; R-4.4 es el otro (una regla de nombres ya guardada, deuda muerta «en el mismo pass, sin allowlist»). **FIRMADO 2026-08-20 — el slot de tinta es `fg`; `color` como slot MUERE.** La sesión del 20 leyó lo que la del 19 no había leído (`arts/color`, `architecture/eidos.md` entero, motion §17, §25/§38) y encontró que la pregunta central no era «normalizar hacia lo documentado» sino una CONTRADICCIÓN entre dos doctrinas: theming §6.7 r7 (`fg`) contra el principio de plataforma del px/py («el token se llama como la propiedad»: `color`). El autor adjudicó **`fg`**: el principio de plataforma gobierna los ejes DIMENSIONALES, no la pareja `bg`/`fg` (si la gobernara, `bg` sería `background-color`); `color` es la palabra más sobrecargada del sistema (`--color-*`, `data-color`, 9 roles, `arts/color`); `on-fg` compone; Chakra v3/Panda hablan la pareja. **Inventario CORREGIDO 2026-08-20** (instrumental: los `scripts/__names-*.ts` de esta sesión — `census`, `slots`, `owners`, `audit7`, `audit7b`; el exacto y regenerable lo produce `--names` en el paso 1). Sobre **3.333 claves públicas**, **301 desviadas en 66 componentes** — 193 con `color` como slot, 78 con el modificador DETRÁS a nivel de receta, 30 con ambas. La semilla `__names-inventory.ts` decía 370 porque contaba como desviadas **47 claves `{rol}-{slot-de-rol}` que son CANÓNICAS** (`button.primary-solid-hover`, `toggle.palette-hover`… — `COLOR_ROLE_SLOTS` pone el modificador detrás POR CONSTRUCCIÓN) — un codemod sobre las 370 las habría roto. Adopción viva: 196 `-color` contra 55 `-fg` (las `fg` son mayormente las del backfill de este eje). Peores: tag-group 22 · toast 15 · field 13 · calendar 13 · tabs 12. **Tres familias, tres gramáticas** (el guard codifica las tres): rol/paleta = modificador DETRÁS (canon) · sistema (`--state-hover`, `--opacity-hover`) = detrás, fuera del alcance del guard · recipe-level = §6.7 r7 (delante: `hover-bg`, 13 claves conformes contra 22 `bg-hover`). **Falsos amigos (22) — FIRMADOS en el acta**: `*-focus-ring-color` (3, espeja la familia del SISTEMA) · `aura.orb-color-*` (6, color como sustantivo del orbe) · `gradient-builder.stop-color-*` (4, **stop-color ES la parte** — verificado: `gradient-builder-stop-color.svelte` + parts DOM) → **se quedan** · los 7 con modificador «fuera de vocabulario» resultaron ser DOS clases que el morfo ya resuelve (valores de `data-state`/ejes declarados: `active`, `partial`, `read`, `failed`; y `current` = aria, que entra al vocabulario universal) más una de pseudo-partes (`played`/`buffered`) → **se renombran** (destinos en el acta) · `background.scrim-color-on-{dark,light}` (2, chocaban con el prefijo `on-`) → **`scrim-fg-over-{dark,light}`**. **Hovers neutros — firmado: NO se renombran, MIGRAN**: §38 + R-4.3 deprecan el hover neutro por componente (es la capa `--state-*`). `dropdown-menu.item-bg-hover`, `context-menu.item-bg-hover`, `scroll-area.track/thumb-bg-hover`, `splitter.handle-bg-hover`… la firma 3 del acta ordena su migración a la capa de estado (captura + diff explicado por componente). El codemod los EXCLUYE y el `--names` los lista aparte como cola de esa migración. Los pasos, en orden y con su gate (**el paso 0 murió el 2026-08-20: D-TH.6 está firmada COMPLETA** — ver §«Firmas del 2026-08-20»; la gramática ejecutable es: slot `fg` · delante lo interactivo, detrás lo dimensional/contextual · modificador ∈ universal (+`current`) ∪ `data-state`/ejes del morfo · rol/paleta detrás · hovers neutros excluidos · renombres nominales de los ex-falsos amigos en el acta): 1. **`--names` en el censo**: el inventario exacto por clase de desviación, regenerable, con las 47 role-slot reconocidas como canónicas, los falsos amigos resueltos por el acta (los 3 grupos que se quedan excluidos; los 4 renombrados contados como desviación con su destino) y los hovers neutros listados APARTE (van a la firma 3, no al codemod). Muta-prueba del instrumento: 3 desviadas conocidas salen, 3 role-slot NO salen. 2. **Codemod value-preserving** (molde: px/py del 2026-07-06 y su triple muro): `-color` → `-fg` (196 + 49 mediales, menos los 3 grupos que el acta deja quedarse y los hovers neutros que migran aparte), los renombres nominales del acta (rating-group · chat-message · breadcrumb · waveform · `scrim-fg-over-*`), y modificador detrás → delante a nivel receta (`bg-hover` → `hover-bg`, 22 claves); renombra la clave en `recipes/base.ts` Y todos los consumidores (`var(--{c}-{clave})` en css/svelte/ts/md — con `git ls-files`, no de memoria; los .md de HISTORIA — changelog, errores-toxico, PLAN-affix/background — NO se reescriben), regenera, y verifica: diff de `generated/` = **renombres 1:1**, censo global IDÉNTICO (un rename no cambia alcance), sonda de computed en 3 componentes renombrados = **0 diffs**. Un commit, script commiteado como registro. 3. **Guard R-5.3 en `component-audit`** — de **GRAMÁTICA**, no de lista (a diferencia del de eventos, que valida pertenencia a un vocabulario cerrado), y POR FAMILIA: `{rol|palette}-{slot-de-rol}` con modificador detrás (`COLOR_ROLE_SLOTS`) ∪ recipe-level `[variante-]?[modificador-]?[parte-]?[slot][-talla]?` donde talla ∈ las 7 canónicas, modificador ∈ vocabulario cerrado y DELANTE, slot ∈ (§6.7 recipe-level ∪ ejes dimensionales de recipe-contract §1), y la PARTE contra `compileMorfo(morfo).parts` + la allowlist eidos-only (el mismo criterio que `eidos-lint`). Severidad **`error` directo**: tras el codemod no hay deuda que tolerar, y la rampa `warn` es como estos guards mueren. **Muta-prueba obligatoria de tres caras** antes de darlo por bueno: `-bg-hover` rojo · `trigger-color` rojo · `primary-solid-hover` VERDE (memoria `a-guard-that-inspects-nothing-passes`). 4. **Docs en el mismo pass**: recipe-contract §1 gana la fila del modificador (hoy sólo la insinúa §6.7) y §4 la fila R-5.3; theming §6.7 nota fechada con la firma de `fg` y el acotamiento del principio de plataforma a los ejes dimensionales; T-TH.2 del plan se cierra. Gates del bloque entero: censo global idéntico antes/después del codemod · suite eidos sin rojos nuevos · `component:audit` sin fallos nuevos fuera de R-5.3 · `docs:check` 0 · `rtl:check` 0. ## Después — por dónde seguir 1. `node --import tsx/esm scripts/theming-census.ts` — debe dar **162 recetas · 5.203 knobs · 1.841 públicos (37 %) · 56 sin contrato** (antes del codemod; tras él, el MISMO alcance con nombres nuevos). Si no cuadra, alguien tocó recetas: regenerar el informe (`--report`) antes de nada. 2. Abrir la ficha del componente que toque y leer su **§5 Veredicto**: dice qué de la propuesta es correcto, qué está mal y qué espera firma. Está verificado contra el CSS; la §4 generada NO se sigue a ciegas. **Ojo**: los veredictos se escribieron ANTES del acta del 2026-08-20 — sus «espera firma» se cruzan con §«Firmas del 2026-08-20» (la mayoría ya están resueltos: calendar, Field, hover→estado, D-TH.2) y sus nombres propuestos se pasan por la gramática firmada (p. ej. el `guide-color` de la fila tree-grid de la cola es `guide-fg`). 3. Ejecutar con el protocolo §7 entero. **Un componente = un commit.** ## PRIMERA POSICIÓN — repaso de los 7 ya corregidos contra la gramática nueva Encargo del autor (2026-08-20): revisar lo ya ejecutado por si incumple las reglas nuevas. **Auditados los 7 con `scripts/__names-audit7.ts` y `__names-audit7b.ts`** (claves públicas + privados + consumo en css/README). Resultado: **6 de 7 limpios, 1 hallazgo real**. | componente | claves | veredicto | | --------------------- | -----: | ----------------------------------------------------------------- | | `combobox` | 106 | ✅ limpio — ya habla `fg`, sin modificador detrás, privados sanos | | `natural-time-picker` | 62 | ✅ limpio | | `date-range-picker` | 24 | ✅ limpio | | `time-range-picker` | 22 | ✅ limpio | | `proof-of-human` | 20 | ✅ limpio | | `time-picker` | 16 | ✅ limpio | | `gradient-builder` | 72 | ⚠ **1 corrección** (abajo) | Que los 6 salgan limpios NO es casualidad ni suerte: el backfill de este eje venía escribiendo `fg` (55 de las ~55 claves `-fg` del catálogo son suyas), que es exactamente lo que la firma ratificó. La firma **valida** el trabajo hecho, no lo invalida. **La corrección — `gradient-builder.checker-color` → `checker-fg`** (1 clave, consumida 4 veces en `gradient-builder.css:29-32`). Es el color de los cuadros del damero de transparencia: `color` ahí es **slot**, no sustantivo — cae de lleno en la regla firmada. Las otras 4 claves que el audit marca (`stop-color-title-font-size`, `-font-weight`, `-fg`, `stop-color-sliders-gap`) son **falso amigo verificado**: `stop-color` es la PARTE (`gradient-builder-stop-color.svelte`, `[data-gradient-builder-stop-color-*]`), y una de ellas ya termina en `-fg` correctamente. **No se tocan.** Ejecución: va DENTRO del codemod del paso 2 (misma mecánica, mismos gates), no como commit aparte — es una clave, y sacarla del codemod duplicaría la verificación. Si el codemod se retrasa por las firmas pendientes, se ejecuta sola con el protocolo §7. ## Cola inmediata — ejecutables SIN firma (revisados, con veredicto) Por orden del informe (peor alcance primero): | # | componente | qué hacer | ojo | | --- | ----------------- | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | 1 | `command` | tokens + talla resuelta | el ⚠ de `radius` es adaptación al Dialog, no colisión; el de `list-padding-inline` es el hueco del scrollbar (patrón `scrollbar-inset` de combobox) | | 2 | `table` | tokens + talla | `root-height-{k}` es la **fila** → `row-height-{k}` al bundle; el prefijo `root-` sobra en el resto | | 3 | `media-player` | renombrar `--_mp-*` + tokens | su acento YA está firmado theme-stable en `base.ts` — no tocarlo; el ⚠ de `bg` son dos knobs (superficie y velo) | | 4 | `tree-grid` | renombrar nada; tokens + talla | `root-bg-image` son las **guías de indentación**: el knob es la tinta (`guide-fg` — era `guide-color` pre-acta), los 5 gradientes se quedan en la receta | | 5 | `gradient-picker` | renombrar `--gp-*` + tokens | el ⚠ de `content-width` es **por talla** (20/18/22rem) | | 6 | `listbox` | tokens | **retirar** la fila `font-size = var(--list-font-size)`: es público de la capa `list-surface` | | 7 | `carousel` | tokens + talla | los `indicator-width-{k}` son geometría propia (rem), ni bundle ni desviación | | 8 | `feed` | tokens + talla | el ⚠ del título es **por talla**; el feed tipa por debajo del 1:1 (md → sm), verbatim | ## Firmas del 2026-08-20 — el acta (las 14, «FIRMO TODAS») Presentadas con recomendación fundada y firmadas en bloque por el autor. El detalle razonado de cada una vive en la conversación del 2026-08-20 y en la fila correspondiente del plan §4; aquí el QUÉ ejecutable: 1. **Familia calendar = capa compartida** (molde `lib/list-surface.css`: attr de capa, un eje = token público + ranura privada `var(--_x, var(--x))`, guardada por `shared-layer-contract.test.ts` + `layer:check`). NUNCA ~150 alias `--calendar-*`. Desbloquea `range-calendar` (98), `month-grid` (61), `year-grid` (61) y media `date-range-picker`. El diseño de la capa (nombre, ejes, qué posee) se presenta antes de escribirla. 2. **Mandato Field: ejecutable.** Los x-field/pickers componen el wrapper Field; las alturas de segmento salen del eje size a nivel familia (`--field-control-height-{k}`); muere la re-duplicación `height-{xs..xl}` por componente (theming-audit §2-B: computaban `auto` desde su nacimiento). 3. **hover → capa de estado, donde reaparezca** (empezando por los 3 knobs de `combobox`). Mueve píxel hacia el canon §38/R-4.3 ⇒ cada migración lleva captura antes/después y su diff explicado en el commit. Desbloquea la poda D-2 (changelog §38 ◇: los `--{x}-bg-hover` huérfanos, incluido `pagination.selected-bg-hover` = capa sobre acento). 4. **D-TH.6 completa** — slot `fg` + modificador DELANTE con la regla: **delante lo interactivo, detrás lo dimensional y contextual**; modificador válido ∈ vocabulario universal (+`current`) ∪ valores `data-state`/ejes del morfo del componente; `waveform.played/buffered` = pseudo-partes; `scrim-color-on-*` → `scrim-fg-over-{dark,light}`; hovers neutros FUERA del codemod (migran por la firma 3). Renombres nominales de los ex-falsos amigos: `item-fg`/`active-item-fg`/`partial-item-fg` (rating-group), `read-status-fg`/`failed-status-fg` (chat-message), `current-link-fg` (breadcrumb), `played-fg`/`buffered-fg` (waveform). Se quedan: `*-focus-ring-color` (sistema), `aura.orb-color-*` (sustantivo), `gradient-builder.stop-color-*` (parte). 5. **D-TH.1** R-5 dura · **D-TH.3** tracks WIP en censo, `error` sólo fuera de carril · **D-TH.5** el default no cambia (única excepción: la firma 3, con captura). 6. **D-TH.2** con la opción (b) de `heading`: la familia tipográfica B5 mide por `--style-*` como sistema transversal — no se acuñan `--{c}-*` tipográficos. La lista `KNOB_PROPS` es el perímetro; cambios = firma. 7. **D-TH.4** con enmienda: la cola revisada de este CONTINUE prioriza sobre el orden de familias B1-B8. 8. **D-TH.7** con enmienda: **R-5.3 a `error` directo tras el codemod**; R-5.1/5.2 esperan F3. 9. **D-TH.8**: panel de tokens en `/temas`, en F4. ## Lo ejecutado (censo antes → después) | componente | alcance | commit | | --------------------------- | -------------: | ----------- | | `gradient-builder` (piloto) | 0 % → **80 %** | `3cdb5b5f0` | | `combobox` | 0 % → **76 %** | `cef2d32f1` | | `natural-time-picker` | 0 % → **61 %** | `e774ca7e8` | | `date-range-picker` | 0 % → **42 %** | `07778b597` | | `time-range-picker` | 0 % → **19 %** | `68eb8af26` | | `time-picker` | 0 % → **18 %** | `e4fcdd62e` | | `proof-of-human` | 0 % → **14 %** | `ae91573c3` | Global del catálogo: **33 % → 37 %** (1.621 → 1.841 knobs públicos). Informe + instrumento: `2591ec2f0`; revisión de las fichas 11-20: `3bfe0d413`. ## El instrumental (vive en `scripts/`, prefijo `__` = temporal del eje) ```bash node scripts/__theming-probe.ts # sonda antes/después node scripts/__theming-diff.ts # el gate: diff VACÍO node scripts/__theming-sentinel.ts # ¿alcanza cada token? node scripts/__shot.ts # captura 2× del stage ``` Se ejecutan **con `node`, no con `tsx`** (tsx inyecta helpers que rompen `page.evaluate`) y **desde la raíz** del repo, contra el dev server en `localhost:5173`. Los `scripts/__dbg*.ts` y `__sentinel-targeted*.ts` son de un solo uso (verificaciones dirigidas de ayer); no se commitean. ## Lo que el instrumento aprendió a fuerza de mentir - **La sonda congela `transition`, NUNCA `animation`**: una propiedad transicionada devuelve su valor INICIAL justo tras escribir el token (un token vivo parecía muerto), pero congelar `animation` impide que Presence monte el panel — y entonces mides un popup que no existe. - **Un token RESUELTO no se mueve desde `:root`**: se declara en `[data-{c}]` o en su parte, así que el tema mueve la **coordenada**. El centinela escribe en `:root` **y** en el host, y aun así los resueltos salen «muertos» por diseño. - **Un panel portalado no ve el ámbito del componente**: sus tokens se emiten en `:root` (o con `parts`), y un override scoped no lo alcanza. - **La demo puede tapar el componente**: `proof-of-human` declara su propio `min-block-size` y fondo sobre el stage — ahí el centinela no sirve, y se dice. - **Un valor centinela igual al real lee como «no efecto»** (`9999px` contra un `--radius-full` que ya era 9999px). ## Trampas que costaron un commit cada una (no repetirlas) - **Clave duplicada en `recipes/base.ts`**: añadir un bloque que ya existía lo descarta **en silencio** (gana la segunda). Guard antes de generar: `grep -oE "^\t'?[a-z0-9-]+'?: \{" src/uix/eidos/lib/recipes/base.ts | sed "s/[\t':{ ]//g" | sort | uniq -d` → debe salir vacío. - **Dejar vivos los bloques `[data-…][data-size='…']` del CSS** al emitir la cascada por el TSC: pisan los tokens nuevos con los valores viejos, y el diff de computed da 0 **porque la ruta vieja sigue mandando**. Lo cazó el centinela. - **Correr `component:audit` DESPUÉS de `prettier`, no antes**: al renombrar un privado a su nombre completo, prettier partió tres declaraciones en varias líneas y **separó la anotación `/* literal: … */` de su valor** — R-2.1 la exige en la MISMA línea, así que el guard pasó a ver colores crudos donde antes veía excepciones anotadas. Se arregla con `/* prettier-ignore */` sobre la declaración: NO acortando el comentario ni tocando el valor. Regresión real en `natural-time-picker`, cazada un día tarde por correr el audit antes del formateo. - **Backticks en `node -e "…"` desde bash**: se ejecutan y vacían el texto. Para editar markdown con código, usar el editor, no el shell. - **`prettier --write` sobre un README** no protege de que un Edit se coma un encabezado: comprobar `grep -c "^## Gaps"` después de insertar secciones. - Lo de ayer sigue vigente: medir el nodo que el usuario señala (píxel arriba), el hover es del sistema, el default no cambia, rama compartida sin `stash` ni `--amend`, `check` **por fichero**. ## Fuentes - Plan y decisiones: [`PLAN-theming.md`](./PLAN-theming.md) · informe: [`docs/audit/theming/README.md`](../audit/theming/README.md). - Auditoría del SISTEMA (fase 1, cerrada): [`theming-audit.md`](../audit/theming-audit.md) — §B familia calendar, §5.3-3 mandato de Field. - Doctrina: `docs/theming/reference.md` (§5 talla, §6 nombres, §38 capa de estado) · `docs/canon/recipe-contract.md` · `docs/canon/tsc.md` · `docs/architecture/eidos.md` (vertebración tipográfica, columna `unused`) · `src/uix/eidos/components/README.md` (capas compartidas). - Precedente de la forma por talla: [`PLAN-sidebar.md`](./PLAN-sidebar.md) §3 + F3 y el bloque `sidebar` de `recipes/base.ts`.