You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/docs/process/CONTINUE-theming.md

34 KiB

CONTINUE — eje Theming «theme-reach» (handoff, act. 2026-08-20)

Estado: EN EJECUCIÓN — bloque del vocabulario CERRADO. 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». Y el bloque del vocabulario se ejecutó el mismo día (tres commits, §«Lo ejecutado»): 269 claves renombradas, R-5.3 en error, doctrina al día. Lo siguiente es la migración a la capa de estado (firma 3).

  • Fuente viva del método: PLAN-theming.md (§7 el protocolo, sin excepciones).
  • Informe por componente: docs/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.

EJECUTADO 2026-08-20 — 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 CUATRO PASOS ESTÁN HECHOS (f09e04fab acta · bd916ef3f codemod · 7781d6e4c guard + doctrina, sin pushear). Lo que queda del bloque, y sólo esto:

  • El tercer muro espera a la firma 3. El tipo en defineRecipes (molde PhysicalAxisKey) está escrito y probado: dispara sobre 17 claves, que son los hovers neutros con color en cola de migración. Meterlo hoy rompe npm run check a todo el mundo. Entra cuando la migración las vacíe — dos líneas: una rama ColorSlotKey y las tres excepciones firmadas (*focus-ring-color, orb-color-*, stop-color-*) deletreadas.
  • __names-inventory.ts quedó superado por theming-census --names (contaba 370 porque metía las 47 role-slot). No se borra sin que lo digas.

El registro de los pasos, para quien audite después:

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.

BLOQUE F2-A — el próximo grupo: analizar y corregir (preparado 2026-08-20)

El primer bloque del backfill bajo TODAS las firmas. Ocho componentes con veredicto §5 verificado + una pieza de instrumento que va primero. Suma 330 knobs hoy a 0 %; al cerrar, el global debe rondar 37 % → ~42 %. Cada componente: protocolo §7 entero, un componente = un commit, con la gramática firmada (¡el guard R-5.3 y el muro de tipos ya vigilan!).

Pieza 0 — el instrumento ANTES del bloque (D-TH.2-b, firmada). La familia tipográfica B5 mide su superficie por --style-* como sistema transversal: el censo añade --style-* a la clase system PARA los primitivos tipográficos (heading, text, display, code, s-text…), con gate: las cifras de TODOS los demás componentes quedan idénticas, y heading pasa de «36 privados» a su verdad (consume la capa semántica). Va primero porque cambia el suelo del censo — hacerla a mitad de bloque falsearía los antes/después.

# componente knobs defectos medidos (censo + ficha) corrección firmada por el veredicto
1 ✅ command 52 SIN bloque en base.ts; 35 globales · 9 privados sin derivar · 6 literales contrato entero con talla resuelta (patrón Sidebar); radius con la adaptación al Dialog como CONTEXTO (lee un público de dialog, legítimo — no partir en dos); list-padding-inline + scrollbar-inset (patrón combobox); sonda con el palette ABIERTO
2 table 46 23 privados · 14 globales · 5 literales root-height-{k} es la fila → row-height-{k} al bundle; el prefijo root- sobra en el resto; talla
3 media-player 45 37 globales · 8 literales; privados --_mp-* ABREVIADOS (§6 r5 los prohíbe) renombrar --_mp-* al nombre completo (⚠ prettier: la anotación /* literal: */ en la MISMA línea — trampa de natural-time-picker) + tokens; su acento theme-stable está FIRMADO en base.ts — no tocarlo; el ⚠ de bg son DOS knobs (superficie y velo)
4 tree-grid 42 29 privados · 8 globales 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; talla
5 gradient-picker 38 privados --gp-* ABREVIADOS · 23 globales renombrar --gp-* + tokens; content-width es por talla (20/18/22rem), no un solo knob
6 listbox 38 23 globales · 9 privados; una fila FALSA en la propuesta tokens; retirar la fila font-size = var(--list-font-size): es público de la capa list-surface — un consumidor no acuña lo que la capa posee
7 carousel 36 12 globales · 14 privados · 5 literales tokens + talla; los indicator-width-{k} son geometría propia (rem), ni bundle ni desviación
8 feed 33 16 globales · 13 privados tokens + talla; el título es por talla y el feed tipa por debajo del 1:1 (md → sm), verbatim — no «corregirlo» al 1:1

Cada componente engancha su tab Tokens en el MISMO commit (firmado 2026-08-20, a raíz de que el autor viera que la demo de command no enseñaba su superficie de tema — y resultó que ninguna lo hacía). Es una línea: <TokensPanel component="x" stage={stageRef} /> más el botón del tab; el panel (web/routes/uix/lib/TokensPanel.svelte) lee el contrato vivo con eidos.getRecipeTokens(), así que no hay tabla que mantener. Un componente temable cuya demo esconde sus tokens está a medio entregar.

Gates de cada componente (§7.4/7.5, sin excepciones): censo --only {c} al 100 % o excepción escrita · sonda antes/después = 0 diffs · centinela por token nuevo · component:audit PASS (R-5.3 incluida) · eidos-lint 0 invalid · suite eidos sin rojos nuevos · rtl:check 0 · docs:check 0 · check por fichero · README del componente con la tabla «Talla y tema» (molde navigation-menu). Al cerrar el bloque: revisión adversarial §7.7 (molde Sidebar: refutar «idéntico», «alcanza», «píxel arriba» con medición propia).

Al cerrar F2-A, lo desbloqueado por las firmas espera en este orden:

  1. Diseño de la capa calendar-surface (firma 1) — se PRESENTA antes de escribirla (nombre, ejes, qué posee; molde list-surface y sus 4 reglas). Desbloquea los tres gordos del catálogo: range-calendar (98) + month-grid (61) + year-grid (61) y media date-range-picker — 220 knobs, el salto de ~42 % a ~47 %.
  2. Mandato Field ejecutable (firma 2) — field-langs (41) + las mitades de time-picker / time-range-picker / date-range-picker.
  3. El resto del orden B1-B8 de D-TH.4 (grid-list 32 · textarea 28 · …).

AL TERMINAR EL EJE ENTERO — partir recipes/base.ts por componente

Firmado 2026-08-20, a raíz de la pregunta del autor «¿por qué todos los tokens viven en un fichero?». Va al final de TODO el themeable, no al cerrar un bloque: mientras quede un componente por tokenizar, ese fichero se toca en cada commit.

recipes/base.ts son 6.223 líneas y 128 componentes, y NO es un fichero de tokens: es el valor del campo recipes de EidosConfig, serializable, que un tema hidrata. Por eso los valores no pueden mudarse a components/{x}/ — se intentó y se retiró (architecture/eidos.md: «The old tokens/components/* were retired»). Lo que sí puede repartirse es la AUTORÍA:

// lib/recipes/table.ts
export const tableRecipe = { 'row-height-md': '…', … };
// lib/recipes/base.ts  — el valor sigue siendo UNO
export const THEME_BASE_RECIPE_TOKENS = defineRecipes({ table: tableRecipe, … });

Verificado antes de proponerlo, no supuesto: el muro de tipos de defineRecipes SIGUE disparando cuando las claves llegan por spread desde un fichero suelto — probado con trigger-color (slot color) y padding-x (eje físico), ambas dan error de compilación igual que inline. El único consumidor sensible a la forma es eidos-purge.ts, que hace Object.keys() y es indiferente. recipe-css-contract, el censo, R-5.3 y getRecipeTokens() leen el objeto compuesto.

Los tres costes que paga el fichero único, todos medidos en la sesión del 2026-08-20: casi entra un SEGUNDO bloque table que el catálogo habría descartado en silencio (la trampa está anotada abajo); una coma perdida rompió el catálogo entero dos veces; y es el punto de conflicto de todas las sesiones concurrentes. Repartido, un error afecta a un componente.

Por qué al final y no antes: son ~6.000 líneas movidas en el fichero que cada commit del backfill toca. Hacerlo con componentes pendientes garantiza conflicto con toda sesión que esté tokenizando; con el eje cerrado es un commit mecánico y aislado.

Gate: THEME_BASE_RECIPE_TOKENS idéntico antes/después (comparación ESTRUCTURAL del objeto, no del fichero) · censo global idéntico · generated/base.css sin un solo cambio · el muro de tipos con su muta-prueba (las dos claves malas siguen sin compilar).

FIRMA 3 en ejecución — hover → capa de estado (act. 2026-08-20)

El diagnóstico cambió al empezar. La firma decía «migrar los hovers neutros a la capa de estado». Al hacerlo salió que el velo del sistema no llegaba a ninguno, y no por el hover: por la regla BASE. archetypes.css pinta el velo con :where(...), peso (0,0,0); cualquier receta que declare el ATAJO background: sobre ese mismo nodo fija background-image: none con (0,1,0) y lo cancela SIEMPRE — ni en hover ni nunca. El hover propio no era un capricho: era la cicatriz de un velo que no llegaba.

Así que cada migración son DOS cambios: background: → background-color: en la base, y fuera el hover propio.

componente commit medido
checkbox b35855c20 8 diffs, todos en hover: velo + fondo base (2.436 valores)
splitter c66b73810 2 diffs en el asa (812 valores)
switch 3219a19c3 2 diffs en el track off (377); el swap del checked, verificado
date-range-picker 0c80a8efb sin medir el pintado — la demo no monta kind month/año

Los 6 knobs de la cola eran 47 hasta que se midió la PROPIEDAD: el velo es background-image, así que un hover de borde (22) o de tinta (19) no es la capa de estado, sea cual sea su valor. Esos 41 eran deuda de nombre y se renombraron (93017975c).

Las incidencias van al registro, no al eje

Todo lo que este eje destapa y NO le toca arreglar está registrado en docs/next-features.md §12 y §13 — con su medición, no como impresión:

  • §12 · El contrato de cascada del velo de estado. Seis incidencias que son la misma: nadie fijó cómo compone el velo de archetypes.css con las reglas de una receta. Incluye las 131 declaraciones-atajo en 45 componentes, la prop hoverable que no suprime nada, el empate (0,5,0) que resuelve el orden de carga, la banda de tree-grid que mata el hover, el token de tinta que no puede ganar al arquetipo, y el thumb de scroll-area sin velo al que migrar.
  • §13 · Huecos de instrumento y de demo. Lo que hizo que una medición mintiera o no existiera: falsos negativos del centinela, pseudo-elementos y componentes compuestos que no ve, la sonda que no pasa el ratón por un <tr>, y la lección general — una sonda sobre una parte que la demo no monta compara CERO valores y pasa.

Regla para las sesiones de este eje: si una incidencia mueve píxel o toca morfo, se MIDE, se anota ahí y se sigue; no se arregla dentro del commit del componente.

Lo que queda de la firma 3, y por qué no lo he hecho

  1. scroll-area.thumb-bg-hover — pide DECISIÓN de morfo. El thumb lleva archetype: 'thumb', que NO recibe velo. Migrarlo exige cambiar su arquetipo, y un arquetipo es morfo: se presenta, no se toca de oficio (§7.3-9). La alternativa es dejar su hover como excepción anotada.
  2. El hallazgo sistémico: 141 declaraciones en 45 componentes cancelan el velo del mismo modo, medido con scripts/__statelayer-shorthand.ts (que comprueba contra el morfo que el atajo cae sobre el nodo VELADO, no sobre cualquiera). Peores: radio-group 12 · tag-group 8 · checkbox 7 · table 7 · combobox 6 · tabs 6. Esto explica la adopción 21/135 que midió la auditoría del 2026-07-01: el velo estaba escrito y estructuralmente derrotado. Es un barrido de catálogo que mueve píxel en 45 componentes: no entra en la firma 3, que se firmó sobre unos knobs concretos.
  3. Hueco de demo: date-range-picker no expone kind='month'/'year' (norma N-6), así que la mitad de su API no se ve ni se mide.

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)

Bloque del vocabulario, 2026-08-20 (tres commits, sin pushear):

commit qué
f09e04fab el acta de las 14 firmas + la revisión de los 7 ya corregidos
bd916ef3f codemod: 269 claves · 793 referencias · 101 ficheros · alcance IDÉNTICO · 0 diffs de computed
7781d6e4c R-5.3 en error (muta-prueba de 3 caras) + recipe-contract §1/§4 + theming §6.7 + checklist

Lo que el bloque enseñó (más caro de lo que parece):

  • El plan del codemod se escribe ANTES de tocar nada. Releer el clasificador después da CERO desviaciones —ya están renombradas— y el verificador del diff pasa sobre el vacío. Costó dos vueltas.
  • Un valor puede cambiar de texto sin cambiar de significado: 22 privados reapuntan a nombres nuevos. El gate normaliza por el plan antes de comparar, o los cuenta como cambio de valor y da rojo en falso.
  • No pasar prettier sobre lo que el codemod tocó. El árbol tiene CRLF (autocrlf=true) y 88 de 101 ficheros ya salían «sucios» ANTES; un --write en masa reformateó 300 ficheros de deriva ajena. Se midió lo que importaba —¿alarga el renombrado alguna línea?— y la respuesta fue no en todos, así que prettier no hacía falta.
  • Clasificar un hover por el NOMBRE es clasificarlo mal: dropdown-menu.item-bg-hover lee var(--color-primary-element) — es el palette swap con valencia de recipe-contract §2, no el hover bespoke que §38 deprecó. Lo decide el VALOR. Yo lo había listado mal en este mismo handoff.
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)

node scripts/__theming-probe.ts <componente> <salida.json>   # sonda antes/después
node scripts/__theming-diff.ts <antes.json> <después.json>   # el gate: diff VACÍO
node scripts/__theming-sentinel.ts <componente>              # ¿alcanza cada token?
node scripts/__shot.ts <componente> <salida.png>             # 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 · informe: docs/audit/theming/README.md.
  • Auditoría del SISTEMA (fase 1, cerrada): 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 §3 + F3 y el bloque sidebar de recipes/base.ts.

Powered by TurnKey Linux.