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/PLAN-theming.md

740 lines
79 KiB

docs(theming): el plan del eje theme-reach — medir qué alcanza un tema, y corregir componente a componente El autor, al ver que el radio del trigger del nav vivía en un privado clavado a --radius-default: «¿los componentes son themables? si no lo son, es un error como framework». Lo es. La doctrina ya exige que cada receta declare sus knobs en recipes/base.ts (recipe-contract §1, theming §6), pero R-1…R-4 sólo comprueban «sin literales», no «alcanzable por un tema»: una receta con todo en var(--radius-md) y privados pasa component:audit en PASS y sólo se puede temar moviendo el sistema entero. Medido, no opinado — scripts/theming-census.ts (instrumento, NO guard): de 5.205 knobs de apariencia en 162 recetas, 1.621 (33 %) pasan por un token público del componente; 1.880 atan directo a un primitivo global, 856 a privados, 614 son literales; 62 componentes no tienen una sola entrada en el contrato; 59 tienen alcance < 20 %; 6 al 100 %. navigation-menu, tras todo el día de hoy, 44 %. El plan (PLAN-theming.md): §1 el censo por familia y por componente (tabla regenerable, no editable) · §2 los cuatro ejes de «personalizable» (tokens · talla · color/variante · estados) · §3 la regla R-5 «alcance de tema» para component-audit, con su válvula de excepción · §4 ocho decisiones D-TH para el autor (R-5 dura · perímetro de knob · WIP · orden · DEFAULT IDÉNTICO · normalizar nombres de slot, que hoy incumplen tabs y el propio nav · cuándo gradúa a error · panel de temas) · §5 fases F0–F4 con gates · §6 deuda transversal · §7 el PROTOCOLO DE VERIFICACIÓN por componente, porque «lo ejecutará Opus, y Opus falla mucho»: cinco preguntas, medir ANTES (computed por parte/estado/talla, sonda headless desde la raíz, fuera de callbacks de MutationObserver), editar sólo la forma firmada, medir DESPUÉS (diff de computed = 0, prueba de CENTINELA por cada token nuevo, desde el píxel hacia arriba, registro de animationstart/end), guards por fichero, un componente = un commit con sus artefactos, y revisión adversarial por bloque. Cada paso tiene un artefacto; sin artefacto no está hecho. El instrumento se probó por mutación antes de creerle, y falló la primera vez: no contaba una regla de una sola línea (`[x] { padding-inline: 8px; }`), así que meter un literal no movía la cifra. Regex endurecido; ahora 6→7→8 con una y dos mutaciones. Las cifras del plan son las del instrumento final, y el handoff (CONTINUE-theming.md) obliga a reproducirlas antes de seguir. Nada firmado, nada construido. Gates del commit: docs:check 0/642 · prettier del script limpio · tsc del script sin errores · el plan cita ficheros que existen (los once, comprobados). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
# PLAN — Theming: que TODO componente sea personalizable por un tema (eje `theme-reach`)
docs(theming): el handoff, reescrito para que otra sesión arranque sin arqueología La sesión llega llena. El handoff había crecido por acumulación —cada bloque añadía su sección— y para saber qué hacer había que leerlo entero y deducirlo. Ahora la AGENDA va arriba y el registro histórico abajo, separado por su propio encabezado. Arriba: qué comprobar en los primeros cinco minutos (las dos cifras que deben cuadrar, el dev server antes de medir, leer el veredicto §5); tres opciones con recomendación —revisión adversarial de F2-A, el diseño de `calendar-surface`, o seguir la cola por alcance, con la cola ya recalculada—; el protocolo en diez pasos; y **lo que este bloque enseñó**, que es lo que evita repetir el día: el veredicto orienta pero la medición decide (tres de ocho tenían un error de lectura) · un token que no mueve nada miente, y lo que procede es retirar la declaración muerta · un alias puro se borra, no se renombra · lo que la capa posee el consumidor no lo acuña · y la sonda miente de tres maneras distintas, así que ante un diff inesperado se corre DOS VECES sobre el mismo código antes de sospechar del cambio. El plan §8 gana la entrada de la sesión con las cifras y los commits, y su cabecera de estado pasa a 43 %. Estado que hereda la sesión siguiente: alcance global 43 % (era 33 % al abrir el eje), 15 componentes con contrato, 49 sin él, 7 al 100 %, gramática con 0 desviadas y guard en `error`. 29 commits, sin pushear. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
> **Estado (act. 2026-08-21): EN EJECUCIÓN — alcance global 43 %, 15
> componentes con contrato, 49 sin él. Vocabulario CERRADO (codemod + R-5.3 +
> muro de tipos) y bloque F2-A CERRADO. Las D-TH.1…8 de §4 FIRMADAS TODAS el
> 2026-08-20** (más las
docs(theming): el acta — las catorce decisiones que faltaban, firmadas El eje llevaba desde el 19 ejecutando sólo lo que no dependía de una firma. Hoy se presentaron las catorce con recomendación fundada y el autor las firmó en bloque. Quedan escritas donde se ejecutan: §4 del plan fila a fila, y una sección de acta en el handoff con el QUÉ ejecutable de cada una. La que cambió de sentido al presentarla fue D-TH.6. La pregunta no era «normalizar hacia lo documentado» sino una CONTRADICCIÓN entre dos doctrinas firmadas: theming §6.7 r7 dice que el slot de tinta es `fg`; el principio de plataforma del codemod px/py («el token se llama como la propiedad») dice `color`. Se adjudicó `fg`, y con una razón que acota el principio en vez de romperlo: gobierna los ejes DIMENSIONALES, no la pareja `bg`/`fg` — si la gobernara, `bg` tendría que llamarse `background-color`, y nadie lo propone. De la misma lectura salió que el inventario heredado estaba inflado: de las 370 claves «desviadas», 47 son `{rol}-{slot-de-rol}` (`primary-solid-hover`, `palette-hover`) donde COLOR_ROLE_SLOTS pone el modificador detrás POR CONSTRUCCIÓN. Un codemod sobre las 370 las habría roto — `button` entero. El inventario real es 301, y la gramática es POR FAMILIA, no única. Los 22 «falsos amigos» resultaron ser tres cosas distintas: los que se quedan (la familia del sistema `--focus-ring-*`, el color como sustantivo de `aura`, y `stop-color` que ES una parte de gradient-builder), los que el morfo ya resuelve (`partial`, `read`, `failed` son valores declarados de `data-state` y `data-delivery`), y los dos `scrim-color-on-*`, que chocaban con el prefijo canónico `on-` del acento y pasan a `scrim-fg-over-*`. Y una que no era limpieza sino decisión: los hovers neutros NO se renombran, MIGRAN a la capa de estado (§38 + R-4.3). Renombrarlos habría presumido que sobreviven. Con las firmas, la revisión de los 7 componentes ya corregidos contra la gramática nueva: 6 limpios y una corrección (`gradient-builder.checker-color` → `checker-fg`), que entra en el codemod. Que salgan limpios no es suerte — el backfill de este eje ya venía escribiendo `fg`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
> tres decisiones hermanas del mismo día: familia calendar = capa compartida,
> mandato Field ejecutable, hover→capa de estado — ver «Firmas del 2026-08-20»
> del CONTINUE). El registro con cifras y commits está en §8, y el punto
> de entrada diario es [`CONTINUE-theming.md`](./CONTINUE-theming.md). Toda
> decisión NUEVA que surja se sigue presentando una por mensaje, con las
> cinco preguntas.
docs(theming): el plan del eje theme-reach — medir qué alcanza un tema, y corregir componente a componente El autor, al ver que el radio del trigger del nav vivía en un privado clavado a --radius-default: «¿los componentes son themables? si no lo son, es un error como framework». Lo es. La doctrina ya exige que cada receta declare sus knobs en recipes/base.ts (recipe-contract §1, theming §6), pero R-1…R-4 sólo comprueban «sin literales», no «alcanzable por un tema»: una receta con todo en var(--radius-md) y privados pasa component:audit en PASS y sólo se puede temar moviendo el sistema entero. Medido, no opinado — scripts/theming-census.ts (instrumento, NO guard): de 5.205 knobs de apariencia en 162 recetas, 1.621 (33 %) pasan por un token público del componente; 1.880 atan directo a un primitivo global, 856 a privados, 614 son literales; 62 componentes no tienen una sola entrada en el contrato; 59 tienen alcance < 20 %; 6 al 100 %. navigation-menu, tras todo el día de hoy, 44 %. El plan (PLAN-theming.md): §1 el censo por familia y por componente (tabla regenerable, no editable) · §2 los cuatro ejes de «personalizable» (tokens · talla · color/variante · estados) · §3 la regla R-5 «alcance de tema» para component-audit, con su válvula de excepción · §4 ocho decisiones D-TH para el autor (R-5 dura · perímetro de knob · WIP · orden · DEFAULT IDÉNTICO · normalizar nombres de slot, que hoy incumplen tabs y el propio nav · cuándo gradúa a error · panel de temas) · §5 fases F0–F4 con gates · §6 deuda transversal · §7 el PROTOCOLO DE VERIFICACIÓN por componente, porque «lo ejecutará Opus, y Opus falla mucho»: cinco preguntas, medir ANTES (computed por parte/estado/talla, sonda headless desde la raíz, fuera de callbacks de MutationObserver), editar sólo la forma firmada, medir DESPUÉS (diff de computed = 0, prueba de CENTINELA por cada token nuevo, desde el píxel hacia arriba, registro de animationstart/end), guards por fichero, un componente = un commit con sus artefactos, y revisión adversarial por bloque. Cada paso tiene un artefacto; sin artefacto no está hecho. El instrumento se probó por mutación antes de creerle, y falló la primera vez: no contaba una regla de una sola línea (`[x] { padding-inline: 8px; }`), así que meter un literal no movía la cifra. Regex endurecido; ahora 6→7→8 con una y dos mutaciones. Las cifras del plan son las del instrumento final, y el handoff (CONTINUE-theming.md) obliga a reproducirlas antes de seguir. Nada firmado, nada construido. Gates del commit: docs:check 0/642 · prettier del script limpio · tsc del script sin errores · el plan cita ficheros que existen (los once, comprobados). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
>
> **Origen (conversación 2026-08-19)**: al arreglar `navigation-menu` salió que
> el radio de su trigger vivía en un privado clavado a `--radius-default` y que
> el contrato del componente sólo publicaba tres knobs: ningún tema podía decir
> «píldora» sin mover el radio del sistema entero. El autor, textual: **«esto
> ha abierto una duda importante, y es si los componentes creados son
> themables; si no lo son, es un error como framework. Necesitaríamos
> desarrollar un plan para auditar todos los componentes, verificar qué grado
> de personalización soportan y desarrollar una corrección a cada uno de ellos
> para que sean totalmente personalizables.»** Y sobre la ejecución: **«lo
> ejecutará Opus, y Opus falla mucho, por lo que tienes que establecer procesos
> de verificación en todo el plan.»** Este documento obedece a las dos frases:
> §1 es la auditoría MEDIDA (reproducible por script, no por memoria), §5 son
> las fases, y §7 es el protocolo de verificación que cada sesión ejecuta sin
> excepción.
>
> **Kickoff para sesión nueva**: _«Lee `docs/process/PLAN-theming.md` ENTERO y
> después `CONTINUE-theming.md`; si las D-TH de §4 están firmadas, ejecuta el
> siguiente bloque de §5 con el protocolo de §7, un componente por commit; si
> no, PARA y preséntalas una a una.»_
>
> Fuentes de doctrina que este plan cita y NO copia:
> [`docs/theming/reference.md`](../theming/reference.md) §1bis, §3, §5, §6,
> §7, §12, §16 · [`docs/canon/tsc.md`](../canon/tsc.md) ·
> [`docs/canon/recipe-contract.md`](../canon/recipe-contract.md) §1–§5 ·
> [`docs/theming/motion.md`](../theming/motion.md) §"When both write
> `animation` on ONE node" · [`docs/guides/component-guide.md`](../guides/component-guide.md)
> §Build contract · [`src/uix/eidos/components/README.md`](../../src/uix/eidos/components/README.md)
> · el precedente ejecutado y firmado: [`PLAN-sidebar.md`](./PLAN-sidebar.md)
> §3 + F3 (el eje `size` de la fila, 2026-08-19) y `navigation-menu`
> (`ae9e277fa` · `a82794845`, el cromo del trigger en tokens públicos).
---
## 0. Veredicto corto
1. **La doctrina ya lo exige; los guards no lo comprueban.** recipe-contract §1:
«Every recipe declares its knobs in `lib/recipes/base.ts`»; theming §6: el
token público de componente es `--{c}-{slot}`; la TSC decide dónde se emite.
Pero R-1…R-4 comprueban que **no haya literales** (y que lo que haya sea un
`var(`), no que cada knob sea **alcanzable por un tema**. Una receta con
todo en `var(--radius-md)` y en privados `--_{c}-*` pasa `component:audit`
en PASS y la dimensión 10 «Adopción theming» de la ficha en ✓ — y sólo se
puede temar moviendo el planeta. Es un error de framework porque rompe la
promesa que el framework hace por escrito.
2. **Medido, no opinado** (§1, `scripts/theming-census.ts`): de 5.205 knobs
de apariencia en 162 recetas, **1.621 (33 %) pasan por un token público del
componente**; 1.880 atan directo a un primitivo global; 856 a privados;
614 son literales. **62 componentes no tienen una sola entrada en el
contrato**; 59 tienen alcance < 20 %; 6 están al 100 %. `navigation-menu`,
tras el trabajo de hoy, está en 44 %: el eje no se cierra arreglando un
componente.
3. **La corrección es mecánica y ya tiene forma firmada**: la de la fila del
`Sidebar` (D-SB.5/D-SB.6, `PLAN-sidebar.md` §3) y la del trigger del nav
(`ae9e277fa`): tokens públicos por talla que apuntan al bundle
`--size-{k}-*`, nombre RESUELTO por `data-size`, acentos de estado en
`background-color`, y **el default queda idéntico** — sólo cambia quién
puede moverlo. Lo que NO hay que hacer: rediseñar nada por el camino.
4. **Sin guard no hay cierre**: el censo de hoy se repetirá en seis meses si
R-5 no existe. F0 crea el guard en `warn` con la cifra de hoy como suelo; F3
lo gradúa a `error` cuando el backfill termina. Igual que hizo R-4.x el
2026-07-02.
---
## 1. Inventario medido (2026-08-19)
**Instrumento**: `node --import tsx/esm scripts/theming-census.ts` (añadido
con este plan; `--json` para máquina; `--only {c}` por componente). Cuenta, por
receta CSS, cada declaración de APARIENCIA (lista `KNOB_PROPS` en el script:
fondo, tinta, borde, radio, sombra, padding, gap, tipografía, tamaño, opacidad,
fill/stroke, filtros) y la clasifica por el ORIGEN de su valor:
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y no por la memoria de nadie. `CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia calendar, mandato de Field, el perímetro del knob para la familia tipográfica, el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes → después. El plan gana su §8 con el registro completo y corrige su cabecera, que seguía diciendo que no había nada construido. Y se commitea el instrumental, porque sin él el protocolo §7 es una intención: la sonda de computed antes/después, el diff que es el gate (vacío o se para), el centinela que pregunta si cada token alcanza de verdad, y la captura del stage. Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev server. Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el `animationend` para montar el panel, y si lo congelas mides un popup que no existe); un token resuelto no se mueve desde `:root` por diseño; un panel portalado no ve el ámbito del componente; la demo puede tapar el componente que mides; y un valor centinela igual al real se lee como «no efecto». Igual las cuatro trampas que costaron un commit cada una, con su guard al lado: la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el encabezado que un Edit se come al insertar una sección. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
| Clase | Qué es | ¿La alcanza un tema de componente? |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `public` | `var(--{c}-…)` — token del contrato del componente | **Sí** |
| `private` | `var(--_{c}-…)` — nombre interno | No (salvo que el privado DERIVE de un público — el script no lo distingue, cuenta como no) |
| `global` | cualquier otro `var(--…)`: `--space-*`, `--radius-*`, `--color-*`, `--font-size-*`, `--size-*`… | Sólo moviendo el sistema |
| `literal` | sin `var(`: `8px`, `1.25`, `#fff` | No, y además viola R-4/R-2 |
| `system` | sistemas transversales que una receta CONSUME: capa de estado, anillo de foco, planos de depth, motion, bandas z, opacidad, shape, `floating-gap` | Sí, **a nivel de sistema**, por diseño (recipe-contract §2) — fuera del ratio |
docs(theming): el plan del eje theme-reach — medir qué alcanza un tema, y corregir componente a componente El autor, al ver que el radio del trigger del nav vivía en un privado clavado a --radius-default: «¿los componentes son themables? si no lo son, es un error como framework». Lo es. La doctrina ya exige que cada receta declare sus knobs en recipes/base.ts (recipe-contract §1, theming §6), pero R-1…R-4 sólo comprueban «sin literales», no «alcanzable por un tema»: una receta con todo en var(--radius-md) y privados pasa component:audit en PASS y sólo se puede temar moviendo el sistema entero. Medido, no opinado — scripts/theming-census.ts (instrumento, NO guard): de 5.205 knobs de apariencia en 162 recetas, 1.621 (33 %) pasan por un token público del componente; 1.880 atan directo a un primitivo global, 856 a privados, 614 son literales; 62 componentes no tienen una sola entrada en el contrato; 59 tienen alcance < 20 %; 6 al 100 %. navigation-menu, tras todo el día de hoy, 44 %. El plan (PLAN-theming.md): §1 el censo por familia y por componente (tabla regenerable, no editable) · §2 los cuatro ejes de «personalizable» (tokens · talla · color/variante · estados) · §3 la regla R-5 «alcance de tema» para component-audit, con su válvula de excepción · §4 ocho decisiones D-TH para el autor (R-5 dura · perímetro de knob · WIP · orden · DEFAULT IDÉNTICO · normalizar nombres de slot, que hoy incumplen tabs y el propio nav · cuándo gradúa a error · panel de temas) · §5 fases F0–F4 con gates · §6 deuda transversal · §7 el PROTOCOLO DE VERIFICACIÓN por componente, porque «lo ejecutará Opus, y Opus falla mucho»: cinco preguntas, medir ANTES (computed por parte/estado/talla, sonda headless desde la raíz, fuera de callbacks de MutationObserver), editar sólo la forma firmada, medir DESPUÉS (diff de computed = 0, prueba de CENTINELA por cada token nuevo, desde el píxel hacia arriba, registro de animationstart/end), guards por fichero, un componente = un commit con sus artefactos, y revisión adversarial por bloque. Cada paso tiene un artefacto; sin artefacto no está hecho. El instrumento se probó por mutación antes de creerle, y falló la primera vez: no contaba una regla de una sola línea (`[x] { padding-inline: 8px; }`), así que meter un literal no movía la cifra. Regex endurecido; ahora 6→7→8 con una y dos mutaciones. Las cifras del plan son las del instrumento final, y el handoff (CONTINUE-theming.md) obliga a reproducirlas antes de seguir. Nada firmado, nada construido. Gates del commit: docs:check 0/642 · prettier del script limpio · tsc del script sin errores · el plan cita ficheros que existen (los once, comprobados). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
**Alcance** = `public / (public + private + global + literal)`.
### 1.1 Totales
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y no por la memoria de nadie. `CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia calendar, mandato de Field, el perímetro del knob para la familia tipográfica, el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes → después. El plan gana su §8 con el registro completo y corrige su cabecera, que seguía diciendo que no había nada construido. Y se commitea el instrumental, porque sin él el protocolo §7 es una intención: la sonda de computed antes/después, el diff que es el gate (vacío o se para), el centinela que pregunta si cada token alcanza de verdad, y la captura del stage. Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev server. Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el `animationend` para montar el panel, y si lo congelas mides un popup que no existe); un token resuelto no se mueve desde `:root` por diseño; un panel portalado no ve el ámbito del componente; la demo puede tapar el componente que mides; y un valor centinela igual al real se lee como «no efecto». Igual las cuatro trampas que costaron un commit cada una, con su guard al lado: la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el encabezado que un Edit se come al insertar una sección. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
| Medida | Valor |
| -------------------------------------- | ---------------- |
| Componentes con receta CSS | 162 |
| Knobs de apariencia | 5.205 |
| Por token público del componente | **1.621 (33 %)** |
| Por privado | 856 |
| Directo a primitivo global | 1.880 |
| Literales | 614 |
| Sistema transversal (fuera del ratio) | 234 |
| **Sin ninguna entrada en el contrato** | **62** |
| Alcance < 20 % | 59 |
| Alcance = 100 % | 6 |
| Con eje `data-size` en la receta | 82 |
docs(theming): el plan del eje theme-reach — medir qué alcanza un tema, y corregir componente a componente El autor, al ver que el radio del trigger del nav vivía en un privado clavado a --radius-default: «¿los componentes son themables? si no lo son, es un error como framework». Lo es. La doctrina ya exige que cada receta declare sus knobs en recipes/base.ts (recipe-contract §1, theming §6), pero R-1…R-4 sólo comprueban «sin literales», no «alcanzable por un tema»: una receta con todo en var(--radius-md) y privados pasa component:audit en PASS y sólo se puede temar moviendo el sistema entero. Medido, no opinado — scripts/theming-census.ts (instrumento, NO guard): de 5.205 knobs de apariencia en 162 recetas, 1.621 (33 %) pasan por un token público del componente; 1.880 atan directo a un primitivo global, 856 a privados, 614 son literales; 62 componentes no tienen una sola entrada en el contrato; 59 tienen alcance < 20 %; 6 al 100 %. navigation-menu, tras todo el día de hoy, 44 %. El plan (PLAN-theming.md): §1 el censo por familia y por componente (tabla regenerable, no editable) · §2 los cuatro ejes de «personalizable» (tokens · talla · color/variante · estados) · §3 la regla R-5 «alcance de tema» para component-audit, con su válvula de excepción · §4 ocho decisiones D-TH para el autor (R-5 dura · perímetro de knob · WIP · orden · DEFAULT IDÉNTICO · normalizar nombres de slot, que hoy incumplen tabs y el propio nav · cuándo gradúa a error · panel de temas) · §5 fases F0–F4 con gates · §6 deuda transversal · §7 el PROTOCOLO DE VERIFICACIÓN por componente, porque «lo ejecutará Opus, y Opus falla mucho»: cinco preguntas, medir ANTES (computed por parte/estado/talla, sonda headless desde la raíz, fuera de callbacks de MutationObserver), editar sólo la forma firmada, medir DESPUÉS (diff de computed = 0, prueba de CENTINELA por cada token nuevo, desde el píxel hacia arriba, registro de animationstart/end), guards por fichero, un componente = un commit con sus artefactos, y revisión adversarial por bloque. Cada paso tiene un artefacto; sin artefacto no está hecho. El instrumento se probó por mutación antes de creerle, y falló la primera vez: no contaba una regla de una sola línea (`[x] { padding-inline: 8px; }`), así que meter un literal no movía la cifra. Regex endurecido; ahora 6→7→8 con una y dos mutaciones. Las cifras del plan son las del instrumento final, y el handoff (CONTINUE-theming.md) obliga a reproducirlas antes de seguir. Nada firmado, nada construido. Gates del commit: docs:check 0/642 · prettier del script limpio · tsc del script sin errores · el plan cita ficheros que existen (los once, comprobados). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
### 1.2 Por familia (propuesta de familias en D-TH.4 — el autor la firma o la cambia)
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y no por la memoria de nadie. `CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia calendar, mandato de Field, el perímetro del knob para la familia tipográfica, el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes → después. El plan gana su §8 con el registro completo y corrige su cabecera, que seguía diciendo que no había nada construido. Y se commitea el instrumental, porque sin él el protocolo §7 es una intención: la sonda de computed antes/después, el diff que es el gate (vacío o se para), el centinela que pregunta si cada token alcanza de verdad, y la captura del stage. Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev server. Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el `animationend` para montar el panel, y si lo congelas mides un popup que no existe); un token resuelto no se mueve desde `:root` por diseño; un panel portalado no ve el ámbito del componente; la demo puede tapar el componente que mides; y un valor centinela igual al real se lee como «no efecto». Igual las cuatro trampas que costaron un commit cada una, con su guard al lado: la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el encabezado que un Edit se come al insertar una sección. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
| Familia | Comp. | Knobs | Alcance | Sin contrato | Fuera del contrato |
| ----------------------------------- | ----: | ----: | ------: | -----------: | -----------------: |
| Tiempo (chronos · fecha/hora) | 15 | 879 | 9 % | 9 | 774 |
| Campos | 24 | 905 | 38 % | 9 | 526 |
| Controles · marcas | 33 | 867 | 39 % | 9 | 502 |
| Menús · listas · navegación | 24 | 735 | 37 % | 10 | 432 |
| Superficies · overlays · estado | 34 | 807 | 45 % | 10 | 429 |
| WIP (`palabras`) | 1 | 471 | 22 % | 1 | 363 |
| Texto · tipografía | 12 | 238 | 9 % | 10 | 217 |
| Media · chat | 9 | 283 | 61 % | 1 | 104 |
| Layout (primitivos) | 9 | 14 | 93 % | 3 | 1 |
| (`separator`, sin familia asignada) | 1 | 6 | 67 % | 0 | 2 |
docs(theming): el plan del eje theme-reach — medir qué alcanza un tema, y corregir componente a componente El autor, al ver que el radio del trigger del nav vivía en un privado clavado a --radius-default: «¿los componentes son themables? si no lo son, es un error como framework». Lo es. La doctrina ya exige que cada receta declare sus knobs en recipes/base.ts (recipe-contract §1, theming §6), pero R-1…R-4 sólo comprueban «sin literales», no «alcanzable por un tema»: una receta con todo en var(--radius-md) y privados pasa component:audit en PASS y sólo se puede temar moviendo el sistema entero. Medido, no opinado — scripts/theming-census.ts (instrumento, NO guard): de 5.205 knobs de apariencia en 162 recetas, 1.621 (33 %) pasan por un token público del componente; 1.880 atan directo a un primitivo global, 856 a privados, 614 son literales; 62 componentes no tienen una sola entrada en el contrato; 59 tienen alcance < 20 %; 6 al 100 %. navigation-menu, tras todo el día de hoy, 44 %. El plan (PLAN-theming.md): §1 el censo por familia y por componente (tabla regenerable, no editable) · §2 los cuatro ejes de «personalizable» (tokens · talla · color/variante · estados) · §3 la regla R-5 «alcance de tema» para component-audit, con su válvula de excepción · §4 ocho decisiones D-TH para el autor (R-5 dura · perímetro de knob · WIP · orden · DEFAULT IDÉNTICO · normalizar nombres de slot, que hoy incumplen tabs y el propio nav · cuándo gradúa a error · panel de temas) · §5 fases F0–F4 con gates · §6 deuda transversal · §7 el PROTOCOLO DE VERIFICACIÓN por componente, porque «lo ejecutará Opus, y Opus falla mucho»: cinco preguntas, medir ANTES (computed por parte/estado/talla, sonda headless desde la raíz, fuera de callbacks de MutationObserver), editar sólo la forma firmada, medir DESPUÉS (diff de computed = 0, prueba de CENTINELA por cada token nuevo, desde el píxel hacia arriba, registro de animationstart/end), guards por fichero, un componente = un commit con sus artefactos, y revisión adversarial por bloque. Cada paso tiene un artefacto; sin artefacto no está hecho. El instrumento se probó por mutación antes de creerle, y falló la primera vez: no contaba una regla de una sola línea (`[x] { padding-inline: 8px; }`), así que meter un literal no movía la cifra. Regex endurecido; ahora 6→7→8 con una y dos mutaciones. Las cifras del plan son las del instrumento final, y el handoff (CONTINUE-theming.md) obliga a reproducirlas antes de seguir. Nada firmado, nada construido. Gates del commit: docs:check 0/642 · prettier del script limpio · tsc del script sin errores · el plan cita ficheros que existen (los once, comprobados). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
### 1.3 Los tres defectos que el censo NO ve y la ejecución sí tiene que mirar
El script es un regex sobre texto: sobre-reporta, nunca infra-reporta. Tres
clases de defecto de theming que un alcance del 100 % no garantiza:
- **Privado que no deriva de un público** (`--_c-radius: var(--radius-default)`):
cuenta como `private`; la corrección es que el privado lea el público o
desaparezca.
- **Velo o acento en el nodo equivocado**: `archetype: 'item'` en un `<li>`
envoltorio (`navigation-menu`, `822b78cf2`), o el shorthand `background`
matando la capa de estado (`Sidebar` F3, `navigation-menu` `ae9e277fa`). No
es un knob: es un nodo. Se detecta midiendo desde el píxel hacia arriba (§7.3).
- **Doble animación al mover un sello** a una superficie con animación propia
(`navigation-menu` `822b78cf2`: `present-rise` + la entrada de receta). Se
detecta con un registro de `animationstart/end`, no con `getComputedStyle`.
### 1.4 Tabla completa por componente
Al final del documento (§9), ordenada por alcance. Se REGENERA con el script
en cada bloque; no se edita a mano.
---
## 2. Contrato objetivo — qué significa «totalmente personalizable» (los cuatro ejes)
Un componente es personalizable cuando un tema puede moverlo **sin tocar el
sistema ni la receta**, en estos ejes:
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y no por la memoria de nadie. `CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia calendar, mandato de Field, el perímetro del knob para la familia tipográfica, el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes → después. El plan gana su §8 con el registro completo y corrige su cabecera, que seguía diciendo que no había nada construido. Y se commitea el instrumental, porque sin él el protocolo §7 es una intención: la sonda de computed antes/después, el diff que es el gate (vacío o se para), el centinela que pregunta si cada token alcanza de verdad, y la captura del stage. Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev server. Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el `animationend` para montar el panel, y si lo congelas mides un popup que no existe); un token resuelto no se mueve desde `:root` por diseño; un panel portalado no ve el ámbito del componente; la demo puede tapar el componente que mides; y un valor centinela igual al real se lee como «no efecto». Igual las cuatro trampas que costaron un commit cada una, con su guard al lado: la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el encabezado que un Edit se come al insertar una sección. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
| Eje | Mecanismo canónico | Cómo se comprueba |
| -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| **A · Tokens** (forma, color, espacio, tipografía) | todo knob de apariencia lee `--{c}-{slot}` del contrato (`recipes/base.ts`), nombre RESUELTO por talla/estado; los privados sólo derivan de públicos | censo al 100 % **+** prueba de centinela (§7.4) |
| **B · Talla** | `size` → `data-size` en el wrapper; tokens `{part}-{eje}-{k}` apuntando al bundle `--size-{k}-*` (theming §5; recipe-contract §1) | computed por talla = bundle; `md` idéntico al default anterior |
| **C · Color / variante** | `data-color` / `data-variant` vía paleta (`--{c}-{role}-{slot}`, `--{c}-palette-*`), los 9 roles (theming §4) | cada variante lee tokens, ninguna un `--scale-*` (R-4.6) |
| **D · Estados** | hover/press/selected neutros = la capa del sistema en el nodo CON forma; acentos de estado (`on-bg`…) en `background-color` para que la capa componga | `background-image` presente en hover, `<li>`/envoltorios sin velo, abierto + hover = acento + capa |
docs(theming): el plan del eje theme-reach — medir qué alcanza un tema, y corregir componente a componente El autor, al ver que el radio del trigger del nav vivía en un privado clavado a --radius-default: «¿los componentes son themables? si no lo son, es un error como framework». Lo es. La doctrina ya exige que cada receta declare sus knobs en recipes/base.ts (recipe-contract §1, theming §6), pero R-1…R-4 sólo comprueban «sin literales», no «alcanzable por un tema»: una receta con todo en var(--radius-md) y privados pasa component:audit en PASS y sólo se puede temar moviendo el sistema entero. Medido, no opinado — scripts/theming-census.ts (instrumento, NO guard): de 5.205 knobs de apariencia en 162 recetas, 1.621 (33 %) pasan por un token público del componente; 1.880 atan directo a un primitivo global, 856 a privados, 614 son literales; 62 componentes no tienen una sola entrada en el contrato; 59 tienen alcance < 20 %; 6 al 100 %. navigation-menu, tras todo el día de hoy, 44 %. El plan (PLAN-theming.md): §1 el censo por familia y por componente (tabla regenerable, no editable) · §2 los cuatro ejes de «personalizable» (tokens · talla · color/variante · estados) · §3 la regla R-5 «alcance de tema» para component-audit, con su válvula de excepción · §4 ocho decisiones D-TH para el autor (R-5 dura · perímetro de knob · WIP · orden · DEFAULT IDÉNTICO · normalizar nombres de slot, que hoy incumplen tabs y el propio nav · cuándo gradúa a error · panel de temas) · §5 fases F0–F4 con gates · §6 deuda transversal · §7 el PROTOCOLO DE VERIFICACIÓN por componente, porque «lo ejecutará Opus, y Opus falla mucho»: cinco preguntas, medir ANTES (computed por parte/estado/talla, sonda headless desde la raíz, fuera de callbacks de MutationObserver), editar sólo la forma firmada, medir DESPUÉS (diff de computed = 0, prueba de CENTINELA por cada token nuevo, desde el píxel hacia arriba, registro de animationstart/end), guards por fichero, un componente = un commit con sus artefactos, y revisión adversarial por bloque. Cada paso tiene un artefacto; sin artefacto no está hecho. El instrumento se probó por mutación antes de creerle, y falló la primera vez: no contaba una regla de una sola línea (`[x] { padding-inline: 8px; }`), así que meter un literal no movía la cifra. Regex endurecido; ahora 6→7→8 con una y dos mutaciones. Las cifras del plan son las del instrumento final, y el handoff (CONTINUE-theming.md) obliga a reproducirlas antes de seguir. Nada firmado, nada construido. Gates del commit: docs:check 0/642 · prettier del script limpio · tsc del script sin errores · el plan cita ficheros que existen (los once, comprobados). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
**Regla de oro del eje**: **el default no cambia.** Cada componente se mide
antes y después en sus estados (reposo · hover · activo/abierto · disabled ·
foco) y tallas, y el diff de computed styles es **cero** salvo donde una
decisión firmada diga lo contrario. Este eje mueve QUIÉN puede cambiar cada
cosa, no QUÉ se ve.
---
## 3. Contrato objetivo — la regla R-5 «alcance de tema»
Nueva regla de `component-audit` (familia R-5), misma mecánica que R-4.x:
- **R-5.1** — Toda declaración de apariencia (`KNOB_PROPS`) en una receta lee
(a) un público `var(--{c}-…)`, (b) un privado `--_{c}-*` cuya definición en
la misma receta lee un público, o (c) un token de sistema transversal (la
lista `SYSTEM` del censo). Otra cosa es fallo.
- **R-5.2** — Todo componente con receta tiene entrada en `recipes/base.ts`.
- **R-5.3** — Los nombres de los tokens siguen recipe-contract §1 (ejes
lógicos, `{part}-{eje}-{k}`) y theming §6 regla 7 (slots de color: `bg, fg,
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y no por la memoria de nadie. `CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia calendar, mandato de Field, el perímetro del knob para la familia tipográfica, el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes → después. El plan gana su §8 con el registro completo y corrige su cabecera, que seguía diciendo que no había nada construido. Y se commitea el instrumental, porque sin él el protocolo §7 es una intención: la sonda de computed antes/después, el diff que es el gate (vacío o se para), el centinela que pregunta si cada token alcanza de verdad, y la captura del stage. Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev server. Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el `animationend` para montar el panel, y si lo congelas mides un popup que no existe); un token resuelto no se mueve desde `:root` por diseño; un panel portalado no ve el ámbito del componente; la demo puede tapar el componente que mides; y un valor centinela igual al real se lee como «no efecto». Igual las cuatro trampas que costaron un commit cada una, con su guard al lado: la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el encabezado que un Edit se come al insertar una sección. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
border, on-bg, on-fg, on-border, hover-bg, on-hover-bg`). **Hoy no se
docs(theming): el plan del eje theme-reach — medir qué alcanza un tema, y corregir componente a componente El autor, al ver que el radio del trigger del nav vivía en un privado clavado a --radius-default: «¿los componentes son themables? si no lo son, es un error como framework». Lo es. La doctrina ya exige que cada receta declare sus knobs en recipes/base.ts (recipe-contract §1, theming §6), pero R-1…R-4 sólo comprueban «sin literales», no «alcanzable por un tema»: una receta con todo en var(--radius-md) y privados pasa component:audit en PASS y sólo se puede temar moviendo el sistema entero. Medido, no opinado — scripts/theming-census.ts (instrumento, NO guard): de 5.205 knobs de apariencia en 162 recetas, 1.621 (33 %) pasan por un token público del componente; 1.880 atan directo a un primitivo global, 856 a privados, 614 son literales; 62 componentes no tienen una sola entrada en el contrato; 59 tienen alcance < 20 %; 6 al 100 %. navigation-menu, tras todo el día de hoy, 44 %. El plan (PLAN-theming.md): §1 el censo por familia y por componente (tabla regenerable, no editable) · §2 los cuatro ejes de «personalizable» (tokens · talla · color/variante · estados) · §3 la regla R-5 «alcance de tema» para component-audit, con su válvula de excepción · §4 ocho decisiones D-TH para el autor (R-5 dura · perímetro de knob · WIP · orden · DEFAULT IDÉNTICO · normalizar nombres de slot, que hoy incumplen tabs y el propio nav · cuándo gradúa a error · panel de temas) · §5 fases F0–F4 con gates · §6 deuda transversal · §7 el PROTOCOLO DE VERIFICACIÓN por componente, porque «lo ejecutará Opus, y Opus falla mucho»: cinco preguntas, medir ANTES (computed por parte/estado/talla, sonda headless desde la raíz, fuera de callbacks de MutationObserver), editar sólo la forma firmada, medir DESPUÉS (diff de computed = 0, prueba de CENTINELA por cada token nuevo, desde el píxel hacia arriba, registro de animationstart/end), guards por fichero, un componente = un commit con sus artefactos, y revisión adversarial por bloque. Cada paso tiene un artefacto; sin artefacto no está hecho. El instrumento se probó por mutación antes de creerle, y falló la primera vez: no contaba una regla de una sola línea (`[x] { padding-inline: 8px; }`), así que meter un literal no movía la cifra. Regex endurecido; ahora 6→7→8 con una y dos mutaciones. Las cifras del plan son las del instrumento final, y el handoff (CONTINUE-theming.md) obliga a reproducirlas antes de seguir. Nada firmado, nada construido. Gates del commit: docs:check 0/642 · prettier del script limpio · tsc del script sin errores · el plan cita ficheros que existen (los once, comprobados). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
cumple** ni en recetas viejas (`tabs`: `trigger-color-*`) ni en las de hoy
(`navigation-menu`: `trigger-bg-active`, `trigger-color-active`) — ver
D-TH.6.
- **Válvula** (recipe-contract §3): `R-5.1 exception: <razón>` en el README
del componente, greppable, UNA por knob o grupo de knobs con la razón (p. ej.
«glifo ∝ diámetro, excepción canónica avatar/marker»). Sin razón escrita no
hay excepción.
docs(theming): el acta — las catorce decisiones que faltaban, firmadas El eje llevaba desde el 19 ejecutando sólo lo que no dependía de una firma. Hoy se presentaron las catorce con recomendación fundada y el autor las firmó en bloque. Quedan escritas donde se ejecutan: §4 del plan fila a fila, y una sección de acta en el handoff con el QUÉ ejecutable de cada una. La que cambió de sentido al presentarla fue D-TH.6. La pregunta no era «normalizar hacia lo documentado» sino una CONTRADICCIÓN entre dos doctrinas firmadas: theming §6.7 r7 dice que el slot de tinta es `fg`; el principio de plataforma del codemod px/py («el token se llama como la propiedad») dice `color`. Se adjudicó `fg`, y con una razón que acota el principio en vez de romperlo: gobierna los ejes DIMENSIONALES, no la pareja `bg`/`fg` — si la gobernara, `bg` tendría que llamarse `background-color`, y nadie lo propone. De la misma lectura salió que el inventario heredado estaba inflado: de las 370 claves «desviadas», 47 son `{rol}-{slot-de-rol}` (`primary-solid-hover`, `palette-hover`) donde COLOR_ROLE_SLOTS pone el modificador detrás POR CONSTRUCCIÓN. Un codemod sobre las 370 las habría roto — `button` entero. El inventario real es 301, y la gramática es POR FAMILIA, no única. Los 22 «falsos amigos» resultaron ser tres cosas distintas: los que se quedan (la familia del sistema `--focus-ring-*`, el color como sustantivo de `aura`, y `stop-color` que ES una parte de gradient-builder), los que el morfo ya resuelve (`partial`, `read`, `failed` son valores declarados de `data-state` y `data-delivery`), y los dos `scrim-color-on-*`, que chocaban con el prefijo canónico `on-` del acento y pasan a `scrim-fg-over-*`. Y una que no era limpieza sino decisión: los hovers neutros NO se renombran, MIGRAN a la capa de estado (§38 + R-4.3). Renombrarlos habría presumido que sobreviven. Con las firmas, la revisión de los 7 componentes ya corregidos contra la gramática nueva: 6 limpios y una corrección (`gradient-builder.checker-color` → `checker-fg`), que entra en el codemod. Que salgan limpios no es suerte — el backfill de este eje ya venía escribiendo `fg`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- **Severidad**: `warn` desde F0 con el suelo del censo; `error` en F3 (D-TH.7
— firmada con enmienda: **R-5.3 gradúa a `error` directo tras el codemod**,
su deuda muere en el mismo pass; R-5.1/5.2 esperan F3).
docs(theming): el plan del eje theme-reach — medir qué alcanza un tema, y corregir componente a componente El autor, al ver que el radio del trigger del nav vivía en un privado clavado a --radius-default: «¿los componentes son themables? si no lo son, es un error como framework». Lo es. La doctrina ya exige que cada receta declare sus knobs en recipes/base.ts (recipe-contract §1, theming §6), pero R-1…R-4 sólo comprueban «sin literales», no «alcanzable por un tema»: una receta con todo en var(--radius-md) y privados pasa component:audit en PASS y sólo se puede temar moviendo el sistema entero. Medido, no opinado — scripts/theming-census.ts (instrumento, NO guard): de 5.205 knobs de apariencia en 162 recetas, 1.621 (33 %) pasan por un token público del componente; 1.880 atan directo a un primitivo global, 856 a privados, 614 son literales; 62 componentes no tienen una sola entrada en el contrato; 59 tienen alcance < 20 %; 6 al 100 %. navigation-menu, tras todo el día de hoy, 44 %. El plan (PLAN-theming.md): §1 el censo por familia y por componente (tabla regenerable, no editable) · §2 los cuatro ejes de «personalizable» (tokens · talla · color/variante · estados) · §3 la regla R-5 «alcance de tema» para component-audit, con su válvula de excepción · §4 ocho decisiones D-TH para el autor (R-5 dura · perímetro de knob · WIP · orden · DEFAULT IDÉNTICO · normalizar nombres de slot, que hoy incumplen tabs y el propio nav · cuándo gradúa a error · panel de temas) · §5 fases F0–F4 con gates · §6 deuda transversal · §7 el PROTOCOLO DE VERIFICACIÓN por componente, porque «lo ejecutará Opus, y Opus falla mucho»: cinco preguntas, medir ANTES (computed por parte/estado/talla, sonda headless desde la raíz, fuera de callbacks de MutationObserver), editar sólo la forma firmada, medir DESPUÉS (diff de computed = 0, prueba de CENTINELA por cada token nuevo, desde el píxel hacia arriba, registro de animationstart/end), guards por fichero, un componente = un commit con sus artefactos, y revisión adversarial por bloque. Cada paso tiene un artefacto; sin artefacto no está hecho. El instrumento se probó por mutación antes de creerle, y falló la primera vez: no contaba una regla de una sola línea (`[x] { padding-inline: 8px; }`), así que meter un literal no movía la cifra. Regex endurecido; ahora 6→7→8 con una y dos mutaciones. Las cifras del plan son las del instrumento final, y el handoff (CONTINUE-theming.md) obliga a reproducirlas antes de seguir. Nada firmado, nada construido. Gates del commit: docs:check 0/642 · prettier del script limpio · tsc del script sin errores · el plan cita ficheros que existen (los once, comprobados). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
---
## 4. Decisiones que firma el autor (una por mensaje, con sus cinco preguntas)
docs(theming): el acta — las catorce decisiones que faltaban, firmadas El eje llevaba desde el 19 ejecutando sólo lo que no dependía de una firma. Hoy se presentaron las catorce con recomendación fundada y el autor las firmó en bloque. Quedan escritas donde se ejecutan: §4 del plan fila a fila, y una sección de acta en el handoff con el QUÉ ejecutable de cada una. La que cambió de sentido al presentarla fue D-TH.6. La pregunta no era «normalizar hacia lo documentado» sino una CONTRADICCIÓN entre dos doctrinas firmadas: theming §6.7 r7 dice que el slot de tinta es `fg`; el principio de plataforma del codemod px/py («el token se llama como la propiedad») dice `color`. Se adjudicó `fg`, y con una razón que acota el principio en vez de romperlo: gobierna los ejes DIMENSIONALES, no la pareja `bg`/`fg` — si la gobernara, `bg` tendría que llamarse `background-color`, y nadie lo propone. De la misma lectura salió que el inventario heredado estaba inflado: de las 370 claves «desviadas», 47 son `{rol}-{slot-de-rol}` (`primary-solid-hover`, `palette-hover`) donde COLOR_ROLE_SLOTS pone el modificador detrás POR CONSTRUCCIÓN. Un codemod sobre las 370 las habría roto — `button` entero. El inventario real es 301, y la gramática es POR FAMILIA, no única. Los 22 «falsos amigos» resultaron ser tres cosas distintas: los que se quedan (la familia del sistema `--focus-ring-*`, el color como sustantivo de `aura`, y `stop-color` que ES una parte de gradient-builder), los que el morfo ya resuelve (`partial`, `read`, `failed` son valores declarados de `data-state` y `data-delivery`), y los dos `scrim-color-on-*`, que chocaban con el prefijo canónico `on-` del acento y pasan a `scrim-fg-over-*`. Y una que no era limpieza sino decisión: los hovers neutros NO se renombran, MIGRAN a la capa de estado (§38 + R-4.3). Renombrarlos habría presumido que sobreviven. Con las firmas, la revisión de los 7 componentes ya corregidos contra la gramática nueva: 6 limpios y una corrección (`gradient-builder.checker-color` → `checker-fg`), que entra en el codemod. Que salgan limpios no es suerte — el backfill de este eje ya venía escribiendo `fg`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
| Id | Decisión | Propuesta | Estado |
| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **D-TH.1** | ¿R-5 es regla DURA? («todo knob público salvo sistema transversal o excepción firmada») | Sí. Es lo que recipe-contract §1 ya dice sin guard. | **FIRMADA 2026-08-20** tal cual. Ancla: la medición 2026-07-01 (canon sin guard deriva 30-85 %) y el mandato origen del eje. |
| **D-TH.2** | Perímetro de «knob» | La lista `KNOB_PROPS` del script (apariencia; layout fuera). Cambios a la lista = cambio del guard, firmados aquí. | **FIRMADA 2026-08-20** con la opción (b) de la ficha `heading`: la familia tipográfica B5 mide su superficie por `--style-*` como sistema transversal (verdad estructural — la vertebración tipográfica RECHAZA acoplar componentes a la capa semántica; acuñar `--heading-*` = 36 alias de una capa ya pública, la clase que §39 mató). No se acuñan `--{c}-*` para los primitivos tipográficos. |
| **D-TH.3** | Tracks WIP (`palabras`, `words`, `chronos` y su familia de fecha/hora) | En el CENSO siempre; en R-5 `error` sólo cuando salgan de su carril (coherente con R-4.x, que los excluye). | **FIRMADA 2026-08-20** tal cual. |
| **D-TH.4** | Familias y ORDEN del backfill | El de §5 F2: menús/navegación → campos → controles → superficies → texto → media/chat → tiempo (el último por ser el más grande y estar en carril propio). Dentro de cada familia, sin contrato primero, luego por knobs fuera del contrato. | **FIRMADA 2026-08-20** con enmienda: la cola REVISADA del CONTINUE (fichas con veredicto §5 verificado) tiene prioridad sobre el orden de familias — trabajo revisado no se deja enfriar. |
| **D-TH.5** | El default NO cambia en este eje | Sí. Toda mejora visual que aparezca se anota como deuda y se firma aparte (como hoy con `navigation-menu`). | **FIRMADA 2026-08-20** sin matices — es lo que hace verificable el eje (diff de computed = 0 como gate). Única excepción viva: la migración hover→capa de estado, firmada aparte el mismo día (ver «Firmas del 2026-08-20» del CONTINUE), que mueve píxel con captura y diff explicado. |
| **D-TH.6** | Vocabulario de slots (R-5.3): ¿se NORMALIZAN los nombres que ya se desvían (`trigger-color-*` de `tabs`, `*-bg-active`/`*-color-active` del nav de hoy, …) con un codemod value-preserving como el de px/py del 2026-07-06? | Sí, en F1, ANTES del backfill — si no, el backfill escribirá más nombres fuera de vocabulario. Inventario exacto de desviaciones sale en F0 (el censo añade `--names`). | **FIRMADA COMPLETA 2026-08-20**, en dos actos. (1) El slot de tinta es **`fg`**; `color` como slot MUERE (196 claves cola). Razón: el principio de plataforma del 2026-07-06 gobierna los ejes DIMENSIONALES, no la pareja `bg`/`fg` (si gobernara, `bg` sería `background-color`); `color` colisiona con `--color-*`, `data-color` y los 9 roles; `on-fg` compone. (2) Modificador recipe-level DELANTE — regla: **delante lo interactivo, detrás lo dimensional y contextual** (`hover-bg` pero `control-height-md`, `scrim-fg-over-dark`); rol/paleta conserva su gramática propia (detrás, `COLOR_ROLE_SLOTS` — las 47 `{rol}-{slot-de-rol}` son canónicas). Modificador válido ∈ vocabulario universal (+`current`, aria) ∪ valores `data-state`/ejes del MORFO del componente (así `partial` de rating-group y `read/failed` de chat-message validan contra el compilado, igual que la parte). `waveform.played/buffered` = pseudo-partes eidos-only (`played-fg`). Los hovers neutros se EXCLUYEN del codemod: migran a la capa de estado (firma aparte, mismo día). Detalle operativo: CONTINUE §«Mañana». |
| **D-TH.7** | Cuándo gradúa R-5 a `error` | Cuando el censo global dé 100 % o excepción documentada (F3), nunca antes de terminar F2 — un `error` a medio backfill rompe el `component:audit` de todo el parque. | **FIRMADA 2026-08-20** con enmienda: **R-5.3 (gramática de nombres) gradúa a `error` DIRECTO tras el codemod** — su deuda muere en el mismo pass (precedente R-4.4, «sin allowlist»). R-5.1/R-5.2 esperan F3 como propone la fila. |
| **D-TH.8** | ¿Entra la exposición en la demo de temas (`/temas`: un panel que liste los tokens públicos por componente y permita moverlos)? | Sí pero DESPUÉS (F4, opcional): es la prueba viva de A; sin el backfill no hay nada que exponer. | **FIRMADA 2026-08-20** tal cual — el instrumental existe (`listRecipes()` / `getRecipeTokens()`; la ruta `/temas` existe). |
docs(theming): el plan del eje theme-reach — medir qué alcanza un tema, y corregir componente a componente El autor, al ver que el radio del trigger del nav vivía en un privado clavado a --radius-default: «¿los componentes son themables? si no lo son, es un error como framework». Lo es. La doctrina ya exige que cada receta declare sus knobs en recipes/base.ts (recipe-contract §1, theming §6), pero R-1…R-4 sólo comprueban «sin literales», no «alcanzable por un tema»: una receta con todo en var(--radius-md) y privados pasa component:audit en PASS y sólo se puede temar moviendo el sistema entero. Medido, no opinado — scripts/theming-census.ts (instrumento, NO guard): de 5.205 knobs de apariencia en 162 recetas, 1.621 (33 %) pasan por un token público del componente; 1.880 atan directo a un primitivo global, 856 a privados, 614 son literales; 62 componentes no tienen una sola entrada en el contrato; 59 tienen alcance < 20 %; 6 al 100 %. navigation-menu, tras todo el día de hoy, 44 %. El plan (PLAN-theming.md): §1 el censo por familia y por componente (tabla regenerable, no editable) · §2 los cuatro ejes de «personalizable» (tokens · talla · color/variante · estados) · §3 la regla R-5 «alcance de tema» para component-audit, con su válvula de excepción · §4 ocho decisiones D-TH para el autor (R-5 dura · perímetro de knob · WIP · orden · DEFAULT IDÉNTICO · normalizar nombres de slot, que hoy incumplen tabs y el propio nav · cuándo gradúa a error · panel de temas) · §5 fases F0–F4 con gates · §6 deuda transversal · §7 el PROTOCOLO DE VERIFICACIÓN por componente, porque «lo ejecutará Opus, y Opus falla mucho»: cinco preguntas, medir ANTES (computed por parte/estado/talla, sonda headless desde la raíz, fuera de callbacks de MutationObserver), editar sólo la forma firmada, medir DESPUÉS (diff de computed = 0, prueba de CENTINELA por cada token nuevo, desde el píxel hacia arriba, registro de animationstart/end), guards por fichero, un componente = un commit con sus artefactos, y revisión adversarial por bloque. Cada paso tiene un artefacto; sin artefacto no está hecho. El instrumento se probó por mutación antes de creerle, y falló la primera vez: no contaba una regla de una sola línea (`[x] { padding-inline: 8px; }`), así que meter un literal no movía la cifra. Regex endurecido; ahora 6→7→8 con una y dos mutaciones. Las cifras del plan son las del instrumento final, y el handoff (CONTINUE-theming.md) obliga a reproducirlas antes de seguir. Nada firmado, nada construido. Gates del commit: docs:check 0/642 · prettier del script limpio · tsc del script sin errores · el plan cita ficheros que existen (los once, comprobados). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
---
## 5. Fases y gates
Cada fase: **qué leer ANTES** (entero), **qué producir**, **guard**. Ninguna
arranca sin la anterior en verde. Todo bloque de F2 ejecuta el protocolo de §7
**por componente**, sin atajos: es la parte del plan que existe porque «Opus
falla mucho».
### F0 — Instrumento + guard en `warn` (una sesión)
- Leer: theming §1bis, §3, §5, §6, §7, §12, §16 · tsc.md entero ·
recipe-contract §1–§5 · `scripts/component-audit.ts` (cómo se declara una
regla R-x, `readmeException`, severidades) · `src/uix/eidos/recipe-css-contract.test.ts`
(qué ya guarda) · `PLAN-sidebar.md` §3 y F3 · este plan entero.
- Producir: (a) `scripts/theming-census.ts` endurecido: `--names` (inventario
de nombres de token fuera del vocabulario §1/§6.7), detección «privado que
deriva de público» (leer `--_c-x: var(--c-y)` en la misma receta y
reclasificar), salida `--json` estable; (b) **R-5.1/5.2/5.3 en
`component-audit`** con severidad `warn` y la válvula `R-5.x exception:`;
(c) `docs/theming/reference.md` §12 (tabla de tooling) gana la fila del censo;
(d) un test `scripts/theming-census.test.ts` o bajo `src/uix/eidos/` que
**fije el suelo**: alcance global ≥ el de hoy (33 %) y literales ≤ 614 — el
número SUBE o el test falla (muta-prueba: meter un literal en una receta y ver
el rojo antes de dar el test por bueno — memoria `a-guard-that-inspects-nothing-passes`.
Ya pasó una vez HOY: la primera versión del censo no contaba una regla de una
sola línea, y la mutación `padding-inline: 8px` no movió la cifra; se endureció
el regex y la mutación pasó a 6→7→8).
- Guard: `node --import tsx/esm scripts/theming-census.ts` reproduce §1.1 a
la cifra · `npm run component:audit` NO añade ningún `error` (sólo `warn`) ·
`npx vitest run src/uix/eidos` sin nuevos rojos (el rojo conocido es
`skin-media-player`, ajeno) · `npm run check` con cero errores en los
ficheros tocados (§7.6).
### F1 — Doctrina + vocabulario (una sesión)
- Leer: F0 + theming §6 (reglas estrictas) + el codemod del 2026-07-06
(recipe-contract §1, bloque «Normalización ejecutada») como molde.
- Producir: recipe-contract §1 gana la regla R-5 (y §2 una fila «Alcance de
tema»), §4 la tabla de enforcement, §5 el checklist (punto 12: «censo del
componente al 100 % o excepción»); theming §6 referencia cruzada; el
checklist de cierre (`docs/guides/completion-checklist.md`) y el
`COMPONENT_AUDIT_GUIDE` endurecen las dimensiones 5 y 10 («usa tokens» ≠
«es temable»: la ficha exige el % del censo). **D-TH.6**: codemod
value-preserving de los nombres fuera de vocabulario (lista de F0), con
`generated/base.css` regenerado y diff = renombres 1:1, computed idéntico en
navegador (mismo triple muro que px/py).
- Guard: `docs:check` 0 · el diff de `generated/base.css` son SOLO renombres ·
censo idéntico antes/después del codemod (un rename no cambia el alcance) ·
sonda de computed en 3 componentes renombrados = idéntico.
docs(theming): el acta — las catorce decisiones que faltaban, firmadas El eje llevaba desde el 19 ejecutando sólo lo que no dependía de una firma. Hoy se presentaron las catorce con recomendación fundada y el autor las firmó en bloque. Quedan escritas donde se ejecutan: §4 del plan fila a fila, y una sección de acta en el handoff con el QUÉ ejecutable de cada una. La que cambió de sentido al presentarla fue D-TH.6. La pregunta no era «normalizar hacia lo documentado» sino una CONTRADICCIÓN entre dos doctrinas firmadas: theming §6.7 r7 dice que el slot de tinta es `fg`; el principio de plataforma del codemod px/py («el token se llama como la propiedad») dice `color`. Se adjudicó `fg`, y con una razón que acota el principio en vez de romperlo: gobierna los ejes DIMENSIONALES, no la pareja `bg`/`fg` — si la gobernara, `bg` tendría que llamarse `background-color`, y nadie lo propone. De la misma lectura salió que el inventario heredado estaba inflado: de las 370 claves «desviadas», 47 son `{rol}-{slot-de-rol}` (`primary-solid-hover`, `palette-hover`) donde COLOR_ROLE_SLOTS pone el modificador detrás POR CONSTRUCCIÓN. Un codemod sobre las 370 las habría roto — `button` entero. El inventario real es 301, y la gramática es POR FAMILIA, no única. Los 22 «falsos amigos» resultaron ser tres cosas distintas: los que se quedan (la familia del sistema `--focus-ring-*`, el color como sustantivo de `aura`, y `stop-color` que ES una parte de gradient-builder), los que el morfo ya resuelve (`partial`, `read`, `failed` son valores declarados de `data-state` y `data-delivery`), y los dos `scrim-color-on-*`, que chocaban con el prefijo canónico `on-` del acento y pasan a `scrim-fg-over-*`. Y una que no era limpieza sino decisión: los hovers neutros NO se renombran, MIGRAN a la capa de estado (§38 + R-4.3). Renombrarlos habría presumido que sobreviven. Con las firmas, la revisión de los 7 componentes ya corregidos contra la gramática nueva: 6 limpios y una corrección (`gradient-builder.checker-color` → `checker-fg`), que entra en el codemod. Que salgan limpios no es suerte — el backfill de este eje ya venía escribiendo `fg`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- **Repaso de lo ya ejecutado (2026-08-20)**: firmar una gramática obliga a
releer lo corregido ANTES de ella. Auditados los 7 de §8 contra `fg` +
familias (`scripts/__names-audit7.ts`): 6 limpios, y una sola corrección —
`gradient-builder.checker-color` → `checker-fg` (slot, no sustantivo; 4
consumos en `gradient-builder.css`). Las 4 claves `stop-color-*` del mismo
componente son falso amigo verificado (`stop-color` es la PARTE) y NO se
tocan. La corrección entra DENTRO del codemod. Detalle y tabla:
[`CONTINUE-theming.md`](./CONTINUE-theming.md) §«Primera posición».
docs(theming): el plan del eje theme-reach — medir qué alcanza un tema, y corregir componente a componente El autor, al ver que el radio del trigger del nav vivía en un privado clavado a --radius-default: «¿los componentes son themables? si no lo son, es un error como framework». Lo es. La doctrina ya exige que cada receta declare sus knobs en recipes/base.ts (recipe-contract §1, theming §6), pero R-1…R-4 sólo comprueban «sin literales», no «alcanzable por un tema»: una receta con todo en var(--radius-md) y privados pasa component:audit en PASS y sólo se puede temar moviendo el sistema entero. Medido, no opinado — scripts/theming-census.ts (instrumento, NO guard): de 5.205 knobs de apariencia en 162 recetas, 1.621 (33 %) pasan por un token público del componente; 1.880 atan directo a un primitivo global, 856 a privados, 614 son literales; 62 componentes no tienen una sola entrada en el contrato; 59 tienen alcance < 20 %; 6 al 100 %. navigation-menu, tras todo el día de hoy, 44 %. El plan (PLAN-theming.md): §1 el censo por familia y por componente (tabla regenerable, no editable) · §2 los cuatro ejes de «personalizable» (tokens · talla · color/variante · estados) · §3 la regla R-5 «alcance de tema» para component-audit, con su válvula de excepción · §4 ocho decisiones D-TH para el autor (R-5 dura · perímetro de knob · WIP · orden · DEFAULT IDÉNTICO · normalizar nombres de slot, que hoy incumplen tabs y el propio nav · cuándo gradúa a error · panel de temas) · §5 fases F0–F4 con gates · §6 deuda transversal · §7 el PROTOCOLO DE VERIFICACIÓN por componente, porque «lo ejecutará Opus, y Opus falla mucho»: cinco preguntas, medir ANTES (computed por parte/estado/talla, sonda headless desde la raíz, fuera de callbacks de MutationObserver), editar sólo la forma firmada, medir DESPUÉS (diff de computed = 0, prueba de CENTINELA por cada token nuevo, desde el píxel hacia arriba, registro de animationstart/end), guards por fichero, un componente = un commit con sus artefactos, y revisión adversarial por bloque. Cada paso tiene un artefacto; sin artefacto no está hecho. El instrumento se probó por mutación antes de creerle, y falló la primera vez: no contaba una regla de una sola línea (`[x] { padding-inline: 8px; }`), así que meter un literal no movía la cifra. Regex endurecido; ahora 6→7→8 con una y dos mutaciones. Las cifras del plan son las del instrumento final, y el handoff (CONTINUE-theming.md) obliga a reproducirlas antes de seguir. Nada firmado, nada construido. Gates del commit: docs:check 0/642 · prettier del script limpio · tsc del script sin errores · el plan cita ficheros que existen (los once, comprobados). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
### F2 — Backfill por familias (6–8 bloques de sesión; **un componente = un commit**)
Orden propuesto (D-TH.4). Dentro de cada bloque: sin contrato primero, luego
por knobs fuera del contrato. Cada componente sigue §7 entero.
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y no por la memoria de nadie. `CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia calendar, mandato de Field, el perímetro del knob para la familia tipográfica, el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes → después. El plan gana su §8 con el registro completo y corrige su cabecera, que seguía diciendo que no había nada construido. Y se commitea el instrumental, porque sin él el protocolo §7 es una intención: la sonda de computed antes/después, el diff que es el gate (vacío o se para), el centinela que pregunta si cada token alcanza de verdad, y la captura del stage. Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev server. Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el `animationend` para montar el panel, y si lo congelas mides un popup que no existe); un token resuelto no se mueve desde `:root` por diseño; un panel portalado no ve el ámbito del componente; la demo puede tapar el componente que mides; y un valor centinela igual al real se lee como «no efecto». Igual las cuatro trampas que costaron un commit cada una, con su guard al lado: la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el encabezado que un Edit se come al insertar una sección. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
| Bloque | Familia | Comp. | Knobs fuera | Entran primero (sin contrato) |
| ------ | ----------------------------------------------------- | ----: | ----------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| B1 | Menús · listas · navegación | 24 | 432 | `command`, `listbox`, `table`, `tree-grid`, `tree-view`, `grid-list`, `virtual-list`, `virtual-grid`, `menu-dial`, `feed` — y `navigation-menu` (44 %) hasta el 100 % |
| B2 | Campos | 24 | 526 | `combobox` (1), `textarea`, `css-field`, `mask-field`, `number-field`, `field-langs`, `gradient-builder`, `gradient-picker`, `label` |
| B3 | Controles · marcas | 33 | 502 | `knob`, `avatar`, `kbd`, `link`, `mark`, `highlight`, `clipboard`, `proof-of-human`, `aspect-ratio` |
| B4 | Superficies · overlays · estado | 34 | 429 | `alert-dialog`, `announce`, `carousel`, `link-preview`, `skeleton`, `spinner`, `scroll-frames`, `motion`, `mockup`, `cascade`, `card-group` |
| B5 | Texto · tipografía | 12 | 217 | `heading`, `text`, `s-text`, `display`, `code`, `code-block`, `text-*` (los que no tienen contrato) |
| B6 | Media · chat | 9 | 104 | `skin-media-player` |
| B7 | Tiempo (chronos · fecha/hora) — **si D-TH.3 lo mete** | 15 | 774 | `range-calendar`, `month-grid`, `year-grid`, `date-picker`, `date-range-picker`, `time-picker`, `time-range-picker`, `natural-time-picker`, `picker-shell` |
| B8 | `palabras` — **si D-TH.3 lo mete** | 1 | 363 | — |
docs(theming): el plan del eje theme-reach — medir qué alcanza un tema, y corregir componente a componente El autor, al ver que el radio del trigger del nav vivía en un privado clavado a --radius-default: «¿los componentes son themables? si no lo son, es un error como framework». Lo es. La doctrina ya exige que cada receta declare sus knobs en recipes/base.ts (recipe-contract §1, theming §6), pero R-1…R-4 sólo comprueban «sin literales», no «alcanzable por un tema»: una receta con todo en var(--radius-md) y privados pasa component:audit en PASS y sólo se puede temar moviendo el sistema entero. Medido, no opinado — scripts/theming-census.ts (instrumento, NO guard): de 5.205 knobs de apariencia en 162 recetas, 1.621 (33 %) pasan por un token público del componente; 1.880 atan directo a un primitivo global, 856 a privados, 614 son literales; 62 componentes no tienen una sola entrada en el contrato; 59 tienen alcance < 20 %; 6 al 100 %. navigation-menu, tras todo el día de hoy, 44 %. El plan (PLAN-theming.md): §1 el censo por familia y por componente (tabla regenerable, no editable) · §2 los cuatro ejes de «personalizable» (tokens · talla · color/variante · estados) · §3 la regla R-5 «alcance de tema» para component-audit, con su válvula de excepción · §4 ocho decisiones D-TH para el autor (R-5 dura · perímetro de knob · WIP · orden · DEFAULT IDÉNTICO · normalizar nombres de slot, que hoy incumplen tabs y el propio nav · cuándo gradúa a error · panel de temas) · §5 fases F0–F4 con gates · §6 deuda transversal · §7 el PROTOCOLO DE VERIFICACIÓN por componente, porque «lo ejecutará Opus, y Opus falla mucho»: cinco preguntas, medir ANTES (computed por parte/estado/talla, sonda headless desde la raíz, fuera de callbacks de MutationObserver), editar sólo la forma firmada, medir DESPUÉS (diff de computed = 0, prueba de CENTINELA por cada token nuevo, desde el píxel hacia arriba, registro de animationstart/end), guards por fichero, un componente = un commit con sus artefactos, y revisión adversarial por bloque. Cada paso tiene un artefacto; sin artefacto no está hecho. El instrumento se probó por mutación antes de creerle, y falló la primera vez: no contaba una regla de una sola línea (`[x] { padding-inline: 8px; }`), así que meter un literal no movía la cifra. Regex endurecido; ahora 6→7→8 con una y dos mutaciones. Las cifras del plan son las del instrumento final, y el handoff (CONTINUE-theming.md) obliga a reproducirlas antes de seguir. Nada firmado, nada construido. Gates del commit: docs:check 0/642 · prettier del script limpio · tsc del script sin errores · el plan cita ficheros que existen (los once, comprobados). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
Por componente se produce: entrada (o ampliación) en `recipes/base.ts` con los
nombres de §1/§6.7 — dimensionales por talla si el componente tiene `size`
(patrón `Sidebar`: `{part}-{eje}-{k}` + nombre resuelto con `declarations` por
`scope: 'size:{k}'`), colores por rol/slot, acentos de estado; la receta
consume SOLO nombres resueltos (nunca un primitivo crudo, nunca un literal);
privados sólo si derivan de públicos; `generated/base.css` regenerado por el
script (**jamás a mano**, theming §16 F); README de eidos con la tabla de
tokens («Talla y tema», molde: `navigation-menu`); registro en §8.
- Guard por componente: §7 entero, y el censo `--only {c}` al **100 %** (o
`R-5.x exception:` con razón) — si no llega, el commit NO se hace.
- Guard por bloque: **revisión adversarial** (§7.7) antes de dar el bloque por
cerrado: un pase escéptico por componente que intente REFUTAR «el default es
idéntico» y «cada token alcanza» con medición propia, no con la del autor
del commit.
### F3 — R-5 a `error` + cierre (una sesión)
- Producir: severidad `error` en R-5.1/5.2/5.3 (D-TH.7); el test del suelo
pasa a exigir 100 % global (o excepciones listadas); theming §12 y
recipe-contract §4 lo reflejan; `CONTINUE-theming.md` cerrado.
- Guard: `component:audit` PASS en los 162 · censo 100 % · `docs:check` 0.
### F4 — Exposición en la demo de temas (opcional, D-TH.8)
- Producir: en `/temas` (o en cada demo) un panel que lista los públicos del
componente desde `recipes/base.ts` y los mueve en vivo — la prueba viva del
eje A, y el detector de tokens que no alcanzan (mover y que nada cambie).
- Guard: mover cada token del componente en la demo y ver cambiar el computed
(automatizable con el centinela de §7.4).
---
## 6. Deuda transversal que este plan destapa — NO se ejecuta aquí
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y no por la memoria de nadie. `CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia calendar, mandato de Field, el perímetro del knob para la familia tipográfica, el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes → después. El plan gana su §8 con el registro completo y corrige su cabecera, que seguía diciendo que no había nada construido. Y se commitea el instrumental, porque sin él el protocolo §7 es una intención: la sonda de computed antes/después, el diff que es el gate (vacío o se para), el centinela que pregunta si cada token alcanza de verdad, y la captura del stage. Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev server. Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el `animationend` para montar el panel, y si lo congelas mides un popup que no existe); un token resuelto no se mueve desde `:root` por diseño; un panel portalado no ve el ámbito del componente; la demo puede tapar el componente que mides; y un valor centinela igual al real se lee como «no efecto». Igual las cuatro trampas que costaron un commit cada una, con su guard al lado: la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el encabezado que un Edit se come al insertar una sección. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
| Id | Dónde | Qué |
| ---------- | ----------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **T-TH.1** | `component-audit` dims 5 y 10 | Dan ✓ a «usa tokens»; el criterio se endurece en F1. Las fichas ya escritas (`docs/audit/components/*.md`) quedan con el ✓ viejo: se corrigen al pasar cada componente por F2, no en masa. |
| **T-TH.2** | Vocabulario de slots (theming §6.7) | Existe y no se guarda: `tabs` (`trigger-color-*`), `navigation-menu` (`-bg-active`, `-color-active`) y más lo incumplen. D-TH.6. |
| **T-TH.3** | `menubar` | Misma clase que el nav: cromo de barra en privados («the bar itself styles via foundation privates», `recipes/base.ts`) + `commit-select` en el trigger al DESPLEGAR (otra sesión, sema). |
| **T-TH.4** | `archetype: 'item'` en envoltorios | Hay que censar qué otras partes `<li>`/wrapper lo declaran (memoria `archetype-item-pulls-interactive-styling`: Timeline, Breadcrumb, NavigationMenu ya corregidos). Un guard de morfo («un `item` no envuelve a un `trigger`») es candidato. |
| **T-TH.5** | `--control-height-*` en `:root` (T-6 del sidebar) | La densidad/zoom por SUBÁRBOL no re-deriva; afecta a la promesa de theming «por ámbito». Eje de theming aparte. |
| **T-TH.6** | 25 knobs visuales fuera del morfo (`component-visual-attrs`, eje Affix) | Filas sin firmar `image/skeleton/qr-code/s-text`. El eje C (variantes) depende de ellas. |
docs(theming): el plan del eje theme-reach — medir qué alcanza un tema, y corregir componente a componente El autor, al ver que el radio del trigger del nav vivía en un privado clavado a --radius-default: «¿los componentes son themables? si no lo son, es un error como framework». Lo es. La doctrina ya exige que cada receta declare sus knobs en recipes/base.ts (recipe-contract §1, theming §6), pero R-1…R-4 sólo comprueban «sin literales», no «alcanzable por un tema»: una receta con todo en var(--radius-md) y privados pasa component:audit en PASS y sólo se puede temar moviendo el sistema entero. Medido, no opinado — scripts/theming-census.ts (instrumento, NO guard): de 5.205 knobs de apariencia en 162 recetas, 1.621 (33 %) pasan por un token público del componente; 1.880 atan directo a un primitivo global, 856 a privados, 614 son literales; 62 componentes no tienen una sola entrada en el contrato; 59 tienen alcance < 20 %; 6 al 100 %. navigation-menu, tras todo el día de hoy, 44 %. El plan (PLAN-theming.md): §1 el censo por familia y por componente (tabla regenerable, no editable) · §2 los cuatro ejes de «personalizable» (tokens · talla · color/variante · estados) · §3 la regla R-5 «alcance de tema» para component-audit, con su válvula de excepción · §4 ocho decisiones D-TH para el autor (R-5 dura · perímetro de knob · WIP · orden · DEFAULT IDÉNTICO · normalizar nombres de slot, que hoy incumplen tabs y el propio nav · cuándo gradúa a error · panel de temas) · §5 fases F0–F4 con gates · §6 deuda transversal · §7 el PROTOCOLO DE VERIFICACIÓN por componente, porque «lo ejecutará Opus, y Opus falla mucho»: cinco preguntas, medir ANTES (computed por parte/estado/talla, sonda headless desde la raíz, fuera de callbacks de MutationObserver), editar sólo la forma firmada, medir DESPUÉS (diff de computed = 0, prueba de CENTINELA por cada token nuevo, desde el píxel hacia arriba, registro de animationstart/end), guards por fichero, un componente = un commit con sus artefactos, y revisión adversarial por bloque. Cada paso tiene un artefacto; sin artefacto no está hecho. El instrumento se probó por mutación antes de creerle, y falló la primera vez: no contaba una regla de una sola línea (`[x] { padding-inline: 8px; }`), así que meter un literal no movía la cifra. Regex endurecido; ahora 6→7→8 con una y dos mutaciones. Las cifras del plan son las del instrumento final, y el handoff (CONTINUE-theming.md) obliga a reproducirlas antes de seguir. Nada firmado, nada construido. Gates del commit: docs:check 0/642 · prettier del script limpio · tsc del script sin errores · el plan cita ficheros que existen (los once, comprobados). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
---
## 7. PROTOCOLO DE VERIFICACIÓN — por componente, sin excepciones
Existe porque el que ejecuta falla, y porque en un solo día (2026-08-19,
`navigation-menu`) cuatro de estos pasos ausentes costaron cuatro commits de
corrección: se dio por arreglado un hover leyendo el nodo equivocado; se
resolvió un doble de animación en la dirección contraria a la doctrina; se
inventó un hover por componente; y se declaró «idéntico» sin captura. Cada paso
tiene un **artefacto** (número, captura, salida de comando) que va al mensaje
de commit. Sin artefacto, el paso no se ha hecho.
### 7.1 Antes de tocar el componente (las cinco preguntas + lectura)
1. Las cinco preguntas del autor, UN componente por mensaje cuando haya
decisión (nombres fuera de vocabulario, una excepción, un default que
parece defecto): qué soluciona · leído todo o presupuesto · qué afecta ·
qué doctrina · qué modificaciones. Si no hay decisión nueva (el componente
sólo necesita tokens con nombres canónicos), se ejecuta sin preguntar y se
reporta.
2. Leer ENTEROS: la receta del componente, su entrada en `recipes/base.ts`,
su README de eidos y de soma, su morfo (¿qué partes llevan arquetipo?), su
ficha en `docs/audit/components/`. Y la doctrina de §0 (fuentes) si la
sesión no la ha leído hoy.
3. `git status` + `git log -1`: ¿hay otra sesión con el árbol sucio?
**Nunca `git stash`** para medir una base (le arranca el trabajo a otro).
### 7.2 Medir ANTES (el suelo del «idéntico»)
4. `node --import tsx/esm scripts/theming-census.ts --only {c}` → guardar la
línea.
5. Sonda Playwright **headless desde la raíz del repo** (la pantalla oculta
congela rAF; `playwright` no resuelve desde el scratchpad) contra la demo
del componente: para cada parte visible y cada talla, computed de los
knobs (`backgroundColor`, `backgroundImage`, `color`, `borderRadius`,
`padding*`, `fontSize`, `lineHeight`, `blockSize`/`inlineSize`,
`boxShadow`, `borderColor`) en reposo · hover · activo/abierto ·
disabled · `:focus-visible`. Muestrear FUERA de callbacks de
`MutationObserver` (miden el instante anterior al flush). Guardar JSON.
6. Captura 2× de la demo en reposo y en hover (sirve de «antes» para la
comparación a ojo).
### 7.3 Editar (la forma firmada — nada más)
7. Tokens con los nombres de recipe-contract §1 / theming §6.7. Por talla si
hay `size`. Acentos de estado en `background-color`. Hover/press/selected
neutros = la capa del sistema (nunca `background:` a mano, nunca
`background-image: none` para «limpiar», nunca una escalera de color
propia). Privados sólo derivando de públicos. Literales: cero.
8. `npm run generate:eidos-css` y `git diff --stat src/uix/eidos/generated/`:
el diff son SOLO los tokens del componente. Si toca otra cosa, parar.
9. Si un envoltorio lleva `archetype: 'item'`/`'option'`/`'trigger'` sin ser
el interactivo, es una DECISIÓN (morfo): se presenta, no se toca de oficio.
### 7.4 Medir DESPUÉS (los tres artefactos)
10. **Idéntico**: repetir la sonda de 7.2 → diff de computed por parte/estado/talla
= **vacío**. Un solo valor distinto = parar y explicar (o es defecto
previo que se firma, o es error del commit).
uix(theming): revisión adversarial de F2-A — 8 hallazgos de 34, y nace R-5.4 Pase escéptico del bloque (§7.7, molde Sidebar) con medición propia: worktree con SOLO el CSS revertido a la base del bloque (DOM de demo constante — prueba más dura que la del autor), sonda x2 por componente para fijar el suelo de ruido. 34 hipótesis · 26 refutadas · 8 reales. Lo que NO se pudo romper: el «default idéntico» aguanta en los ocho (0 diffs; command 8.091 · table 13.050 · media-player 3.277 · gradient-picker 609 · listbox 2.813 · carousel 2.842; los 3 de tree-grid y los 5 de feed salen idénticos corriendo DOS veces el mismo código → instrumento); 0 tokens huérfanos de 336; el bundle ES 1:1 con los primitivos; el «verificado a mano» de gradient-picker era verdad (15/16 alcanzan al montar); sin doble animación; eidos-lint 0 invalid. Los 4 de código, arreglados con diff de computed VACÍO (re-medido: command 8.091 y carousel 2.842 valores, 0 diffs): 1. command: el re-point de `--command-radius` dentro del Dialog era una declaración muerta con comentario falso — la misma regla pone el radio a 0 y el input lee `--command-input-radius`, nunca este token (medido: `--dialog-content-radius: 9999px` no movía NADA; `--command-radius: 77px` movía la paleta inline y no la del dialog). Retirado; comentarios y README reescritos con lo medido. 2. carousel: la regla vertical del indicador activo llevaba el `2.25` a pelo mientras la horizontal leía `active-indicator-scale` — el token alcanzaba media superficie (scale:6 → horizontal 18→48px, vertical 18→18px). Ahora lo leen las dos. 3. carousel: el trío `item-gap*` no podía ganar NUNCA — soma estampa `gap` INLINE desde la prop (lo necesita para el flex-basis; reglas casadas por CDP: la de la receta y encima INLINE gap:0rem). Retirados los 3 tokens y las 2 declaraciones muertas (la base y el gap:0 de ghost): 0 diffs, que es la prueba. Canal de valor de soma, como --gp-current-gradient. Censo carousel 35 knobs · 23 públicos · 77 % · 27 claves; global 43 % intacto (5.190 · 2.102). 4. table: `striped-row-bg` no alcanza NADA en la demo publicada — `:nth-of-type(even)` cuenta los <tr> de RowDetail y las filas de datos quedan impares (medido tr a tr: diez inmunes). Arreglarlo mueve píxel → PENDIENTE DE FIRMA (candidato `:nth-child(even of [data-table-row])`); adjudicado en el ledger mientras tanto. El instrumento cargaba 22 falsos negativos de 26 «no effect», con tres causas medidas, y la cuarta era de la sonda: - el paso de abrir hacía CLIC en el input, y un clic de ratón sobre un input de texto SÍ casa `:focus-visible` — la regla de foco pintaba el borde y el token de reposo leía muerto (era el «falso negativo sin causa» de §13); - el override se escribía solo en `[data-{c}]`, y las partes que cuelgan de `{c}-root` no lo recibían; - `::before`/`::after` eran invisibles (el aro de buffering, el spinner); - la no-determinación de feed no era una animación sin localizar: era la demo aún CARGANDO — el auto load-more mantiene [data-busy] ~3 s y la firma `commit-settle` anima box-shadow sobre el nodo medido. Con la espera de asentado: 0 diffs y 0 nodos ausentes sobre 6.264 valores, dos corridas. Con eso NACE EL GUARD R-5.4 (la deuda que §13 pedía desde gradient-picker, y que carousel convirtió en patrón — un estilo inline de otra capa es invisible para todo análisis estático): `npm run theming:sentinel -- <c> <url>` (scripts/theming-sentinel.ts, promovido de `__`; blur tras abrir, override en todos los nodos del componente, pseudos, pase de hover, settle) + ledger scripts/theming-sentinel-exceptions.ts con UNA razón medida por token — muerto sin adjudicar = exit 1, excepción STALE = aviso. Los ocho componentes en verde: command 50/57 · table 41/45 · media-player 46/61 · tree-grid 45/47 · gradient-picker 9/25 · listbox 20/21 · carousel 26/27 · feed 43/50, con 46 excepciones adjudicadas. El guard ya me corrigió a mí: cinco entradas mías salieron STALE (los control-height-* de media-player SÍ alcanzan) y las borró; y dos muertos nuevos los produjeron los propios arreglos (el blur apaga focus-input-border; el settle desmonta el spinner de feed) — adjudicados con su porqué. Doctrina en recipe-contract §4. También: tres cifras de especificidad de next-features §12 recontadas (la banda de tree-grid es (0,8,0); la tinta seleccionada del arquetipo es (0,4,0); y el `item` de table vive en la fila Y EN LA CELDA — elementsFromPoint pone el velo en la celda), el comentario de listbox que mandaba pisar el privado `--_listbox-max-height` apunta ahora al público, y quedan registrados en §13 los dos `header-z` con literal '2' y el spinner de media-player a 720ms mientras el gemelo de feed lleva tokens. Guards: censo global 43 % intacto · audit --only command+carousel PASS · eidos-lint 0 invalid (command 16/12, carousel 29/9, listbox 12/7) · vitest eidos 434/435 (el rojo es el conocido skin-media-player) · rtl 0/180 · docs 0/813 · check 0 errores en tocados (72 globales de otras sesiones) · prettier limpio en lo nuevo (los desvíos de command.css/carousel.css ya estaban en HEAD y no se tocan). Pendiente de firma (CONTINUE §«PENDIENTE DE FIRMA»): el striped inerte, el arquetipo en fila+celda, y que soma lea el gap de un token. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
11. **Alcanza**: la **prueba de centinela**, que desde 2026-08-21 es el guard
**R-5.4** (`npm run theming:sentinel -- {c} <url>`, doctrina en
recipe-contract §4) — por cada token público, un valor imposible de
confundir (`1234px`, `rgb(1, 2, 3)`, `0.123`) y algún computed tiene que
seguirlo. Si un token no mueve nada, es un token que miente (fantasma,
nombre equivocado, o una cascada que jamás gana) — o su silencio tiene una
razón MEDIDA escrita en el ledger
(`scripts/theming-sentinel-exceptions.ts`): el guard falla con un muerto
sin adjudicar y avisa de excepciones STALE.
docs(theming): el plan del eje theme-reach — medir qué alcanza un tema, y corregir componente a componente El autor, al ver que el radio del trigger del nav vivía en un privado clavado a --radius-default: «¿los componentes son themables? si no lo son, es un error como framework». Lo es. La doctrina ya exige que cada receta declare sus knobs en recipes/base.ts (recipe-contract §1, theming §6), pero R-1…R-4 sólo comprueban «sin literales», no «alcanzable por un tema»: una receta con todo en var(--radius-md) y privados pasa component:audit en PASS y sólo se puede temar moviendo el sistema entero. Medido, no opinado — scripts/theming-census.ts (instrumento, NO guard): de 5.205 knobs de apariencia en 162 recetas, 1.621 (33 %) pasan por un token público del componente; 1.880 atan directo a un primitivo global, 856 a privados, 614 son literales; 62 componentes no tienen una sola entrada en el contrato; 59 tienen alcance < 20 %; 6 al 100 %. navigation-menu, tras todo el día de hoy, 44 %. El plan (PLAN-theming.md): §1 el censo por familia y por componente (tabla regenerable, no editable) · §2 los cuatro ejes de «personalizable» (tokens · talla · color/variante · estados) · §3 la regla R-5 «alcance de tema» para component-audit, con su válvula de excepción · §4 ocho decisiones D-TH para el autor (R-5 dura · perímetro de knob · WIP · orden · DEFAULT IDÉNTICO · normalizar nombres de slot, que hoy incumplen tabs y el propio nav · cuándo gradúa a error · panel de temas) · §5 fases F0–F4 con gates · §6 deuda transversal · §7 el PROTOCOLO DE VERIFICACIÓN por componente, porque «lo ejecutará Opus, y Opus falla mucho»: cinco preguntas, medir ANTES (computed por parte/estado/talla, sonda headless desde la raíz, fuera de callbacks de MutationObserver), editar sólo la forma firmada, medir DESPUÉS (diff de computed = 0, prueba de CENTINELA por cada token nuevo, desde el píxel hacia arriba, registro de animationstart/end), guards por fichero, un componente = un commit con sus artefactos, y revisión adversarial por bloque. Cada paso tiene un artefacto; sin artefacto no está hecho. El instrumento se probó por mutación antes de creerle, y falló la primera vez: no contaba una regla de una sola línea (`[x] { padding-inline: 8px; }`), así que meter un literal no movía la cifra. Regex endurecido; ahora 6→7→8 con una y dos mutaciones. Las cifras del plan son las del instrumento final, y el handoff (CONTINUE-theming.md) obliga a reproducirlas antes de seguir. Nada firmado, nada construido. Gates del commit: docs:check 0/642 · prettier del script limpio · tsc del script sin errores · el plan cita ficheros que existen (los once, comprobados). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
12. **Desde el píxel hacia arriba** en los estados de hover: `elementsFromPoint`
en el centro del control y la pila de ancestros con `backgroundImage` /
`backgroundColor` — el velo cae en el nodo CON forma y en ningún
envoltorio (memoria `measure-the-node-the-user-points-at`). Si hay
animación propia y un sello de sema en el mismo nodo, registro de
`animationstart/end` (no `getAnimations` en un solo instante): UNA
entrada por ocurrencia (memoria motion.md §"when both write `animation`").
13. Captura 2× después, al lado de la de antes. Mirarla.
### 7.5 Guards mecánicos (todos, cada componente)
14. `node --import tsx/esm scripts/theming-census.ts --only {c}` → **100 %** o
excepción escrita. Y el global NO baja.
15. `node --import tsx/esm scripts/component-audit.ts --only {c}` → PASS, sin
R-5 nuevos (en `warn` durante F2, pero se listan y se resuelven).
16. `node --import tsx/esm scripts/eidos-lint.ts {c}` → 0 invalid.
17. `npx vitest run src/uix/eidos` → sin rojos nuevos (comparar **por
fichero de test**, no por cifra: el rojo conocido es `skin-media-player`).
18. `npm run rtl:check` 0 · `npm run docs:check` 0 · `npx prettier --check`
en los ficheros tocados (ojo: `autocrlf=true` hace que prettier se queje
del CRLF del checkout — comprobar el contenido normalizado a LF, no el
fichero; git guarda LF).
19. `npm run check`: atribuir errores **por fichero** (`grep ERROR | grep {c}`),
nunca por cifra global; la rama tiene sesiones concurrentes y el total
se mueve solo. Si el log no llega a `COMPLETED`, es `tmp/lexical/`:
relanzar.
### 7.6 Commit (uno por componente)
20. `git add` con rutas EXPLÍCITAS (sólo lo del componente + `recipes/base.ts`
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y no por la memoria de nadie. `CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia calendar, mandato de Field, el perímetro del knob para la familia tipográfica, el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes → después. El plan gana su §8 con el registro completo y corrige su cabecera, que seguía diciendo que no había nada construido. Y se commitea el instrumental, porque sin él el protocolo §7 es una intención: la sonda de computed antes/después, el diff que es el gate (vacío o se para), el centinela que pregunta si cada token alcanza de verdad, y la captura del stage. Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev server. Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el `animationend` para montar el panel, y si lo congelas mides un popup que no existe); un token resuelto no se mueve desde `:root` por diseño; un panel portalado no ve el ámbito del componente; la demo puede tapar el componente que mides; y un valor centinela igual al real se lee como «no efecto». Igual las cuatro trampas que costaron un commit cada una, con su guard al lado: la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el encabezado que un Edit se come al insertar una sección. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- `generated/base.css` + su README + este plan §8); `git diff --cached --stat`
y `git show :ruta` para verificar el ÁRBOL INDEXADO, no el de trabajo;
`git status` para ver que no entra nada de otra sesión. Mensaje con
`git commit -F fichero` (backticks en `-m` se ejecutan en bash), en
castellano, con: qué knobs entran, la cifra del censo antes → después,
el diff de computed (vacío), el centinela (n tokens probados), los
guards. **Nunca `--amend`** en la rama compartida.
docs(theming): el plan del eje theme-reach — medir qué alcanza un tema, y corregir componente a componente El autor, al ver que el radio del trigger del nav vivía en un privado clavado a --radius-default: «¿los componentes son themables? si no lo son, es un error como framework». Lo es. La doctrina ya exige que cada receta declare sus knobs en recipes/base.ts (recipe-contract §1, theming §6), pero R-1…R-4 sólo comprueban «sin literales», no «alcanzable por un tema»: una receta con todo en var(--radius-md) y privados pasa component:audit en PASS y sólo se puede temar moviendo el sistema entero. Medido, no opinado — scripts/theming-census.ts (instrumento, NO guard): de 5.205 knobs de apariencia en 162 recetas, 1.621 (33 %) pasan por un token público del componente; 1.880 atan directo a un primitivo global, 856 a privados, 614 son literales; 62 componentes no tienen una sola entrada en el contrato; 59 tienen alcance < 20 %; 6 al 100 %. navigation-menu, tras todo el día de hoy, 44 %. El plan (PLAN-theming.md): §1 el censo por familia y por componente (tabla regenerable, no editable) · §2 los cuatro ejes de «personalizable» (tokens · talla · color/variante · estados) · §3 la regla R-5 «alcance de tema» para component-audit, con su válvula de excepción · §4 ocho decisiones D-TH para el autor (R-5 dura · perímetro de knob · WIP · orden · DEFAULT IDÉNTICO · normalizar nombres de slot, que hoy incumplen tabs y el propio nav · cuándo gradúa a error · panel de temas) · §5 fases F0–F4 con gates · §6 deuda transversal · §7 el PROTOCOLO DE VERIFICACIÓN por componente, porque «lo ejecutará Opus, y Opus falla mucho»: cinco preguntas, medir ANTES (computed por parte/estado/talla, sonda headless desde la raíz, fuera de callbacks de MutationObserver), editar sólo la forma firmada, medir DESPUÉS (diff de computed = 0, prueba de CENTINELA por cada token nuevo, desde el píxel hacia arriba, registro de animationstart/end), guards por fichero, un componente = un commit con sus artefactos, y revisión adversarial por bloque. Cada paso tiene un artefacto; sin artefacto no está hecho. El instrumento se probó por mutación antes de creerle, y falló la primera vez: no contaba una regla de una sola línea (`[x] { padding-inline: 8px; }`), así que meter un literal no movía la cifra. Regex endurecido; ahora 6→7→8 con una y dos mutaciones. Las cifras del plan son las del instrumento final, y el handoff (CONTINUE-theming.md) obliga a reproducirlas antes de seguir. Nada firmado, nada construido. Gates del commit: docs:check 0/642 · prettier del script limpio · tsc del script sin errores · el plan cita ficheros que existen (los once, comprobados). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
21. Registro en §8 de este plan (componente · commit · censo antes→después ·
excepciones) y, si el bloque cierra, en `CONTINUE-theming.md`.
### 7.7 Revisión adversarial por bloque
22. Al cerrar un bloque, una pasada escéptica (otra sesión o un pase separado)
que por componente intente REFUTAR con medición propia: «el default es
idéntico» (sonda 7.2 vs 7.4 rehecha, no leída del commit), «cada token
alcanza» (centinela rehecho), «el velo cae donde debe» (píxel arriba),
«no hay doble animación». Hallazgos reales se arreglan en el bloque; los
refutados se anotan. El molde es la revisión del `Sidebar` (21
hallazgos · 17 refutados · 4 reales, `bae03e55c`).
### 7.8 Lo que NO se hace en este eje (y se anota como deuda si aparece)
- Rediseñar un default (radio, color, altura…): deuda firmable, no cambio.
- Tocar sema/morfo salvo un arquetipo en un envoltorio (decisión aparte).
- Cascadear a otro componente «ya que estamos» (memoria `no-cascade-changes`).
- Inventar nombres de token fuera de §1/§6.7, o un hover/estado por componente.
- Editar `generated/base.css` a mano.
- Dar un paso por hecho sin su artefacto.
---
## 8. Registro
feat(calendar-surface): la capa que ya existía, con nombre, casa y su agujero tapado Firma 1 del acta, diseño presentado y firmado. **La medición desmontó el encargo**: el handoff la vendía como una capa NUEVA de ~220 knobs, y la capa ya existía de hecho, sin nombre. `--calendar-*` se emite en `:root` (76 claves) y sus consumidores no acuñan NADA — medido: `range-calendar` 114 referencias prestadas y 0 propias, `month-grid` 77/0, `year-grid` 77/0, y lo único ajeno que leen es sistema puro (`--focus-ring-*`, `--state-hover`). Su 0 % era el artefacto de `listbox` (§13), pero total. **`lib/calendar-surface.css`** (hook `data-calendar-surface`): - cuatro coordenadas por talla — `padding`, `control-size`, `day-size`, `font-size` — xs..lg, porque la familia NO tiene xl, y con la celda DOS pasos por debajo del bundle de control. Esa desviación estaba escondida en cuatro bloques `[data-size]` idénticos, uno por receta, cada uno puenteando a un privado con otro nombre; ahora se lee en un sitio. - la FORMA del anillo de evento y de la marca de festivo. **Capa HÍBRIDA, y es lo que la distingue de sus hermanas**: `list-surface` y `viewport-placement` componen primitivos del sistema, así que declaran sus públicos en el fichero y no tienen entrada de receta. Ésta no puede: su vocabulario son 76 claves SEMÁNTICAS que un tema alcanza una a una por config, así que la entrada `calendar` de `recipes/base.ts` pasa a ser la de la FAMILIA y la capa posee sólo lo que una entrada de receta no sabe expresar. **El defecto que la justificaba, medido**: `--calendar-event-shadow` y `--calendar-day-holiday-shadow` se emitían con ámbito `[data-calendar]` (audit B.2 los host-scopeó por buenas razones) mientras `range-calendar`, `month-grid` y `year-grid` los leían desde hosts que nunca llevan ese atributo: variable VACÍA, `box-shadow` inválido en computed, **el anillo sema de `commit-select` / `commit-set` no pintaba jamás en tres componentes**. Un token prestado cuyo ÁMBITO no te cubre no es un préstamo, es un agujero silencioso, y ningún guard lo veía. Ahora la forma vive en la capa y el acento entra por `--_calendar-surface-accent`, que cada superficie alimenta con su propio forward de paleta THM-2. **Siete wrappers estampan, no cuatro** — y esto casi se me cuela: `DatePicker` y `DateRangePicker` renderizan la superficie soma por sus PROPIOS wrappers (`date-picker-calendar`, `-month-view`, `-year-view`, `date-range-picker-calendar`) y un panel portalado no hereda nada del root del picker. Con sólo los cuatro standalone sellando, ambos quedaban con `--calendar-padding` VACÍA y el panel a padding 0 (medido). La tentación era enganchar la capa a las cuatro identidades de componente: eso viola la regla 1 de capas compartidas, y la respuesta correcta es un sello por wrapper. computed 0 diffs en range-calendar (19.285 valores) · month-grid (3.451) · year-grid (3.451) · date-picker (464) · date-range-picker (406). `calendar` da 12, y son del INSTRUMENTO: dos corridas del MISMO código dan 24 en los mismos nodos y las mismas dos propiedades. Los «missing node» son el propio sello entrando en la clave. **Una incidencia nueva, medida y NO arreglada aquí** (§13): el font-size de los selectores month/year es una moneda al aire — `[data-calendar-month-select][data-button]` (0,2,0) empata con `[data-popover-trigger]:not([data-archetype='field-trigger'])` (0,2,0), la MISMA regla de popover que dejó muerto el cromo de `gradient-picker`, y gana la hoja que cargue después: 16px o 14px según la recarga. Arreglarlo fija el píxel en un lado ⇒ decisión. De paso, `calendar-select.css` deja de puentear un privado que sólo `[data-calendar]` declaraba: en range / date-range corría SIEMPRE por el fallback, clavado a md fuera cual fuera la talla. **El censo deja de penalizar hacer lo correcto**: `LAYER_VOCABULARY` en `theming-census.ts`, mismo precedente que D-TH.2-b con `--style-*`. Global 43 % → **45 %**, `calendar` 75 % → **84 %**. `list-surface` NO se registra: sus consumidores puentean por privados, otra forma, y mueve diez componentes de golpe. Y aparece el techo de debajo, anotado: a los tres consumidores sólo les quedan los forwards de paleta THM-2 —que el censo cuenta como `private` en TODO el catálogo— así que siguen leyendo 0 %. `recipe-css-contract` aprende que una CAPA también declara públicos (antes sólo miraba la receta, y una capa que comparte prefijo con un componente la hacía fallar). Sin debilitarla: un nombre que no declara nadie sigue en rojo. eidos-lint 0 invalid (calendar 29/8 · range-calendar 41/12 · los grids 23/5) · audit --only calendar PASS · vitest eidos 434/435 (el rojo conocido) · rtl 0/181 · docs 0/813 · check 0 errores en tocados · prettier: revertido el reformateo en masa que se coló en cuatro README, el test y el censo (churn ajeno, no mío) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- 2026-08-21 — **La capa `calendar-surface`** (firma 1 del acta, diseño
presentado y firmado el mismo día). La medición desmontó el encargo: la capa
ya existía de hecho — `--calendar-*` en `:root`, y `range-calendar` /
`month-grid` / `year-grid` con 114 / 77 / 77 referencias prestadas y CERO
tokens propios. Se le dio nombre y casa (`lib/calendar-surface.css`, hook
`data-calendar-surface`, cuatro coordenadas xs..lg sin xl, celda dos pasos
bajo el bundle) y se cerró el defecto que la justificaba: las dos sombras del
anillo se emitían con ámbito `[data-calendar]` y los otros tres las leían
vacías, así que el anillo sema **no pintaba** en tres componentes. Capa
HÍBRIDA: el vocabulario se queda en la entrada de receta (76 claves
semánticas alcanzables por config), la capa posee la resolución por talla y
la forma del anillo. **Siete wrappers estampan**, no cuatro — los pickers
renderizan la superficie soma por los suyos y un panel portalado no hereda.
Censo: `LAYER_VOCABULARY` deja de penalizar consumir una capa; global 43 % →
**45 %**, `calendar` 75 % → **84 %**.
uix(theming): revisión adversarial de F2-A — 8 hallazgos de 34, y nace R-5.4 Pase escéptico del bloque (§7.7, molde Sidebar) con medición propia: worktree con SOLO el CSS revertido a la base del bloque (DOM de demo constante — prueba más dura que la del autor), sonda x2 por componente para fijar el suelo de ruido. 34 hipótesis · 26 refutadas · 8 reales. Lo que NO se pudo romper: el «default idéntico» aguanta en los ocho (0 diffs; command 8.091 · table 13.050 · media-player 3.277 · gradient-picker 609 · listbox 2.813 · carousel 2.842; los 3 de tree-grid y los 5 de feed salen idénticos corriendo DOS veces el mismo código → instrumento); 0 tokens huérfanos de 336; el bundle ES 1:1 con los primitivos; el «verificado a mano» de gradient-picker era verdad (15/16 alcanzan al montar); sin doble animación; eidos-lint 0 invalid. Los 4 de código, arreglados con diff de computed VACÍO (re-medido: command 8.091 y carousel 2.842 valores, 0 diffs): 1. command: el re-point de `--command-radius` dentro del Dialog era una declaración muerta con comentario falso — la misma regla pone el radio a 0 y el input lee `--command-input-radius`, nunca este token (medido: `--dialog-content-radius: 9999px` no movía NADA; `--command-radius: 77px` movía la paleta inline y no la del dialog). Retirado; comentarios y README reescritos con lo medido. 2. carousel: la regla vertical del indicador activo llevaba el `2.25` a pelo mientras la horizontal leía `active-indicator-scale` — el token alcanzaba media superficie (scale:6 → horizontal 18→48px, vertical 18→18px). Ahora lo leen las dos. 3. carousel: el trío `item-gap*` no podía ganar NUNCA — soma estampa `gap` INLINE desde la prop (lo necesita para el flex-basis; reglas casadas por CDP: la de la receta y encima INLINE gap:0rem). Retirados los 3 tokens y las 2 declaraciones muertas (la base y el gap:0 de ghost): 0 diffs, que es la prueba. Canal de valor de soma, como --gp-current-gradient. Censo carousel 35 knobs · 23 públicos · 77 % · 27 claves; global 43 % intacto (5.190 · 2.102). 4. table: `striped-row-bg` no alcanza NADA en la demo publicada — `:nth-of-type(even)` cuenta los <tr> de RowDetail y las filas de datos quedan impares (medido tr a tr: diez inmunes). Arreglarlo mueve píxel → PENDIENTE DE FIRMA (candidato `:nth-child(even of [data-table-row])`); adjudicado en el ledger mientras tanto. El instrumento cargaba 22 falsos negativos de 26 «no effect», con tres causas medidas, y la cuarta era de la sonda: - el paso de abrir hacía CLIC en el input, y un clic de ratón sobre un input de texto SÍ casa `:focus-visible` — la regla de foco pintaba el borde y el token de reposo leía muerto (era el «falso negativo sin causa» de §13); - el override se escribía solo en `[data-{c}]`, y las partes que cuelgan de `{c}-root` no lo recibían; - `::before`/`::after` eran invisibles (el aro de buffering, el spinner); - la no-determinación de feed no era una animación sin localizar: era la demo aún CARGANDO — el auto load-more mantiene [data-busy] ~3 s y la firma `commit-settle` anima box-shadow sobre el nodo medido. Con la espera de asentado: 0 diffs y 0 nodos ausentes sobre 6.264 valores, dos corridas. Con eso NACE EL GUARD R-5.4 (la deuda que §13 pedía desde gradient-picker, y que carousel convirtió en patrón — un estilo inline de otra capa es invisible para todo análisis estático): `npm run theming:sentinel -- <c> <url>` (scripts/theming-sentinel.ts, promovido de `__`; blur tras abrir, override en todos los nodos del componente, pseudos, pase de hover, settle) + ledger scripts/theming-sentinel-exceptions.ts con UNA razón medida por token — muerto sin adjudicar = exit 1, excepción STALE = aviso. Los ocho componentes en verde: command 50/57 · table 41/45 · media-player 46/61 · tree-grid 45/47 · gradient-picker 9/25 · listbox 20/21 · carousel 26/27 · feed 43/50, con 46 excepciones adjudicadas. El guard ya me corrigió a mí: cinco entradas mías salieron STALE (los control-height-* de media-player SÍ alcanzan) y las borró; y dos muertos nuevos los produjeron los propios arreglos (el blur apaga focus-input-border; el settle desmonta el spinner de feed) — adjudicados con su porqué. Doctrina en recipe-contract §4. También: tres cifras de especificidad de next-features §12 recontadas (la banda de tree-grid es (0,8,0); la tinta seleccionada del arquetipo es (0,4,0); y el `item` de table vive en la fila Y EN LA CELDA — elementsFromPoint pone el velo en la celda), el comentario de listbox que mandaba pisar el privado `--_listbox-max-height` apunta ahora al público, y quedan registrados en §13 los dos `header-z` con literal '2' y el spinner de media-player a 720ms mientras el gemelo de feed lleva tokens. Guards: censo global 43 % intacto · audit --only command+carousel PASS · eidos-lint 0 invalid (command 16/12, carousel 29/9, listbox 12/7) · vitest eidos 434/435 (el rojo es el conocido skin-media-player) · rtl 0/180 · docs 0/813 · check 0 errores en tocados (72 globales de otras sesiones) · prettier limpio en lo nuevo (los desvíos de command.css/carousel.css ya estaban en HEAD y no se tocan). Pendiente de firma (CONTINUE §«PENDIENTE DE FIRMA»): el striped inerte, el arquetipo en fila+celda, y que soma lea el gap de un token. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- 2026-08-21 — **Revisión adversarial de F2-A (§7.7) + nace R-5.4.** Molde
Sidebar sobre worktree con solo el CSS revertido a la base: 34 hipótesis ·
26 refutadas · 8 reales, todas las de código arregladas con diff de computed
VACÍO (command 8.091 · carousel 2.842 valores). Retiradas: el re-point
muerto de `--command-radius` en el Dialog (su comentario mentía: el input
nunca lo leyó) y el trío `item-gap*` de carousel (el `gap` INLINE de soma
gana siempre — canal de valor, no knob). Tokenizado: el `2.25` vertical del
indicador activo. El instrumento viejo cargaba 22 falsos negativos de 26
«no effect» (clic-foco · override solo en `[data-{c}]` · pseudos) y la
no-determinación de `feed` era la demo cargando — cuatro causas arregladas.
**R-5.4**: `theming-sentinel.ts` + ledger de adjudicaciones, los ocho
fix(table): el striped cuenta filas de DATOS — la banda vuelve a existir Firmado por el autor tras la revisión adversarial: el hallazgo 4 (679dfdb29) midió que `:nth-of-type(even)` cuenta los <tr> hermanos, y con un RowDetail intercalado tras cada fila las filas de datos quedan todas impares — la demo publicada no bandeaba NADA (diez tr inmunes al token, medido uno a uno). El selector pasa a `:nth-child(even of [data-table-row])`, con dos decisiones de peso dentro: - **conserva el (0,6,0) del viejo**: el pseudo más el argumento `of` pesan exactamente lo que pesaba `[data-table-row]:nth-of-type()`, así que el hover —posterior en el fichero— sigue ganando su empate y la banda NO mata el hover. Subirlo de peso habría importado a table el defecto §12.4 de tree-grid. - **`:where(:not([data-selected]))` a peso CERO**: la fila seleccionada siempre estuvo prometida al acento («Overrides hover + striped», dice la regla de al lado a (0,3,0)) pero la banda vieja, de haber casado, se la habría comido por especificidad. El :where excluye sin pesar. Medido (sonda antes/después, 13.195 valores en 7 estados): **12 diffs exactos** — 2 filas pares × 6 estados, backgroundColor 0.9911 → 0.9551 — y nada más se movió. Interacciones verificadas en vivo: selected par conserva el acento (0.9588 con croma) · hover sobre par pinta su tinte (0.931). El ledger de R-5.4 pierde la excepción de `striped-row-bg` (era «genuinely unreachable, pending signature»): el guard adjudica ahora table 42/45 con 3 excepciones, exit 0, sin STALE. eidos-lint 0 invalid · audit --only table PASS · censo table 86 % intacto · vitest eidos 434/435 (el rojo conocido) · rtl 0 · docs 0 · check 0 en tocados · prettier limpio en lo tocado (el wrap de la regla del hover ya estaba desviado en HEAD y no se toca) Incidencia operativa, reparada y anotada: al retirar el worktree de medición de la revisión, `git worktree remove --force` recursó A TRAVÉS de la junction de node_modules y borró parte del real (node_modules/.bin incluido) antes de fallar; `npm install` lo reconstruyó y la suite volvió a 434/435. El árbol git nunca se tocó. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
componentes en verde con 46 excepciones medidas. El `striped` inerte se
firmó y ejecutó el mismo día: `:nth-child(even of [data-table-row])` +
`:where(:not([data-selected]))` al mismo (0,6,0) — 12 diffs exactos, hover
y selected verificados, la excepción del ledger retirada (table 42/45).
Pendiente de firma: el `item` en fila Y celda, y el gap de soma.
uix(theming): revisión adversarial de F2-A — 8 hallazgos de 34, y nace R-5.4 Pase escéptico del bloque (§7.7, molde Sidebar) con medición propia: worktree con SOLO el CSS revertido a la base del bloque (DOM de demo constante — prueba más dura que la del autor), sonda x2 por componente para fijar el suelo de ruido. 34 hipótesis · 26 refutadas · 8 reales. Lo que NO se pudo romper: el «default idéntico» aguanta en los ocho (0 diffs; command 8.091 · table 13.050 · media-player 3.277 · gradient-picker 609 · listbox 2.813 · carousel 2.842; los 3 de tree-grid y los 5 de feed salen idénticos corriendo DOS veces el mismo código → instrumento); 0 tokens huérfanos de 336; el bundle ES 1:1 con los primitivos; el «verificado a mano» de gradient-picker era verdad (15/16 alcanzan al montar); sin doble animación; eidos-lint 0 invalid. Los 4 de código, arreglados con diff de computed VACÍO (re-medido: command 8.091 y carousel 2.842 valores, 0 diffs): 1. command: el re-point de `--command-radius` dentro del Dialog era una declaración muerta con comentario falso — la misma regla pone el radio a 0 y el input lee `--command-input-radius`, nunca este token (medido: `--dialog-content-radius: 9999px` no movía NADA; `--command-radius: 77px` movía la paleta inline y no la del dialog). Retirado; comentarios y README reescritos con lo medido. 2. carousel: la regla vertical del indicador activo llevaba el `2.25` a pelo mientras la horizontal leía `active-indicator-scale` — el token alcanzaba media superficie (scale:6 → horizontal 18→48px, vertical 18→18px). Ahora lo leen las dos. 3. carousel: el trío `item-gap*` no podía ganar NUNCA — soma estampa `gap` INLINE desde la prop (lo necesita para el flex-basis; reglas casadas por CDP: la de la receta y encima INLINE gap:0rem). Retirados los 3 tokens y las 2 declaraciones muertas (la base y el gap:0 de ghost): 0 diffs, que es la prueba. Canal de valor de soma, como --gp-current-gradient. Censo carousel 35 knobs · 23 públicos · 77 % · 27 claves; global 43 % intacto (5.190 · 2.102). 4. table: `striped-row-bg` no alcanza NADA en la demo publicada — `:nth-of-type(even)` cuenta los <tr> de RowDetail y las filas de datos quedan impares (medido tr a tr: diez inmunes). Arreglarlo mueve píxel → PENDIENTE DE FIRMA (candidato `:nth-child(even of [data-table-row])`); adjudicado en el ledger mientras tanto. El instrumento cargaba 22 falsos negativos de 26 «no effect», con tres causas medidas, y la cuarta era de la sonda: - el paso de abrir hacía CLIC en el input, y un clic de ratón sobre un input de texto SÍ casa `:focus-visible` — la regla de foco pintaba el borde y el token de reposo leía muerto (era el «falso negativo sin causa» de §13); - el override se escribía solo en `[data-{c}]`, y las partes que cuelgan de `{c}-root` no lo recibían; - `::before`/`::after` eran invisibles (el aro de buffering, el spinner); - la no-determinación de feed no era una animación sin localizar: era la demo aún CARGANDO — el auto load-more mantiene [data-busy] ~3 s y la firma `commit-settle` anima box-shadow sobre el nodo medido. Con la espera de asentado: 0 diffs y 0 nodos ausentes sobre 6.264 valores, dos corridas. Con eso NACE EL GUARD R-5.4 (la deuda que §13 pedía desde gradient-picker, y que carousel convirtió en patrón — un estilo inline de otra capa es invisible para todo análisis estático): `npm run theming:sentinel -- <c> <url>` (scripts/theming-sentinel.ts, promovido de `__`; blur tras abrir, override en todos los nodos del componente, pseudos, pase de hover, settle) + ledger scripts/theming-sentinel-exceptions.ts con UNA razón medida por token — muerto sin adjudicar = exit 1, excepción STALE = aviso. Los ocho componentes en verde: command 50/57 · table 41/45 · media-player 46/61 · tree-grid 45/47 · gradient-picker 9/25 · listbox 20/21 · carousel 26/27 · feed 43/50, con 46 excepciones adjudicadas. El guard ya me corrigió a mí: cinco entradas mías salieron STALE (los control-height-* de media-player SÍ alcanzan) y las borró; y dos muertos nuevos los produjeron los propios arreglos (el blur apaga focus-input-border; el settle desmonta el spinner de feed) — adjudicados con su porqué. Doctrina en recipe-contract §4. También: tres cifras de especificidad de next-features §12 recontadas (la banda de tree-grid es (0,8,0); la tinta seleccionada del arquetipo es (0,4,0); y el `item` de table vive en la fila Y EN LA CELDA — elementsFromPoint pone el velo en la celda), el comentario de listbox que mandaba pisar el privado `--_listbox-max-height` apunta ahora al público, y quedan registrados en §13 los dos `header-z` con literal '2' y el spinner de media-player a 720ms mientras el gemelo de feed lleva tokens. Guards: censo global 43 % intacto · audit --only command+carousel PASS · eidos-lint 0 invalid (command 16/12, carousel 29/9, listbox 12/7) · vitest eidos 434/435 (el rojo es el conocido skin-media-player) · rtl 0/180 · docs 0/813 · check 0 errores en tocados (72 globales de otras sesiones) · prettier limpio en lo nuevo (los desvíos de command.css/carousel.css ya estaban en HEAD y no se tocan). Pendiente de firma (CONTINUE §«PENDIENTE DE FIRMA»): el striped inerte, el arquetipo en fila+celda, y que soma lea el gap de un token. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
Detalle en [`CONTINUE-theming.md`](./CONTINUE-theming.md) §«Registro».
docs(theming): el handoff, reescrito para que otra sesión arranque sin arqueología La sesión llega llena. El handoff había crecido por acumulación —cada bloque añadía su sección— y para saber qué hacer había que leerlo entero y deducirlo. Ahora la AGENDA va arriba y el registro histórico abajo, separado por su propio encabezado. Arriba: qué comprobar en los primeros cinco minutos (las dos cifras que deben cuadrar, el dev server antes de medir, leer el veredicto §5); tres opciones con recomendación —revisión adversarial de F2-A, el diseño de `calendar-surface`, o seguir la cola por alcance, con la cola ya recalculada—; el protocolo en diez pasos; y **lo que este bloque enseñó**, que es lo que evita repetir el día: el veredicto orienta pero la medición decide (tres de ocho tenían un error de lectura) · un token que no mueve nada miente, y lo que procede es retirar la declaración muerta · un alias puro se borra, no se renombra · lo que la capa posee el consumidor no lo acuña · y la sonda miente de tres maneras distintas, así que ante un diff inesperado se corre DOS VECES sobre el mismo código antes de sospechar del cambio. El plan §8 gana la entrada de la sesión con las cifras y los commits, y su cabecera de estado pasa a 43 %. Estado que hereda la sesión siguiente: alcance global 43 % (era 33 % al abrir el eje), 15 componentes con contrato, 49 sin él, 7 al 100 %, gramática con 0 desviadas y guard en `error`. 29 commits, sin pushear. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- 2026-08-20/21 — **Las 14 firmas, el vocabulario y el bloque F2-A.** El día se
abrió presentando D-TH.6 y acabó con las catorce decisiones pendientes
firmadas en bloque, el vocabulario normalizado con guard, y ocho componentes
tokenizados. Detalle en [`CONTINUE-theming.md`](./CONTINUE-theming.md)
§«Registro de la sesión».
**El vocabulario** (`f09e04fab` acta · `bd916ef3f` codemod · `93017975c`
segunda pasada · `7781d6e4c` guard + doctrina · `476da2dd2` muro de tipos):
307 claves al idioma firmado —tinta `fg`, modificador interactivo delante—
con censo de alcance IDÉNTICO y 0 diffs de computed. R-5.3 entra en `error`
directo; el tipo de `defineRecipes` impide la regresión sin ejecutar nada.
La medición corrigió el inventario heredado: 370 «desviadas» eran 301, porque
47 claves `{rol}-{slot-de-rol}` son canónicas por construcción y un codemod
sobre ellas habría roto `button` entero.
**El bloque F2-A** (pieza 0 `bd032534b` + ocho componentes): global
**37 % → 43 %**, sin contrato **56 → 49**, al 100 % **6 → 7**. `command` 98 %
· `table` 86 % · `media-player` 88 % · `tree-grid` 95 % · `gradient-picker`
88 % · `listbox` 68 % (techo deliberado: su ritmo es de `list-surface`) ·
`carousel` 77 % · `feed` 94 %. Protocolo §7 entero en los ocho; diff de
computed vacío en todos.
**Lo que el bloque destapó y NO se arregló aquí** está en
[`next-features.md`](../next-features.md) §12 y §13: el contrato de cascada
del velo de estado (131 declaraciones-atajo en 45 componentes, la prop
`hoverable` que no suprime nada, un empate de especificidad resuelto por orden
de carga) y los huecos de instrumento y demo. La regla que el eje adoptó: si
una incidencia mueve píxel o toca morfo, se mide, se anota y se sigue.
**Tres veredictos de ocho tenían un error de lectura** que sólo apareció
midiendo, y esa es la lección de método del bloque: el veredicto orienta, la
medición decide.
Además: la demo enseña ahora el contrato de tokens (`90aa6dc5f`,
`TokensPanel` leyendo `getRecipeTokens()` en vivo) — hasta ese día ninguna
demo mostraba la superficie que este eje construye.
docs(theming): el plan del eje theme-reach — medir qué alcanza un tema, y corregir componente a componente El autor, al ver que el radio del trigger del nav vivía en un privado clavado a --radius-default: «¿los componentes son themables? si no lo son, es un error como framework». Lo es. La doctrina ya exige que cada receta declare sus knobs en recipes/base.ts (recipe-contract §1, theming §6), pero R-1…R-4 sólo comprueban «sin literales», no «alcanzable por un tema»: una receta con todo en var(--radius-md) y privados pasa component:audit en PASS y sólo se puede temar moviendo el sistema entero. Medido, no opinado — scripts/theming-census.ts (instrumento, NO guard): de 5.205 knobs de apariencia en 162 recetas, 1.621 (33 %) pasan por un token público del componente; 1.880 atan directo a un primitivo global, 856 a privados, 614 son literales; 62 componentes no tienen una sola entrada en el contrato; 59 tienen alcance < 20 %; 6 al 100 %. navigation-menu, tras todo el día de hoy, 44 %. El plan (PLAN-theming.md): §1 el censo por familia y por componente (tabla regenerable, no editable) · §2 los cuatro ejes de «personalizable» (tokens · talla · color/variante · estados) · §3 la regla R-5 «alcance de tema» para component-audit, con su válvula de excepción · §4 ocho decisiones D-TH para el autor (R-5 dura · perímetro de knob · WIP · orden · DEFAULT IDÉNTICO · normalizar nombres de slot, que hoy incumplen tabs y el propio nav · cuándo gradúa a error · panel de temas) · §5 fases F0–F4 con gates · §6 deuda transversal · §7 el PROTOCOLO DE VERIFICACIÓN por componente, porque «lo ejecutará Opus, y Opus falla mucho»: cinco preguntas, medir ANTES (computed por parte/estado/talla, sonda headless desde la raíz, fuera de callbacks de MutationObserver), editar sólo la forma firmada, medir DESPUÉS (diff de computed = 0, prueba de CENTINELA por cada token nuevo, desde el píxel hacia arriba, registro de animationstart/end), guards por fichero, un componente = un commit con sus artefactos, y revisión adversarial por bloque. Cada paso tiene un artefacto; sin artefacto no está hecho. El instrumento se probó por mutación antes de creerle, y falló la primera vez: no contaba una regla de una sola línea (`[x] { padding-inline: 8px; }`), así que meter un literal no movía la cifra. Regex endurecido; ahora 6→7→8 con una y dos mutaciones. Las cifras del plan son las del instrumento final, y el handoff (CONTINUE-theming.md) obliga a reproducirlas antes de seguir. Nada firmado, nada construido. Gates del commit: docs:check 0/642 · prettier del script limpio · tsc del script sin errores · el plan cita ficheros que existen (los once, comprobados). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- 2026-08-19 — Censo medido (§1) con `scripts/theming-census.ts` (añadido en
este commit como instrumento, sin guard). Plan entregado. **Nada firmado,
nada construido.** Precedente ejecutado hoy que fija la forma:
`navigation-menu` (`ae9e277fa`: tokens del trigger por talla, default
idéntico; `a82794845`: el hover es el del sistema; `822b78cf2`: el `<li>`
sin arquetipo) — y aun así el componente queda en 44 %, prueba de que hace
falta el guard.
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y no por la memoria de nadie. `CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia calendar, mandato de Field, el perímetro del knob para la familia tipográfica, el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes → después. El plan gana su §8 con el registro completo y corrige su cabecera, que seguía diciendo que no había nada construido. Y se commitea el instrumental, porque sin él el protocolo §7 es una intención: la sonda de computed antes/después, el diff que es el gate (vacío o se para), el centinela que pregunta si cada token alcanza de verdad, y la captura del stage. Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev server. Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el `animationend` para montar el panel, y si lo congelas mides un popup que no existe); un token resuelto no se mueve desde `:root` por diseño; un panel portalado no ve el ámbito del componente; la demo puede tapar el componente que mides; y un valor centinela igual al real se lee como «no efecto». Igual las cuatro trampas que costaron un commit cada una, con su guard al lado: la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el encabezado que un Edit se come al insertar una sección. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- 2026-08-20 — **Informe + primeros siete componentes** (`2591ec2f0` el informe
y el instrumento; `3bfe0d413` la revisión de las fichas 11-20). El censo pasa
a `--report`: `docs/audit/theming/` con un README de conjunto y **170 fichas**
(162 recetas + 8 sin receta), cada una con análisis, propuesta derivada de la
doctrina y un bloque de **veredicto** que se conserva al regenerar.
| Componente | Censo antes → después | Commit | Excepciones / lo que queda fuera |
| --------------------------- | --------------------: | ----------- | ------------------------------------------------------------------------------------------------------------------------ |
| `gradient-builder` (piloto) | 0 % → 80 % | `3cdb5b5f0` | foco (sistema) + 3 literales de layout; damero tapado por el `background` inline del wrapper (anotado) |
| `combobox` | 0 % → 76 % | `cef2d32f1` | 3 hovers bespoke (esperan firma) + 6 literales; `content-font-family/-line-height` inertes bajo `[data-depth='overlay']` |
| `natural-time-picker` | 0 % → 61 % | `e774ca7e8` | cielos = excepción de tono fijo ya firmada; panel presta `--popover-*` por composición |
| `date-range-picker` | 0 % → 42 % | `07778b597` | mitad calendario (§B) + mitad campo (§5.3-3) |
| `time-range-picker` | 0 % → 19 % | `68eb8af26` | préstamos de Field/Slider/Toggle |
| `time-picker` | 0 % → 18 % | `e4fcdd62e` | préstamos de Field |
| `proof-of-human` | 0 % → 14 % | `ae91573c3` | escena = `FIXED_TONE_COMPONENTS`; hooks de clase (eje aparte) |
**Global: 33 % → 37 %** (1.621 → 1.841 públicos; 62 → 56 sin contrato).
Protocolo §7 aplicado entero en los siete: diff de computed **vacío** en todos
(6.612 · 1.566 · 6.438 · 493 · 725 · 580 · 406 valores, 7-8 estados cada uno),
centinela por token, `component:audit` PASS, `eidos-lint` 0 invalid,
`rtl:check` 0, `docs:check` 0, suite eidos con el único rojo conocido
(`skin-media-player`).
Cuatro defectos del método que la verificación destapó y que el handoff
recoge: **clave duplicada** en `recipes/base.ts` descarta el bloque en
silencio · dejar vivos los bloques `[data-size]` del CSS hace que el diff dé
0 por la ruta vieja · la sonda debe congelar `transition` pero **no**
`animation` (Presence no monta) · un token RESUELTO no se mueve desde `:root`
por diseño.
Prefijos abreviados o ajenos corregidos de paso (el censo los contaba como
`global` y se cruzarían al anidar): `--gb-*`, `--_ntp-*`, `--_time-field-*` en
los dos time-pickers, `--_date-field-*` y `--_calendar-font-size` en el
date-range-picker.
docs(theming): el plan del eje theme-reach — medir qué alcanza un tema, y corregir componente a componente El autor, al ver que el radio del trigger del nav vivía en un privado clavado a --radius-default: «¿los componentes son themables? si no lo son, es un error como framework». Lo es. La doctrina ya exige que cada receta declare sus knobs en recipes/base.ts (recipe-contract §1, theming §6), pero R-1…R-4 sólo comprueban «sin literales», no «alcanzable por un tema»: una receta con todo en var(--radius-md) y privados pasa component:audit en PASS y sólo se puede temar moviendo el sistema entero. Medido, no opinado — scripts/theming-census.ts (instrumento, NO guard): de 5.205 knobs de apariencia en 162 recetas, 1.621 (33 %) pasan por un token público del componente; 1.880 atan directo a un primitivo global, 856 a privados, 614 son literales; 62 componentes no tienen una sola entrada en el contrato; 59 tienen alcance < 20 %; 6 al 100 %. navigation-menu, tras todo el día de hoy, 44 %. El plan (PLAN-theming.md): §1 el censo por familia y por componente (tabla regenerable, no editable) · §2 los cuatro ejes de «personalizable» (tokens · talla · color/variante · estados) · §3 la regla R-5 «alcance de tema» para component-audit, con su válvula de excepción · §4 ocho decisiones D-TH para el autor (R-5 dura · perímetro de knob · WIP · orden · DEFAULT IDÉNTICO · normalizar nombres de slot, que hoy incumplen tabs y el propio nav · cuándo gradúa a error · panel de temas) · §5 fases F0–F4 con gates · §6 deuda transversal · §7 el PROTOCOLO DE VERIFICACIÓN por componente, porque «lo ejecutará Opus, y Opus falla mucho»: cinco preguntas, medir ANTES (computed por parte/estado/talla, sonda headless desde la raíz, fuera de callbacks de MutationObserver), editar sólo la forma firmada, medir DESPUÉS (diff de computed = 0, prueba de CENTINELA por cada token nuevo, desde el píxel hacia arriba, registro de animationstart/end), guards por fichero, un componente = un commit con sus artefactos, y revisión adversarial por bloque. Cada paso tiene un artefacto; sin artefacto no está hecho. El instrumento se probó por mutación antes de creerle, y falló la primera vez: no contaba una regla de una sola línea (`[x] { padding-inline: 8px; }`), así que meter un literal no movía la cifra. Regex endurecido; ahora 6→7→8 con una y dos mutaciones. Las cifras del plan son las del instrumento final, y el handoff (CONTINUE-theming.md) obliga a reproducirlas antes de seguir. Nada firmado, nada construido. Gates del commit: docs:check 0/642 · prettier del script limpio · tsc del script sin errores · el plan cita ficheros que existen (los once, comprobados). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
---
## 9. Tabla completa por componente (2026-08-19 — regenerar con el script, no editar)
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y no por la memoria de nadie. `CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia calendar, mandato de Field, el perímetro del knob para la familia tipográfica, el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes → después. El plan gana su §8 con el registro completo y corrige su cabecera, que seguía diciendo que no había nada construido. Y se commitea el instrumental, porque sin él el protocolo §7 es una intención: la sonda de computed antes/después, el diff que es el gate (vacío o se para), el centinela que pregunta si cada token alcanza de verdad, y la captura del stage. Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev server. Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el `animationend` para montar el panel, y si lo congelas mides un popup que no existe); un token resuelto no se mueve desde `:root` por diseño; un panel portalado no ve el ámbito del componente; la demo puede tapar el componente que mides; y un valor centinela igual al real se lee como «no efecto». Igual las cuatro trampas que costaron un commit cada una, con su guard al lado: la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el encabezado que un Edit se come al insertar una sección. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
| Componente | Familia | Alcance | Knobs | Público | Privado | Global | Literal | Sistema | Contrato | size |
| --------------------- | ------------------------------- | ------: | ----: | ------: | ------: | -----: | ------: | ------: | -------: | :--: |
| `range-calendar` | Tiempo (chronos · fecha/hora) | 0% | 98 | 0 | 7 | 84 | 4 | 3 | 0 | y |
| `combobox` | Campos | 0% | 96 | 0 | 15 | 67 | 6 | 8 | 1 | y |
| `proof-of-human` | Controles · marcas | 0% | 95 | 0 | 18 | 36 | 37 | 4 | 0 | y |
| `time-range-picker` | Tiempo (chronos · fecha/hora) | 0% | 88 | 0 | 6 | 61 | 17 | 4 | 0 | y |
| `natural-time-picker` | Tiempo (chronos · fecha/hora) | 0% | 78 | 0 | 0 | 69 | 7 | 2 | 0 | y |
| `gradient-builder` | Campos | 0% | 62 | 0 | 8 | 46 | 3 | 5 | 0 | y |
| `month-grid` | Tiempo (chronos · fecha/hora) | 0% | 61 | 0 | 13 | 40 | 5 | 3 | 0 | y |
| `year-grid` | Tiempo (chronos · fecha/hora) | 0% | 61 | 0 | 13 | 40 | 5 | 3 | 0 | y |
| `time-picker` | Tiempo (chronos · fecha/hora) | 0% | 59 | 0 | 3 | 42 | 12 | 2 | 0 | y |
| `date-range-picker` | Tiempo (chronos · fecha/hora) | 0% | 55 | 0 | 0 | 49 | 3 | 3 | 0 | y |
| `command` | Menús · listas · navegación | 0% | 52 | 0 | 9 | 35 | 6 | 2 | 0 | y |
| `table` | Menús · listas · navegación | 0% | 46 | 0 | 23 | 14 | 5 | 4 | 0 | y |
| `media-player` | Media · chat | 0% | 45 | 0 | 0 | 37 | 8 | 0 | 2 | y |
| `tree-grid` | Menús · listas · navegación | 0% | 42 | 0 | 29 | 8 | 2 | 3 | 0 | y |
| `field-langs` | Campos | 0% | 41 | 0 | 0 | 31 | 8 | 2 | 0 | – |
| `gradient-picker` | Campos | 0% | 38 | 0 | 7 | 23 | 5 | 3 | 0 | y |
| `listbox` | Campos | 0% | 38 | 0 | 9 | 23 | 3 | 3 | 0 | – |
| `carousel` | Superficies · overlays · estado | 0% | 36 | 0 | 14 | 12 | 5 | 5 | 0 | y |
| `heading` | Texto · tipografía | 0% | 36 | 0 | 36 | 0 | 0 | 0 | 0 | – |
| `feed` | Menús · listas · navegación | 0% | 33 | 0 | 13 | 16 | 3 | 1 | 0 | y |
| `grid-list` | Menús · listas · navegación | 0% | 32 | 0 | 8 | 17 | 2 | 5 | 0 | y |
| `textarea` | Campos | 0% | 28 | 0 | 16 | 5 | 4 | 3 | 0 | y |
| `tree-view` | Menús · listas · navegación | 0% | 28 | 0 | 8 | 14 | 4 | 2 | 0 | y |
| `code-block` | Texto · tipografía | 0% | 26 | 0 | 2 | 24 | 0 | 0 | 0 | – |
| `card-group` | Superficies · overlays · estado | 0% | 24 | 0 | 0 | 18 | 4 | 2 | 0 | y |
| `text` | Texto · tipografía | 0% | 24 | 0 | 24 | 0 | 0 | 0 | 0 | – |
| `spinner` | Superficies · overlays · estado | 0% | 23 | 0 | 15 | 4 | 4 | 0 | 0 | y |
| `picker-shell` | Tiempo (chronos · fecha/hora) | 0% | 21 | 0 | 4 | 17 | 0 | 0 | 0 | y |
| `link-preview` | Superficies · overlays · estado | 0% | 17 | 0 | 4 | 9 | 1 | 3 | 0 | y |
| `drag-drop` | Controles · marcas | 0% | 16 | 0 | 3 | 6 | 0 | 7 | 1 | – |
| `kbd` | Controles · marcas | 0% | 14 | 0 | 1 | 9 | 4 | 0 | 0 | – |
| `link` | Controles · marcas | 0% | 14 | 0 | 4 | 2 | 5 | 3 | 0 | – |
| `code` | Texto · tipografía | 0% | 13 | 0 | 6 | 3 | 4 | 0 | 0 | – |
| `virtual-list` | Menús · listas · navegación | 0% | 13 | 0 | 2 | 6 | 4 | 1 | 0 | y |
| `virtual-grid` | Menús · listas · navegación | 0% | 12 | 0 | 2 | 6 | 3 | 1 | 0 | y |
| `skeleton` | Superficies · overlays · estado | 0% | 11 | 0 | 9 | 1 | 1 | 0 | 0 | y |
| `skin-media-player` | Media · chat | 0% | 11 | 0 | 0 | 2 | 7 | 2 | 0 | y |
| `scroll-frames` | Superficies · overlays · estado | 0% | 9 | 0 | 1 | 4 | 4 | 0 | 0 | – |
| `announce` | Superficies · overlays · estado | 0% | 8 | 0 | 0 | 8 | 0 | 0 | 0 | – |
| `clipboard` | Controles · marcas | 0% | 8 | 0 | 2 | 5 | 1 | 0 | 0 | – |
| `label` | Campos | 0% | 8 | 0 | 6 | 2 | 0 | 0 | 0 | – |
| `menu-dial` | Menús · listas · navegación | 0% | 8 | 0 | 3 | 1 | 4 | 0 | 0 | y |
| `display` | Texto · tipografía | 0% | 6 | 0 | 6 | 0 | 0 | 0 | 0 | – |
| `mark` | Controles · marcas | 0% | 5 | 0 | 2 | 1 | 2 | 0 | 0 | – |
| `aspect-ratio` | Controles · marcas | 0% | 3 | 0 | 0 | 1 | 2 | 0 | 0 | – |
| `date-picker` | Tiempo (chronos · fecha/hora) | 0% | 2 | 0 | 0 | 0 | 2 | 0 | 0 | – |
| `sticky` | Superficies · overlays · estado | 0% | 2 | 0 | 1 | 0 | 1 | 0 | 1 | – |
| `text-blur` | Texto · tipografía | 0% | 2 | 0 | 0 | 0 | 2 | 0 | 0 | – |
| `cascade` | Menús · listas · navegación | 0% | 1 | 0 | 0 | 0 | 1 | 0 | 0 | – |
| `motion` | Superficies · overlays · estado | 0% | 1 | 0 | 0 | 0 | 1 | 0 | 0 | – |
| `section` | Layout (primitivos) | 0% | 1 | 0 | 0 | 0 | 1 | 0 | 4 | – |
| `chronos` | Tiempo (chronos · fecha/hora) | 1% | 245 | 2 | 22 | 202 | 12 | 7 | 9 | y |
| `prose` | Texto · tipografía | 1% | 74 | 1 | 0 | 43 | 30 | 0 | 2 | y |
| `nav-tree` | Menús · listas · navegación | 4% | 25 | 1 | 0 | 17 | 6 | 1 | 1 | – |
| `s-text` | Texto · tipografía | 7% | 30 | 2 | 28 | 0 | 0 | 0 | 3 | – |
| `chart` | Controles · marcas | 9% | 92 | 8 | 0 | 66 | 14 | 4 | 17 | – |
| `result` | Superficies · overlays · estado | 9% | 22 | 2 | 1 | 15 | 4 | 0 | 8 | – |
| `mockup` | Superficies · overlays · estado | 14% | 29 | 4 | 7 | 13 | 5 | 0 | 0 | – |
| `menubar` | Menús · listas · navegación | 15% | 27 | 4 | 6 | 16 | 0 | 1 | 5 | y |
| `anchor-nav` | Menús · listas · navegación | 20% | 11 | 2 | 0 | 8 | 0 | 1 | 2 | – |
| `banner` | Superficies · overlays · estado | 20% | 10 | 2 | 6 | 1 | 1 | 0 | 42 | y |
| `button` | Controles · marcas | 21% | 30 | 6 | 16 | 0 | 7 | 1 | 105 | y |
| `palabras` | WIP (tracks paralelos) | 22% | 471 | 103 | 1 | 272 | 90 | 5 | 0 | – |
| `toggle` | Controles · marcas | 22% | 28 | 6 | 17 | 1 | 3 | 1 | 119 | y |
| `date-range-field` | Tiempo (chronos · fecha/hora) | 25% | 9 | 2 | 0 | 4 | 2 | 1 | 3 | – |
| `time-range-field` | Tiempo (chronos · fecha/hora) | 25% | 9 | 2 | 0 | 4 | 2 | 1 | 3 | – |
| `surface` | Superficies · overlays · estado | 25% | 4 | 1 | 3 | 0 | 0 | 0 | 25 | – |
| `callout` | Superficies · overlays · estado | 27% | 15 | 4 | 3 | 7 | 1 | 0 | 28 | – |
| `meter` | Superficies · overlays · estado | 29% | 14 | 4 | 6 | 2 | 2 | 0 | 28 | y |
| `knob` | Controles · marcas | 29% | 27 | 7 | 4 | 9 | 4 | 3 | 0 | y |
| `onion-menu` | Menús · listas · navegación | 29% | 27 | 7 | 0 | 8 | 9 | 3 | 12 | – |
| `empty-state` | Superficies · overlays · estado | 29% | 17 | 5 | 7 | 5 | 0 | 0 | 6 | y |
| `search-field` | Campos | 31% | 14 | 4 | 5 | 0 | 4 | 1 | 17 | y |
| `password-field` | Campos | 31% | 36 | 11 | 5 | 9 | 10 | 1 | 23 | y |
| `aura` | Superficies · overlays · estado | 31% | 35 | 11 | 4 | 14 | 6 | 0 | 24 | y |
| `toggle-group` | Controles · marcas | 33% | 3 | 1 | 0 | 1 | 1 | 0 | 1 | – |
| `background` | Superficies · overlays · estado | 35% | 26 | 8 | 6 | 6 | 3 | 3 | 31 | – |
| `avatar` | Controles · marcas | 36% | 36 | 13 | 17 | 0 | 6 | 0 | 0 | y |
| `audio-player` | Media · chat | 37% | 19 | 7 | 0 | 8 | 4 | 0 | 5 | – |
| `tooltip` | Superficies · overlays · estado | 38% | 22 | 8 | 9 | 3 | 1 | 1 | 20 | y |
| `button-group` | Controles · marcas | 40% | 5 | 2 | 0 | 0 | 3 | 0 | 1 | – |
| `badge` | Controles · marcas | 41% | 22 | 9 | 10 | 1 | 2 | 0 | 65 | y |
| `popover` | Superficies · overlays · estado | 42% | 51 | 20 | 6 | 22 | 0 | 3 | 36 | y |
| `qr-code` | Controles · marcas | 43% | 7 | 3 | 0 | 0 | 4 | 0 | 10 | – |
| `navigation-menu` | Menús · listas · navegación | 44% | 38 | 15 | 1 | 13 | 5 | 4 | 26 | y |
| `waveform` | Media · chat | 45% | 11 | 5 | 1 | 1 | 4 | 0 | 10 | y |
| `color-picker` | Campos | 46% | 82 | 36 | 10 | 22 | 11 | 3 | 54 | y |
| `checkbox` | Controles · marcas | 48% | 34 | 16 | 12 | 2 | 3 | 1 | 45 | y |
| `progress` | Superficies · overlays · estado | 49% | 35 | 17 | 9 | 3 | 6 | 0 | 41 | y |
| `tag-group` | Controles · marcas | 49% | 46 | 21 | 21 | 0 | 1 | 3 | 91 | y |
| `color-swatch` | Controles · marcas | 50% | 10 | 5 | 2 | 3 | 0 | 0 | 12 | y |
| `text-scramble` | Texto · tipografía | 50% | 4 | 2 | 0 | 0 | 2 | 0 | 0 | – |
| `split-button` | Controles · marcas | 50% | 2 | 1 | 0 | 1 | 0 | 0 | 1 | y |
| `timeline` | Superficies · overlays · estado | 51% | 49 | 25 | 21 | 0 | 3 | 0 | 45 | y |
| `field` | Campos | 51% | 73 | 35 | 15 | 7 | 11 | 5 | 60 | y |
| `context-menu` | Menús · listas · navegación | 53% | 33 | 16 | 0 | 11 | 3 | 3 | 18 | – |
| `rating-group` | Controles · marcas | 53% | 16 | 8 | 4 | 1 | 2 | 1 | 25 | y |
| `metrics` | Controles · marcas | 54% | 50 | 27 | 10 | 9 | 4 | 0 | 68 | y |
| `sidebar` | Menús · listas · navegación | 55% | 45 | 23 | 0 | 14 | 5 | 3 | 50 | – |
| `drawer` | Superficies · overlays · estado | 55% | 62 | 33 | 9 | 13 | 5 | 2 | 44 | y |
| `color-field` | Campos | 56% | 16 | 9 | 0 | 2 | 5 | 0 | 22 | y |
| `image` | Controles · marcas | 57% | 35 | 20 | 2 | 1 | 12 | 0 | 22 | y |
| `dropdown-menu` | Menús · listas · navegación | 57% | 30 | 16 | 0 | 9 | 3 | 2 | 19 | – |
| `switch` | Controles · marcas | 57% | 22 | 12 | 8 | 1 | 0 | 1 | 52 | y |
| `s-text-virtual-list` | Menús · listas · navegación | 58% | 12 | 7 | 0 | 3 | 2 | 0 | 9 | – |
| `form` | Campos | 59% | 52 | 30 | 14 | 5 | 2 | 1 | 54 | y |
| `float-panel` | Superficies · overlays · estado | 59% | 53 | 29 | 3 | 12 | 5 | 4 | 28 | – |
| `slider` | Controles · marcas | 59% | 33 | 19 | 12 | 0 | 1 | 1 | 39 | y |
| `radio-cards` | Controles · marcas | 60% | 72 | 42 | 0 | 24 | 4 | 2 | 39 | y |
| `card` | Superficies · overlays · estado | 60% | 36 | 21 | 14 | 0 | 0 | 1 | 97 | y |
| `cropper` | Controles · marcas | 60% | 27 | 15 | 0 | 3 | 7 | 2 | 15 | – |
| `emoji-picker` | Campos | 61% | 32 | 17 | 0 | 8 | 3 | 4 | 17 | – |
| `pin-input` | Campos | 61% | 19 | 11 | 0 | 6 | 1 | 1 | 20 | y |
| `editable` | Campos | 63% | 44 | 25 | 12 | 1 | 2 | 4 | 56 | y |
| `text-focus` | Texto · tipografía | 63% | 8 | 5 | 0 | 1 | 2 | 0 | 0 | – |
| `spin-field` | Campos | 64% | 27 | 16 | 0 | 8 | 1 | 2 | 20 | – |
| `file-upload` | Campos | 64% | 61 | 38 | 16 | 1 | 4 | 2 | 98 | y |
| `tags-input` | Campos | 67% | 46 | 28 | 13 | 1 | 0 | 4 | 90 | y |
| `toolbar` | Menús · listas · navegación | 67% | 35 | 22 | 10 | 0 | 1 | 2 | 54 | y |
| `image-adjustments` | Controles · marcas | 67% | 18 | 10 | 0 | 4 | 1 | 3 | 12 | – |
| `text-gradient` | Texto · tipografía | 67% | 9 | 6 | 0 | 0 | 3 | 0 | 0 | – |
| `separator` | SIN FAMILIA | 67% | 6 | 4 | 0 | 0 | 2 | 0 | 3 | – |
| `select` | Campos | 69% | 72 | 45 | 14 | 3 | 3 | 7 | 101 | y |
| `dialog` | Superficies · overlays · estado | 70% | 33 | 23 | 5 | 4 | 1 | 0 | 37 | y |
| `image-picker` | Campos | 71% | 20 | 12 | 0 | 1 | 4 | 3 | 13 | – |
| `pagination` | Menús · listas · navegación | 71% | 26 | 17 | 7 | 0 | 0 | 2 | 34 | y |
| `stepper` | Menús · listas · navegación | 71% | 60 | 40 | 15 | 0 | 1 | 4 | 69 | y |
| `skip-link` | Superficies · overlays · estado | 71% | 8 | 5 | 0 | 0 | 2 | 1 | 8 | – |
| `date-field` | Tiempo (chronos · fecha/hora) | 73% | 11 | 8 | 3 | 0 | 0 | 0 | 21 | y |
| `time-field` | Tiempo (chronos · fecha/hora) | 73% | 11 | 8 | 3 | 0 | 0 | 0 | 21 | y |
| `calendar` | Tiempo (chronos · fecha/hora) | 75% | 71 | 51 | 13 | 0 | 4 | 3 | 76 | y |
| `accordion` | Superficies · overlays · estado | 77% | 65 | 49 | 10 | 0 | 5 | 1 | 76 | y |
| `radio-group` | Controles · marcas | 77% | 75 | 56 | 10 | 7 | 0 | 2 | 61 | y |
| `chat-log` | Media · chat | 77% | 42 | 30 | 0 | 4 | 5 | 3 | 31 | – |
| `chat-composer` | Media · chat | 79% | 44 | 31 | 0 | 3 | 5 | 5 | 31 | – |
| `chat-message` | Media · chat | 82% | 84 | 63 | 2 | 6 | 6 | 7 | 75 | y |
| `fab` | Controles · marcas | 82% | 17 | 14 | 0 | 2 | 1 | 0 | 10 | – |
| `toast` | Superficies · overlays · estado | 83% | 42 | 35 | 6 | 0 | 1 | 0 | 85 | – |
| `text-circular` | Texto · tipografía | 83% | 6 | 5 | 0 | 1 | 0 | 0 | 0 | – |
| `tabs` | Menús · listas · navegación | 88% | 76 | 66 | 6 | 2 | 1 | 1 | 79 | y |
| `chat-typing` | Media · chat | 89% | 9 | 8 | 0 | 1 | 0 | 0 | 7 | – |
| `breadcrumb` | Menús · listas · navegación | 90% | 23 | 18 | 2 | 0 | 0 | 3 | 28 | y |
| `collapsible` | Superficies · overlays · estado | 90% | 12 | 9 | 0 | 1 | 0 | 2 | 11 | – |
| `scroll-area` | Superficies · overlays · estado | 94% | 17 | 15 | 0 | 0 | 1 | 1 | 13 | – |
| `splitter` | Superficies · overlays · estado | 100% | 19 | 18 | 0 | 0 | 0 | 1 | 16 | – |
| `chat-list` | Media · chat | 100% | 18 | 18 | 0 | 0 | 0 | 0 | 18 | – |
| `box` | Layout (primitivos) | 100% | 9 | 9 | 0 | 0 | 0 | 0 | 43 | – |
| `barcode` | Controles · marcas | 100% | 5 | 5 | 0 | 0 | 0 | 0 | 7 | – |
| `flex` | Layout (primitivos) | 100% | 2 | 2 | 0 | 0 | 0 | 0 | 7 | – |
| `grid` | Layout (primitivos) | 100% | 2 | 2 | 0 | 0 | 0 | 0 | 13 | – |
| `alert-dialog` | Superficies · overlays · estado | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| `auto-grid` | Layout (primitivos) | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| `container` | Layout (primitivos) | — | 0 | 0 | 0 | 0 | 0 | 0 | 1 | – |
| `css-field` | Campos | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| `float` | Superficies · overlays · estado | — | 0 | 0 | 0 | 0 | 0 | 0 | 1 | – |
| `group` | Layout (primitivos) | — | 0 | 0 | 0 | 0 | 0 | 0 | 1 | – |
| `highlight` | Controles · marcas | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| `icon` | Controles · marcas | — | 0 | 0 | 0 | 0 | 0 | 0 | 2 | – |
| `mask-field` | Campos | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| `number-field` | Campos | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| `stack` | Layout (primitivos) | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| `wrap` | Layout (primitivos) | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |

Powered by TurnKey Linux.