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

21 KiB

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 (§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.

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)

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.