docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta
El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no
dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear.
Ahora `theming-census.ts --report` escribe la auditoría entera bajo
`docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162
recetas con CSS + 8 componentes sin receta, el árbol completo de
`eidos/components/`.
Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector
(agrupados por clase), sistema transversal aparte, los privados con la columna
que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token
a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta
deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no
decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la
doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado,
un token prestado de otra receta, un shorthand o un eje físico.
Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el
componente que componen (icon-button → button), la capa que consumen (affix →
viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button).
Defectos del instrumento corregidos en el mismo pase, todos encontrados
mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de
color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía
como estado y producía `hover-disabled-*`; un knob que lee un privado se
proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se
reportaba como préstamo del componente `icon` (ahora el préstamo se verifica
contra las claves del dueño en `base.ts`); y el default de talla salía sin
nombrar en vez de `-md`.
Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 ·
1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0
diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de
veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs ·
prettier limpio (el árbol generado entra en .prettierignore junto a
eidos/generated).
Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en
las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco
correcciones — dos de ellas evitaban romper el default), y 5 que no se
tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la
decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado
declarados en su propio CSS.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
# progress — alcance de tema: análisis y propuesta
> Generado por `node --import tsx/esm scripts/theming-census.ts --report`.
> Lo **medido** y la **propuesta** se regeneran; el **Veredicto** (§5) se conserva.
> Vista de conjunto: [README](./README.md) · método y protocolo:
> [`PLAN-theming.md`](../../process/PLAN-theming.md) §1, §2, §7.
docs(theming): 164 fichas regeneradas — dos perdían prosa medida, y el generador es el culpable
Una sesión regeneró todas las fichas de docs/audit/theming con el --report del
censo y las dejó sucias en el árbol. Antes de commitearlas las clasifiqué una a
una en vez de mirar la cifra:
- 155 sólo cambiaban la FECHA de medida. Inocuo, y correcto: el censo corrió
hoy.
- 7 traían cifras nuevas y buenas (collapsible y toolbar, que acaban de cerrar)
o retiraban una propuesta §4 ya cumplida. Mejora.
- 2 —chat-log y chat-composer— BORRABAN prosa medida: donde un humano escribió
«_Ninguno_ — los cuatro que había (el padding-block del divisor, el radio de
foco de la fila de evento…) están cosidos», la plantilla dejaba «_Ninguno._»,
y en chat-composer se llevaba además el bloque de las cuatro declaraciones
retiradas por muertas.
Esas dos van cosidas: el cuerpo de HEAD con la línea de cabecera regenerada.
El resto entra tal cual.
Que la regeneración sea mayoritariamente buena y ocasionalmente destructiva es
el peor reparto posible, porque nadie la mira. Registrado en §13 con su
arreglo: extender los marcadores tipo veredicto:end a cualquier bloque que un
humano haya ampliado, o que el generador respete una línea que no empiece por
su propia plantilla. Mientras tanto, regenerar fichas y commitear sin leer el
diff es una pérdida de datos silenciosa.
Y con él, la hermana que reportó una sesión de la tanda: snapshot-y-restaura
sobre un directorio COMPARTIDO pisa a quien escriba en medio. Dos sesiones
hicieron cp -r de docs/audit/theming y la segunda restauró desde un snapshot
anterior al trabajo de la primera, devolviéndole sus veredictos a «pendiente» a
media faena. El árbol de trabajo también es compartido: respalda por FICHERO
tocado, nunca por directorio.
Quedan fuera a propósito las fichas de chat-message y tags-input: sus sesiones
siguen trabajando.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- **Medido**: 2026-08-24 · **Alcance** : **87%** — 26 de 30 knobs por token público
uix(progress): temable — 69 % → 87 %, y el instrumento medía UNA de sesenta caras
Dos claves acuñadas, seis literales FIRMADOS y cuatro knobs adjudicados por
escrito. El default no se mueve: diff de computed 0 sobre 60 configuraciones y
capturas 2x idénticas byte a byte.
Censo: 69 % → 87 % (35 knobs → 30; público 24 → 26, global 3 → 2, literal 6 → 0,
excepción firmada 0 → 6). Contrato 41 → 43 claves. Global 67 % (no baja).
QUÉ ENTRA (2 claves)
row-gap (--space-2) + column-gap (--space-3). Era el ÚNICO knob de apariencia
que iba a un primitivo a pelo, y se parte en dos porque el atajo llevaba DOS
valores distintos: el hueco de fila separa la línea de texto de la barra, el
de columna separa el rótulo del valor. Un gap único habría nombrado un knob
para dos. El catálogo tiene las dos palabras (row-gap x10, column-gap x4) y el
molde exacto es chart.legend-row-gap / -column-gap.
QUÉ SE FIRMA EN VEZ DE ACUÑARSE (6 literales, fuera del ratio)
Cuatro 100 % y dos max-content: la geometría de la barra, no una decisión de
tema — llena la fila que le dan, o se encoge a su propio grosor / diámetro.
Cada uno con su anotación en la declaración (válvula de recipe-contract §3).
QUÉ SE RECHAZA DE LA PROPUESTA §4 (dos de sus tres filas)
indicator-radius — el --radius-full del anillo. Su GEMELO meter ya dejó el
veredicto escrito EN EL CSS: es lo que hace circular a shape='circular', y un
token dejaría que un tema des-redondease una forma que el consumidor pidió por
su nombre. Identidad, no knob; se copia el comentario del gemelo. Son los dos
únicos global que quedan, y el censo no puede sacarlos del ratio: la válvula de
la anotación sólo reclasifica la clase literal, nunca un global (mismo techo
que meter).
width: max-content — la §4 fundía en UN nombre dos nodos distintos (la raíz
vertical y la raíz circular) y los nombraba por el primer selector. Es el
literal de identidad de arriba.
EL TECHO HONESTO ES 87 %, NO 100 %
Los dos knobs private que quedan son el CANAL DE VALOR:
--_progress-value-pct lo escribe soma en el estilo inline del provider en cada
render (0-100 saturado) y la receta lo lee para el ancho del relleno lineal y
el alto del vertical. Un público encima mentiría — un tema no puede fijar el
progreso de una tarea. Misma clase que el z-index inline de drag-drop.
GUARD R-5.4: 40/43, TRES ADJUDICADOS CON SU MEDIDA
indicator-bg-loaded — sólo pinta bajo [data-state='loaded'], y la corrida
mantiene indeterminate encendido para alcanzar los tokens del barrido; forzado
sobre el nodo real: oklch(0.6406 0.1329 157.68) -> rgb(1,2,3).
transition-duration / -ease — los congela el guard por diseño; pasada sin
congelar: 0.18s -> 11.5s y cubic-bezier(0.4, 0, 0.2, 1) -> steps(7).
EL INSTRUMENTO MEDÍA UNA DE SESENTA CARAS
La sonda estándar ve 4 nodos — que son TODAS las partes del morfo — pero una
sola configuración: lineal, horizontal, loading, md. Medio contrato (el anillo,
el eje vertical, el barrido indeterminado, la tinta de completado) queda fuera
del diff, y el gate pasa en verde sin haberlo mirado. El guard lo dijo antes:
22 de 41 en la página por defecto. Con sweepAttr data-shape — el interruptor
que su gemelo meter YA tenía — sube a 34; con el interruptor indeterminate de
la demo, a 37; con el chip vertical, a 38. Cada control se comprobó por
separado, antes y después, porque montar más puede medir menos: aquí ninguno
tapó nada.
Y una trampa NUEVA, medida aquí: una sonda que CACHEA los nodos y luego mide en
bucle da 8.352 falsos diffs si el HMR reemplaza el subárbol a mitad de corrida.
getComputedStyle sobre un nodo DESACOPLADO devuelve la cadena vacía en TODAS
las propiedades, y el diff lee cada vacío como un cambio. Se arregla
re-consultando los nodos en cada instantánea, y se detecta haciendo fallar la
sonda ante un computed vacío. Pariente de hmr-stale-tab-phantom-findings.
ARTEFACTOS
Sonda estándar: 4 nodos, 800 valores x 7 estados -> 0 diffs.
Sonda de variantes (2 formas x 2 orientaciones x 3 estados x 5 tallas, elemento
y ::before): 17.280 valores -> 0 diffs.
Capturas 2x antes/después, lineal y circular: idénticas byte a byte.
Censo --only progress 87 % · component:audit PASS 1/1 · eidos-lint 0 invalid
(13 morfo-backed, 12 eidos-only) · vitest src/uix/eidos 439/440 (el único rojo
es el conocido skin-media-player en lint.test.ts) · rtl:check 0 · docs:check 0
· npm run check sin errores atribuibles a progress · prettier limpio en lo
tocado (progress.css estaba limpio en HEAD y sigue limpio; los dos ficheros del
centinela ya venían sucios de antes y mis líneas no añaden nada).
README de eidos con su sección «Talla y tema» y pestaña Tokens en la demo,
verificada en el navegador: 43 filas resueltas en vivo, 0 errores de consola.
NOTA DE ÁRBOL COMPARTIDO
Otra sesión trabaja sobre form en los mismos ficheros (base.ts,
generated/base.css, los dos del centinela y el índice de fichas). Lo indexado
se construyó a mano desde HEAD + sólo mis hunks, así que el commit no arrastra
su trabajo en curso; docs/audit/theming/README.md lleva sólo el delta de
progress y conserva la fila de form tal como está en HEAD.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- **Knobs de apariencia**: 30 — público 26 · privado 2 · global 2 · literal 0 · sistema 0 · excepción 6 _(los dos últimos, fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 43 pública(s) — `height-xs` , `height-sm` , `height-md` , `height-lg` , `height-xl` , `vertical-height` , `row-gap` , `column-gap` , `radius-xs` , `radius-sm` , `radius-md` , `radius-lg` , `radius-xl` , `ring-size-xs` , `ring-size-sm` , `ring-size-md` , `ring-size-lg` , `ring-size-xl` , `ring-thickness-xs` , `ring-thickness-sm` , `ring-thickness-md` , `ring-thickness-lg` , `ring-thickness-xl` , `label-font-family` , `label-font-size` , `label-font-weight` , `label-line-height` , `label-fg` , `value-text-font-family` , `value-text-font-size` , `value-text-font-weight` , `value-text-line-height` , `value-text-fg` , `track-bg` , `ring-track-bg` , `ring-center-bg` , `indicator-bg` , `indicator-bg-loaded` , `transition-duration` , `transition-ease` , `indeterminate-width` , `indeterminate-duration` , `indeterminate-ease`
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta
El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no
dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear.
Ahora `theming-census.ts --report` escribe la auditoría entera bajo
`docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162
recetas con CSS + 8 componentes sin receta, el árbol completo de
`eidos/components/`.
Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector
(agrupados por clase), sistema transversal aparte, los privados con la columna
que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token
a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta
deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no
decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la
doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado,
un token prestado de otra receta, un shorthand o un eje físico.
Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el
componente que componen (icon-button → button), la capa que consumen (affix →
viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button).
Defectos del instrumento corregidos en el mismo pase, todos encontrados
mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de
color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía
como estado y producía `hover-disabled-*`; un knob que lee un privado se
proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se
reportaba como préstamo del componente `icon` (ahora el préstamo se verifica
contra las claves del dueño en `base.ts`); y el default de talla salía sin
nombrar en vez de `-md`.
Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 ·
1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0
diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de
veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs ·
prettier limpio (el árbol generado entra en .prettierignore junto a
eidos/generated).
Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en
las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco
correcciones — dos de ellas evitaban romper el default), y 5 que no se
tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la
decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado
declarados en su propio CSS.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- **Eje `size` **: sí · **ficheros** : `progress.css`
## 1. Knobs fuera de alcance
uix(progress): temable — 69 % → 87 %, y el instrumento medía UNA de sesenta caras
Dos claves acuñadas, seis literales FIRMADOS y cuatro knobs adjudicados por
escrito. El default no se mueve: diff de computed 0 sobre 60 configuraciones y
capturas 2x idénticas byte a byte.
Censo: 69 % → 87 % (35 knobs → 30; público 24 → 26, global 3 → 2, literal 6 → 0,
excepción firmada 0 → 6). Contrato 41 → 43 claves. Global 67 % (no baja).
QUÉ ENTRA (2 claves)
row-gap (--space-2) + column-gap (--space-3). Era el ÚNICO knob de apariencia
que iba a un primitivo a pelo, y se parte en dos porque el atajo llevaba DOS
valores distintos: el hueco de fila separa la línea de texto de la barra, el
de columna separa el rótulo del valor. Un gap único habría nombrado un knob
para dos. El catálogo tiene las dos palabras (row-gap x10, column-gap x4) y el
molde exacto es chart.legend-row-gap / -column-gap.
QUÉ SE FIRMA EN VEZ DE ACUÑARSE (6 literales, fuera del ratio)
Cuatro 100 % y dos max-content: la geometría de la barra, no una decisión de
tema — llena la fila que le dan, o se encoge a su propio grosor / diámetro.
Cada uno con su anotación en la declaración (válvula de recipe-contract §3).
QUÉ SE RECHAZA DE LA PROPUESTA §4 (dos de sus tres filas)
indicator-radius — el --radius-full del anillo. Su GEMELO meter ya dejó el
veredicto escrito EN EL CSS: es lo que hace circular a shape='circular', y un
token dejaría que un tema des-redondease una forma que el consumidor pidió por
su nombre. Identidad, no knob; se copia el comentario del gemelo. Son los dos
únicos global que quedan, y el censo no puede sacarlos del ratio: la válvula de
la anotación sólo reclasifica la clase literal, nunca un global (mismo techo
que meter).
width: max-content — la §4 fundía en UN nombre dos nodos distintos (la raíz
vertical y la raíz circular) y los nombraba por el primer selector. Es el
literal de identidad de arriba.
EL TECHO HONESTO ES 87 %, NO 100 %
Los dos knobs private que quedan son el CANAL DE VALOR:
--_progress-value-pct lo escribe soma en el estilo inline del provider en cada
render (0-100 saturado) y la receta lo lee para el ancho del relleno lineal y
el alto del vertical. Un público encima mentiría — un tema no puede fijar el
progreso de una tarea. Misma clase que el z-index inline de drag-drop.
GUARD R-5.4: 40/43, TRES ADJUDICADOS CON SU MEDIDA
indicator-bg-loaded — sólo pinta bajo [data-state='loaded'], y la corrida
mantiene indeterminate encendido para alcanzar los tokens del barrido; forzado
sobre el nodo real: oklch(0.6406 0.1329 157.68) -> rgb(1,2,3).
transition-duration / -ease — los congela el guard por diseño; pasada sin
congelar: 0.18s -> 11.5s y cubic-bezier(0.4, 0, 0.2, 1) -> steps(7).
EL INSTRUMENTO MEDÍA UNA DE SESENTA CARAS
La sonda estándar ve 4 nodos — que son TODAS las partes del morfo — pero una
sola configuración: lineal, horizontal, loading, md. Medio contrato (el anillo,
el eje vertical, el barrido indeterminado, la tinta de completado) queda fuera
del diff, y el gate pasa en verde sin haberlo mirado. El guard lo dijo antes:
22 de 41 en la página por defecto. Con sweepAttr data-shape — el interruptor
que su gemelo meter YA tenía — sube a 34; con el interruptor indeterminate de
la demo, a 37; con el chip vertical, a 38. Cada control se comprobó por
separado, antes y después, porque montar más puede medir menos: aquí ninguno
tapó nada.
Y una trampa NUEVA, medida aquí: una sonda que CACHEA los nodos y luego mide en
bucle da 8.352 falsos diffs si el HMR reemplaza el subárbol a mitad de corrida.
getComputedStyle sobre un nodo DESACOPLADO devuelve la cadena vacía en TODAS
las propiedades, y el diff lee cada vacío como un cambio. Se arregla
re-consultando los nodos en cada instantánea, y se detecta haciendo fallar la
sonda ante un computed vacío. Pariente de hmr-stale-tab-phantom-findings.
ARTEFACTOS
Sonda estándar: 4 nodos, 800 valores x 7 estados -> 0 diffs.
Sonda de variantes (2 formas x 2 orientaciones x 3 estados x 5 tallas, elemento
y ::before): 17.280 valores -> 0 diffs.
Capturas 2x antes/después, lineal y circular: idénticas byte a byte.
Censo --only progress 87 % · component:audit PASS 1/1 · eidos-lint 0 invalid
(13 morfo-backed, 12 eidos-only) · vitest src/uix/eidos 439/440 (el único rojo
es el conocido skin-media-player en lint.test.ts) · rtl:check 0 · docs:check 0
· npm run check sin errores atribuibles a progress · prettier limpio en lo
tocado (progress.css estaba limpio en HEAD y sigue limpio; los dos ficheros del
centinela ya venían sucios de antes y mis líneas no añaden nada).
README de eidos con su sección «Talla y tema» y pestaña Tokens en la demo,
verificada en el navegador: 43 filas resueltas en vivo, 0 errores de consola.
NOTA DE ÁRBOL COMPARTIDO
Otra sesión trabaja sobre form en los mismos ficheros (base.ts,
generated/base.css, los dos del centinela y el índice de fichas). Lo indexado
se construyó a mano desde HEAD + sólo mis hunks, así que el commit no arrastra
su trabajo en curso; docs/audit/theming/README.md lleva sólo el delta de
progress y conserva la fila de form tal como está en HEAD.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
### 1.1 Directo a primitivo global (2)
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta
El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no
dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear.
Ahora `theming-census.ts --report` escribe la auditoría entera bajo
`docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162
recetas con CSS + 8 componentes sin receta, el árbol completo de
`eidos/components/`.
Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector
(agrupados por clase), sistema transversal aparte, los privados con la columna
que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token
a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta
deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no
decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la
doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado,
un token prestado de otra receta, un shorthand o un eje físico.
Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el
componente que componen (icon-button → button), la capa que consumen (affix →
viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button).
Defectos del instrumento corregidos en el mismo pase, todos encontrados
mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de
color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía
como estado y producía `hover-disabled-*`; un knob que lee un privado se
proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se
reportaba como préstamo del componente `icon` (ahora el préstamo se verifica
contra las claves del dueño en `base.ts`); y el default de talla salía sin
nombrar en vez de `-md`.
Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 ·
1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0
diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de
veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs ·
prettier limpio (el árbol generado entra en .prettierignore junto a
eidos/generated).
Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en
las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco
correcciones — dos de ellas evitaban romper el default), y 5 que no se
tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la
decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado
declarados en su propio CSS.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
uix(progress): temable — 69 % → 87 %, y el instrumento medía UNA de sesenta caras
Dos claves acuñadas, seis literales FIRMADOS y cuatro knobs adjudicados por
escrito. El default no se mueve: diff de computed 0 sobre 60 configuraciones y
capturas 2x idénticas byte a byte.
Censo: 69 % → 87 % (35 knobs → 30; público 24 → 26, global 3 → 2, literal 6 → 0,
excepción firmada 0 → 6). Contrato 41 → 43 claves. Global 67 % (no baja).
QUÉ ENTRA (2 claves)
row-gap (--space-2) + column-gap (--space-3). Era el ÚNICO knob de apariencia
que iba a un primitivo a pelo, y se parte en dos porque el atajo llevaba DOS
valores distintos: el hueco de fila separa la línea de texto de la barra, el
de columna separa el rótulo del valor. Un gap único habría nombrado un knob
para dos. El catálogo tiene las dos palabras (row-gap x10, column-gap x4) y el
molde exacto es chart.legend-row-gap / -column-gap.
QUÉ SE FIRMA EN VEZ DE ACUÑARSE (6 literales, fuera del ratio)
Cuatro 100 % y dos max-content: la geometría de la barra, no una decisión de
tema — llena la fila que le dan, o se encoge a su propio grosor / diámetro.
Cada uno con su anotación en la declaración (válvula de recipe-contract §3).
QUÉ SE RECHAZA DE LA PROPUESTA §4 (dos de sus tres filas)
indicator-radius — el --radius-full del anillo. Su GEMELO meter ya dejó el
veredicto escrito EN EL CSS: es lo que hace circular a shape='circular', y un
token dejaría que un tema des-redondease una forma que el consumidor pidió por
su nombre. Identidad, no knob; se copia el comentario del gemelo. Son los dos
únicos global que quedan, y el censo no puede sacarlos del ratio: la válvula de
la anotación sólo reclasifica la clase literal, nunca un global (mismo techo
que meter).
width: max-content — la §4 fundía en UN nombre dos nodos distintos (la raíz
vertical y la raíz circular) y los nombraba por el primer selector. Es el
literal de identidad de arriba.
EL TECHO HONESTO ES 87 %, NO 100 %
Los dos knobs private que quedan son el CANAL DE VALOR:
--_progress-value-pct lo escribe soma en el estilo inline del provider en cada
render (0-100 saturado) y la receta lo lee para el ancho del relleno lineal y
el alto del vertical. Un público encima mentiría — un tema no puede fijar el
progreso de una tarea. Misma clase que el z-index inline de drag-drop.
GUARD R-5.4: 40/43, TRES ADJUDICADOS CON SU MEDIDA
indicator-bg-loaded — sólo pinta bajo [data-state='loaded'], y la corrida
mantiene indeterminate encendido para alcanzar los tokens del barrido; forzado
sobre el nodo real: oklch(0.6406 0.1329 157.68) -> rgb(1,2,3).
transition-duration / -ease — los congela el guard por diseño; pasada sin
congelar: 0.18s -> 11.5s y cubic-bezier(0.4, 0, 0.2, 1) -> steps(7).
EL INSTRUMENTO MEDÍA UNA DE SESENTA CARAS
La sonda estándar ve 4 nodos — que son TODAS las partes del morfo — pero una
sola configuración: lineal, horizontal, loading, md. Medio contrato (el anillo,
el eje vertical, el barrido indeterminado, la tinta de completado) queda fuera
del diff, y el gate pasa en verde sin haberlo mirado. El guard lo dijo antes:
22 de 41 en la página por defecto. Con sweepAttr data-shape — el interruptor
que su gemelo meter YA tenía — sube a 34; con el interruptor indeterminate de
la demo, a 37; con el chip vertical, a 38. Cada control se comprobó por
separado, antes y después, porque montar más puede medir menos: aquí ninguno
tapó nada.
Y una trampa NUEVA, medida aquí: una sonda que CACHEA los nodos y luego mide en
bucle da 8.352 falsos diffs si el HMR reemplaza el subárbol a mitad de corrida.
getComputedStyle sobre un nodo DESACOPLADO devuelve la cadena vacía en TODAS
las propiedades, y el diff lee cada vacío como un cambio. Se arregla
re-consultando los nodos en cada instantánea, y se detecta haciendo fallar la
sonda ante un computed vacío. Pariente de hmr-stale-tab-phantom-findings.
ARTEFACTOS
Sonda estándar: 4 nodos, 800 valores x 7 estados -> 0 diffs.
Sonda de variantes (2 formas x 2 orientaciones x 3 estados x 5 tallas, elemento
y ::before): 17.280 valores -> 0 diffs.
Capturas 2x antes/después, lineal y circular: idénticas byte a byte.
Censo --only progress 87 % · component:audit PASS 1/1 · eidos-lint 0 invalid
(13 morfo-backed, 12 eidos-only) · vitest src/uix/eidos 439/440 (el único rojo
es el conocido skin-media-player en lint.test.ts) · rtl:check 0 · docs:check 0
· npm run check sin errores atribuibles a progress · prettier limpio en lo
tocado (progress.css estaba limpio en HEAD y sigue limpio; los dos ficheros del
centinela ya venían sucios de antes y mis líneas no añaden nada).
README de eidos con su sección «Talla y tema» y pestaña Tokens en la demo,
verificada en el navegador: 43 filas resueltas en vivo, 0 errores de consola.
NOTA DE ÁRBOL COMPARTIDO
Otra sesión trabaja sobre form en los mismos ficheros (base.ts,
generated/base.css, los dos del centinela y el índice de fichas). Lo indexado
se construyó a mano desde HEAD + sólo mis hunks, así que el commit no arrastra
su trabajo en curso; docs/audit/theming/README.md lleva sólo el delta de
progress y conserva la fila de form tal como está en HEAD.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
| 1 | `progress.css:127` | `[data-progress][data-shape='circular'] [data-progress-indicator]` | `border-radius` | `var(--radius-full)` |
| 2 | `progress.css:139` | `[data-progress][data-shape='circular'] [data-progress-indicator]::before` | `border-radius` | `var(--radius-full)` |
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta
El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no
dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear.
Ahora `theming-census.ts --report` escribe la auditoría entera bajo
`docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162
recetas con CSS + 8 componentes sin receta, el árbol completo de
`eidos/components/`.
Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector
(agrupados por clase), sistema transversal aparte, los privados con la columna
que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token
a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta
deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no
decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la
doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado,
un token prestado de otra receta, un shorthand o un eje físico.
Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el
componente que componen (icon-button → button), la capa que consumen (affix →
viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button).
Defectos del instrumento corregidos en el mismo pase, todos encontrados
mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de
color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía
como estado y producía `hover-disabled-*`; un knob que lee un privado se
proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se
reportaba como préstamo del componente `icon` (ahora el préstamo se verifica
contra las claves del dueño en `base.ts`); y el default de talla salía sin
nombrar en vez de `-md`.
Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 ·
1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0
diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de
veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs ·
prettier limpio (el árbol generado entra en .prettierignore junto a
eidos/generated).
Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en
las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco
correcciones — dos de ellas evitaban romper el default), y 5 que no se
tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la
decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado
declarados en su propio CSS.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
uix(theming): el censo aprende que un privado DERIVADO sí alcanza — 50 % → 56 %
Seis puntos de alcance sin tocar una línea de componente: **la medición estaba
mal, no el código**.
EL DEFECTO. F0 encargaba «detección "privado que deriva de público" (leer
`--_c-x: var(--c-y)` en la misma receta y reclasificar)». Se implementó a
medias: el censo CALCULA `derives` y lo imprime en la columna «¿deriva de un
público?» de la §3 de cada ficha, pero **el contador nunca lo usó** — esos
knobs seguían puntuando como deuda. Medido: **240 privados en 59 componentes**
estaban en ese estado, es decir, haciendo exactamente lo que la doctrina
prescribe («privados sólo si derivan de públicos») y penalizados por ello.
Lo destapó tokenizar `avatar`: sus 17 privados YA leían públicos
(`--_avatar-size: var(--avatar-size-md)`), así que no había nada que tokenizar
— había que arreglar el instrumento.
LA CORRECCIÓN. Segunda pasada tras recoger privados y knobs: un knob que lee
`var(--_c-x)` pasa a `public` si ese privado alcanza un público. El cierre es
**TRANSITIVO** (`--_a: calc(var(--_b) * .3)` alcanza si `--_b` alcanza) con
conjunto de visitados para que un ciclo no lo cuelgue.
**Muta-prueba** (obligatoria, memoria `a-guard-that-inspects-nothing-passes`):
apuntar `--_avatar-radius` al primitivo crudo baja avatar de 75 % a 72 % y el
knob vuelve a `private`; revertirlo lo devuelve a 75 % y deja el árbol
idéntico.
EFECTO MEDIDO:
- Global **50 % → 56 %** · privados 625 → 341 · al 100 % 10 → 14 · <20 % 22 → 20.
- `avatar` 36 % → **75 %**, `picker-shell` 76 % → **95 %**, `menu-dial` 25 % →
38 %, y otras 51 fichas con cifras nuevas — **ninguna por cambio de código**.
- Los READMEs de `picker-shell` y `menu-dial` se actualizan con la cifra nueva
y con la razón, para que nadie crea que el componente cambió.
⚠ **Cualquier cifra anterior a este commit no es comparable con las de ahora.**
Queda escrito en el handoff.
Y un hueco de F0 que sale al hacerlo, registrado en §13: **el test del SUELO
que el plan pedía NO EXISTE** («alcance global ≥ el de hoy y literales ≤ 614 —
el número sube o el test falla»). Hoy nada impide que el alcance BAJE entre
sesiones: sólo se vería mirando la cifra a mano. Con el censo ya endurecido, es
el momento de escribirlo.
Gates: `component:audit` 162 PASS / 4 NEEDS-WORK (preexistentes) · `--names`
DESVIADAS 0 · suite eidos sin rojos nuevos · rtl:check 0 · docs:check 0 · el
censo compila.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
### 1.2 A través de un privado (2)
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta
El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no
dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear.
Ahora `theming-census.ts --report` escribe la auditoría entera bajo
`docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162
recetas con CSS + 8 componentes sin receta, el árbol completo de
`eidos/components/`.
Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector
(agrupados por clase), sistema transversal aparte, los privados con la columna
que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token
a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta
deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no
decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la
doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado,
un token prestado de otra receta, un shorthand o un eje físico.
Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el
componente que componen (icon-button → button), la capa que consumen (affix →
viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button).
Defectos del instrumento corregidos en el mismo pase, todos encontrados
mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de
color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía
como estado y producía `hover-disabled-*`; un knob que lee un privado se
proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se
reportaba como préstamo del componente `icon` (ahora el préstamo se verifica
contra las claves del dueño en `base.ts`); y el default de talla salía sin
nombrar en vez de `-md`.
Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 ·
1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0
diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de
veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs ·
prettier limpio (el árbol generado entra en .prettierignore junto a
eidos/generated).
Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en
las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco
correcciones — dos de ellas evitaban romper el default), y 5 que no se
tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la
decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado
declarados en su propio CSS.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
uix(progress): temable — 69 % → 87 %, y el instrumento medía UNA de sesenta caras
Dos claves acuñadas, seis literales FIRMADOS y cuatro knobs adjudicados por
escrito. El default no se mueve: diff de computed 0 sobre 60 configuraciones y
capturas 2x idénticas byte a byte.
Censo: 69 % → 87 % (35 knobs → 30; público 24 → 26, global 3 → 2, literal 6 → 0,
excepción firmada 0 → 6). Contrato 41 → 43 claves. Global 67 % (no baja).
QUÉ ENTRA (2 claves)
row-gap (--space-2) + column-gap (--space-3). Era el ÚNICO knob de apariencia
que iba a un primitivo a pelo, y se parte en dos porque el atajo llevaba DOS
valores distintos: el hueco de fila separa la línea de texto de la barra, el
de columna separa el rótulo del valor. Un gap único habría nombrado un knob
para dos. El catálogo tiene las dos palabras (row-gap x10, column-gap x4) y el
molde exacto es chart.legend-row-gap / -column-gap.
QUÉ SE FIRMA EN VEZ DE ACUÑARSE (6 literales, fuera del ratio)
Cuatro 100 % y dos max-content: la geometría de la barra, no una decisión de
tema — llena la fila que le dan, o se encoge a su propio grosor / diámetro.
Cada uno con su anotación en la declaración (válvula de recipe-contract §3).
QUÉ SE RECHAZA DE LA PROPUESTA §4 (dos de sus tres filas)
indicator-radius — el --radius-full del anillo. Su GEMELO meter ya dejó el
veredicto escrito EN EL CSS: es lo que hace circular a shape='circular', y un
token dejaría que un tema des-redondease una forma que el consumidor pidió por
su nombre. Identidad, no knob; se copia el comentario del gemelo. Son los dos
únicos global que quedan, y el censo no puede sacarlos del ratio: la válvula de
la anotación sólo reclasifica la clase literal, nunca un global (mismo techo
que meter).
width: max-content — la §4 fundía en UN nombre dos nodos distintos (la raíz
vertical y la raíz circular) y los nombraba por el primer selector. Es el
literal de identidad de arriba.
EL TECHO HONESTO ES 87 %, NO 100 %
Los dos knobs private que quedan son el CANAL DE VALOR:
--_progress-value-pct lo escribe soma en el estilo inline del provider en cada
render (0-100 saturado) y la receta lo lee para el ancho del relleno lineal y
el alto del vertical. Un público encima mentiría — un tema no puede fijar el
progreso de una tarea. Misma clase que el z-index inline de drag-drop.
GUARD R-5.4: 40/43, TRES ADJUDICADOS CON SU MEDIDA
indicator-bg-loaded — sólo pinta bajo [data-state='loaded'], y la corrida
mantiene indeterminate encendido para alcanzar los tokens del barrido; forzado
sobre el nodo real: oklch(0.6406 0.1329 157.68) -> rgb(1,2,3).
transition-duration / -ease — los congela el guard por diseño; pasada sin
congelar: 0.18s -> 11.5s y cubic-bezier(0.4, 0, 0.2, 1) -> steps(7).
EL INSTRUMENTO MEDÍA UNA DE SESENTA CARAS
La sonda estándar ve 4 nodos — que son TODAS las partes del morfo — pero una
sola configuración: lineal, horizontal, loading, md. Medio contrato (el anillo,
el eje vertical, el barrido indeterminado, la tinta de completado) queda fuera
del diff, y el gate pasa en verde sin haberlo mirado. El guard lo dijo antes:
22 de 41 en la página por defecto. Con sweepAttr data-shape — el interruptor
que su gemelo meter YA tenía — sube a 34; con el interruptor indeterminate de
la demo, a 37; con el chip vertical, a 38. Cada control se comprobó por
separado, antes y después, porque montar más puede medir menos: aquí ninguno
tapó nada.
Y una trampa NUEVA, medida aquí: una sonda que CACHEA los nodos y luego mide en
bucle da 8.352 falsos diffs si el HMR reemplaza el subárbol a mitad de corrida.
getComputedStyle sobre un nodo DESACOPLADO devuelve la cadena vacía en TODAS
las propiedades, y el diff lee cada vacío como un cambio. Se arregla
re-consultando los nodos en cada instantánea, y se detecta haciendo fallar la
sonda ante un computed vacío. Pariente de hmr-stale-tab-phantom-findings.
ARTEFACTOS
Sonda estándar: 4 nodos, 800 valores x 7 estados -> 0 diffs.
Sonda de variantes (2 formas x 2 orientaciones x 3 estados x 5 tallas, elemento
y ::before): 17.280 valores -> 0 diffs.
Capturas 2x antes/después, lineal y circular: idénticas byte a byte.
Censo --only progress 87 % · component:audit PASS 1/1 · eidos-lint 0 invalid
(13 morfo-backed, 12 eidos-only) · vitest src/uix/eidos 439/440 (el único rojo
es el conocido skin-media-player en lint.test.ts) · rtl:check 0 · docs:check 0
· npm run check sin errores atribuibles a progress · prettier limpio en lo
tocado (progress.css estaba limpio en HEAD y sigue limpio; los dos ficheros del
centinela ya venían sucios de antes y mis líneas no añaden nada).
README de eidos con su sección «Talla y tema» y pestaña Tokens en la demo,
verificada en el navegador: 43 filas resueltas en vivo, 0 errores de consola.
NOTA DE ÁRBOL COMPARTIDO
Otra sesión trabaja sobre form en los mismos ficheros (base.ts,
generated/base.css, los dos del centinela y el índice de fichas). Lo indexado
se construyó a mano desde HEAD + sólo mis hunks, así que el commit no arrastra
su trabajo en curso; docs/audit/theming/README.md lleva sólo el delta de
progress y conserva la fila de form tal como está en HEAD.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
| 1 | `progress.css:67` | `[data-progress-indicator]::before` | `inline-size` | `calc(var(--_progress-value-pct, 0) * 1%)` |
| 2 | `progress.css:83` | `[data-progress][data-orientation='vertical'] [data-progress-indicator]::before` | `block-size` | `calc(var(--_progress-value-pct, 0) * 1%)` |
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta
El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no
dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear.
Ahora `theming-census.ts --report` escribe la auditoría entera bajo
`docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162
recetas con CSS + 8 componentes sin receta, el árbol completo de
`eidos/components/`.
Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector
(agrupados por clase), sistema transversal aparte, los privados con la columna
que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token
a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta
deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no
decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la
doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado,
un token prestado de otra receta, un shorthand o un eje físico.
Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el
componente que componen (icon-button → button), la capa que consumen (affix →
viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button).
Defectos del instrumento corregidos en el mismo pase, todos encontrados
mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de
color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía
como estado y producía `hover-disabled-*`; un knob que lee un privado se
proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se
reportaba como préstamo del componente `icon` (ahora el préstamo se verifica
contra las claves del dueño en `base.ts`); y el default de talla salía sin
nombrar en vez de `-md`.
Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 ·
1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0
diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de
veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs ·
prettier limpio (el árbol generado entra en .prettierignore junto a
eidos/generated).
Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en
las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco
correcciones — dos de ellas evitaban romper el default), y 5 que no se
tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la
decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado
declarados en su propio CSS.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
uix(progress): temable — 69 % → 87 %, y el instrumento medía UNA de sesenta caras
Dos claves acuñadas, seis literales FIRMADOS y cuatro knobs adjudicados por
escrito. El default no se mueve: diff de computed 0 sobre 60 configuraciones y
capturas 2x idénticas byte a byte.
Censo: 69 % → 87 % (35 knobs → 30; público 24 → 26, global 3 → 2, literal 6 → 0,
excepción firmada 0 → 6). Contrato 41 → 43 claves. Global 67 % (no baja).
QUÉ ENTRA (2 claves)
row-gap (--space-2) + column-gap (--space-3). Era el ÚNICO knob de apariencia
que iba a un primitivo a pelo, y se parte en dos porque el atajo llevaba DOS
valores distintos: el hueco de fila separa la línea de texto de la barra, el
de columna separa el rótulo del valor. Un gap único habría nombrado un knob
para dos. El catálogo tiene las dos palabras (row-gap x10, column-gap x4) y el
molde exacto es chart.legend-row-gap / -column-gap.
QUÉ SE FIRMA EN VEZ DE ACUÑARSE (6 literales, fuera del ratio)
Cuatro 100 % y dos max-content: la geometría de la barra, no una decisión de
tema — llena la fila que le dan, o se encoge a su propio grosor / diámetro.
Cada uno con su anotación en la declaración (válvula de recipe-contract §3).
QUÉ SE RECHAZA DE LA PROPUESTA §4 (dos de sus tres filas)
indicator-radius — el --radius-full del anillo. Su GEMELO meter ya dejó el
veredicto escrito EN EL CSS: es lo que hace circular a shape='circular', y un
token dejaría que un tema des-redondease una forma que el consumidor pidió por
su nombre. Identidad, no knob; se copia el comentario del gemelo. Son los dos
únicos global que quedan, y el censo no puede sacarlos del ratio: la válvula de
la anotación sólo reclasifica la clase literal, nunca un global (mismo techo
que meter).
width: max-content — la §4 fundía en UN nombre dos nodos distintos (la raíz
vertical y la raíz circular) y los nombraba por el primer selector. Es el
literal de identidad de arriba.
EL TECHO HONESTO ES 87 %, NO 100 %
Los dos knobs private que quedan son el CANAL DE VALOR:
--_progress-value-pct lo escribe soma en el estilo inline del provider en cada
render (0-100 saturado) y la receta lo lee para el ancho del relleno lineal y
el alto del vertical. Un público encima mentiría — un tema no puede fijar el
progreso de una tarea. Misma clase que el z-index inline de drag-drop.
GUARD R-5.4: 40/43, TRES ADJUDICADOS CON SU MEDIDA
indicator-bg-loaded — sólo pinta bajo [data-state='loaded'], y la corrida
mantiene indeterminate encendido para alcanzar los tokens del barrido; forzado
sobre el nodo real: oklch(0.6406 0.1329 157.68) -> rgb(1,2,3).
transition-duration / -ease — los congela el guard por diseño; pasada sin
congelar: 0.18s -> 11.5s y cubic-bezier(0.4, 0, 0.2, 1) -> steps(7).
EL INSTRUMENTO MEDÍA UNA DE SESENTA CARAS
La sonda estándar ve 4 nodos — que son TODAS las partes del morfo — pero una
sola configuración: lineal, horizontal, loading, md. Medio contrato (el anillo,
el eje vertical, el barrido indeterminado, la tinta de completado) queda fuera
del diff, y el gate pasa en verde sin haberlo mirado. El guard lo dijo antes:
22 de 41 en la página por defecto. Con sweepAttr data-shape — el interruptor
que su gemelo meter YA tenía — sube a 34; con el interruptor indeterminate de
la demo, a 37; con el chip vertical, a 38. Cada control se comprobó por
separado, antes y después, porque montar más puede medir menos: aquí ninguno
tapó nada.
Y una trampa NUEVA, medida aquí: una sonda que CACHEA los nodos y luego mide en
bucle da 8.352 falsos diffs si el HMR reemplaza el subárbol a mitad de corrida.
getComputedStyle sobre un nodo DESACOPLADO devuelve la cadena vacía en TODAS
las propiedades, y el diff lee cada vacío como un cambio. Se arregla
re-consultando los nodos en cada instantánea, y se detecta haciendo fallar la
sonda ante un computed vacío. Pariente de hmr-stale-tab-phantom-findings.
ARTEFACTOS
Sonda estándar: 4 nodos, 800 valores x 7 estados -> 0 diffs.
Sonda de variantes (2 formas x 2 orientaciones x 3 estados x 5 tallas, elemento
y ::before): 17.280 valores -> 0 diffs.
Capturas 2x antes/después, lineal y circular: idénticas byte a byte.
Censo --only progress 87 % · component:audit PASS 1/1 · eidos-lint 0 invalid
(13 morfo-backed, 12 eidos-only) · vitest src/uix/eidos 439/440 (el único rojo
es el conocido skin-media-player en lint.test.ts) · rtl:check 0 · docs:check 0
· npm run check sin errores atribuibles a progress · prettier limpio en lo
tocado (progress.css estaba limpio en HEAD y sigue limpio; los dos ficheros del
centinela ya venían sucios de antes y mis líneas no añaden nada).
README de eidos con su sección «Talla y tema» y pestaña Tokens en la demo,
verificada en el navegador: 43 filas resueltas en vivo, 0 errores de consola.
NOTA DE ÁRBOL COMPARTIDO
Otra sesión trabaja sobre form en los mismos ficheros (base.ts,
generated/base.css, los dos del centinela y el índice de fichas). Lo indexado
se construyó a mano desde HEAD + sólo mis hunks, así que el commit no arrastra
su trabajo en curso; docs/audit/theming/README.md lleva sólo el delta de
progress y conserva la fila de form tal como está en HEAD.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
### 1.3 Literales (0)
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta
El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no
dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear.
Ahora `theming-census.ts --report` escribe la auditoría entera bajo
`docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162
recetas con CSS + 8 componentes sin receta, el árbol completo de
`eidos/components/`.
Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector
(agrupados por clase), sistema transversal aparte, los privados con la columna
que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token
a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta
deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no
decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la
doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado,
un token prestado de otra receta, un shorthand o un eje físico.
Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el
componente que componen (icon-button → button), la capa que consumen (affix →
viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button).
Defectos del instrumento corregidos en el mismo pase, todos encontrados
mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de
color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía
como estado y producía `hover-disabled-*`; un knob que lee un privado se
proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se
reportaba como préstamo del componente `icon` (ahora el préstamo se verifica
contra las claves del dueño en `base.ts`); y el default de talla salía sin
nombrar en vez de `-md`.
Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 ·
1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0
diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de
veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs ·
prettier limpio (el árbol generado entra en .prettierignore junto a
eidos/generated).
Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en
las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco
correcciones — dos de ellas evitaban romper el default), y 5 que no se
tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la
decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado
declarados en su propio CSS.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
uix(progress): temable — 69 % → 87 %, y el instrumento medía UNA de sesenta caras
Dos claves acuñadas, seis literales FIRMADOS y cuatro knobs adjudicados por
escrito. El default no se mueve: diff de computed 0 sobre 60 configuraciones y
capturas 2x idénticas byte a byte.
Censo: 69 % → 87 % (35 knobs → 30; público 24 → 26, global 3 → 2, literal 6 → 0,
excepción firmada 0 → 6). Contrato 41 → 43 claves. Global 67 % (no baja).
QUÉ ENTRA (2 claves)
row-gap (--space-2) + column-gap (--space-3). Era el ÚNICO knob de apariencia
que iba a un primitivo a pelo, y se parte en dos porque el atajo llevaba DOS
valores distintos: el hueco de fila separa la línea de texto de la barra, el
de columna separa el rótulo del valor. Un gap único habría nombrado un knob
para dos. El catálogo tiene las dos palabras (row-gap x10, column-gap x4) y el
molde exacto es chart.legend-row-gap / -column-gap.
QUÉ SE FIRMA EN VEZ DE ACUÑARSE (6 literales, fuera del ratio)
Cuatro 100 % y dos max-content: la geometría de la barra, no una decisión de
tema — llena la fila que le dan, o se encoge a su propio grosor / diámetro.
Cada uno con su anotación en la declaración (válvula de recipe-contract §3).
QUÉ SE RECHAZA DE LA PROPUESTA §4 (dos de sus tres filas)
indicator-radius — el --radius-full del anillo. Su GEMELO meter ya dejó el
veredicto escrito EN EL CSS: es lo que hace circular a shape='circular', y un
token dejaría que un tema des-redondease una forma que el consumidor pidió por
su nombre. Identidad, no knob; se copia el comentario del gemelo. Son los dos
únicos global que quedan, y el censo no puede sacarlos del ratio: la válvula de
la anotación sólo reclasifica la clase literal, nunca un global (mismo techo
que meter).
width: max-content — la §4 fundía en UN nombre dos nodos distintos (la raíz
vertical y la raíz circular) y los nombraba por el primer selector. Es el
literal de identidad de arriba.
EL TECHO HONESTO ES 87 %, NO 100 %
Los dos knobs private que quedan son el CANAL DE VALOR:
--_progress-value-pct lo escribe soma en el estilo inline del provider en cada
render (0-100 saturado) y la receta lo lee para el ancho del relleno lineal y
el alto del vertical. Un público encima mentiría — un tema no puede fijar el
progreso de una tarea. Misma clase que el z-index inline de drag-drop.
GUARD R-5.4: 40/43, TRES ADJUDICADOS CON SU MEDIDA
indicator-bg-loaded — sólo pinta bajo [data-state='loaded'], y la corrida
mantiene indeterminate encendido para alcanzar los tokens del barrido; forzado
sobre el nodo real: oklch(0.6406 0.1329 157.68) -> rgb(1,2,3).
transition-duration / -ease — los congela el guard por diseño; pasada sin
congelar: 0.18s -> 11.5s y cubic-bezier(0.4, 0, 0.2, 1) -> steps(7).
EL INSTRUMENTO MEDÍA UNA DE SESENTA CARAS
La sonda estándar ve 4 nodos — que son TODAS las partes del morfo — pero una
sola configuración: lineal, horizontal, loading, md. Medio contrato (el anillo,
el eje vertical, el barrido indeterminado, la tinta de completado) queda fuera
del diff, y el gate pasa en verde sin haberlo mirado. El guard lo dijo antes:
22 de 41 en la página por defecto. Con sweepAttr data-shape — el interruptor
que su gemelo meter YA tenía — sube a 34; con el interruptor indeterminate de
la demo, a 37; con el chip vertical, a 38. Cada control se comprobó por
separado, antes y después, porque montar más puede medir menos: aquí ninguno
tapó nada.
Y una trampa NUEVA, medida aquí: una sonda que CACHEA los nodos y luego mide en
bucle da 8.352 falsos diffs si el HMR reemplaza el subárbol a mitad de corrida.
getComputedStyle sobre un nodo DESACOPLADO devuelve la cadena vacía en TODAS
las propiedades, y el diff lee cada vacío como un cambio. Se arregla
re-consultando los nodos en cada instantánea, y se detecta haciendo fallar la
sonda ante un computed vacío. Pariente de hmr-stale-tab-phantom-findings.
ARTEFACTOS
Sonda estándar: 4 nodos, 800 valores x 7 estados -> 0 diffs.
Sonda de variantes (2 formas x 2 orientaciones x 3 estados x 5 tallas, elemento
y ::before): 17.280 valores -> 0 diffs.
Capturas 2x antes/después, lineal y circular: idénticas byte a byte.
Censo --only progress 87 % · component:audit PASS 1/1 · eidos-lint 0 invalid
(13 morfo-backed, 12 eidos-only) · vitest src/uix/eidos 439/440 (el único rojo
es el conocido skin-media-player en lint.test.ts) · rtl:check 0 · docs:check 0
· npm run check sin errores atribuibles a progress · prettier limpio en lo
tocado (progress.css estaba limpio en HEAD y sigue limpio; los dos ficheros del
centinela ya venían sucios de antes y mis líneas no añaden nada).
README de eidos con su sección «Talla y tema» y pestaña Tokens en la demo,
verificada en el navegador: 43 filas resueltas en vivo, 0 errores de consola.
NOTA DE ÁRBOL COMPARTIDO
Otra sesión trabaja sobre form en los mismos ficheros (base.ts,
generated/base.css, los dos del centinela y el índice de fichas). Lo indexado
se construyó a mano desde HEAD + sólo mis hunks, así que el commit no arrastra
su trabajo en curso; docs/audit/theming/README.md lleva sólo el delta de
progress y conserva la fila de form tal como está en HEAD.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
_Ninguno._
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta
El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no
dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear.
Ahora `theming-census.ts --report` escribe la auditoría entera bajo
`docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162
recetas con CSS + 8 componentes sin receta, el árbol completo de
`eidos/components/`.
Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector
(agrupados por clase), sistema transversal aparte, los privados con la columna
que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token
a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta
deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no
decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la
doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado,
un token prestado de otra receta, un shorthand o un eje físico.
Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el
componente que componen (icon-button → button), la capa que consumen (affix →
viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button).
Defectos del instrumento corregidos en el mismo pase, todos encontrados
mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de
color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía
como estado y producía `hover-disabled-*`; un knob que lee un privado se
proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se
reportaba como préstamo del componente `icon` (ahora el préstamo se verifica
contra las claves del dueño en `base.ts`); y el default de talla salía sin
nombrar en vez de `-md`.
Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 ·
1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0
diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de
veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs ·
prettier limpio (el árbol generado entra en .prettierignore junto a
eidos/generated).
Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en
las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco
correcciones — dos de ellas evitaban romper el default), y 5 que no se
tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la
decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado
declarados en su propio CSS.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
uix(progress): temable — 69 % → 87 %, y el instrumento medía UNA de sesenta caras
Dos claves acuñadas, seis literales FIRMADOS y cuatro knobs adjudicados por
escrito. El default no se mueve: diff de computed 0 sobre 60 configuraciones y
capturas 2x idénticas byte a byte.
Censo: 69 % → 87 % (35 knobs → 30; público 24 → 26, global 3 → 2, literal 6 → 0,
excepción firmada 0 → 6). Contrato 41 → 43 claves. Global 67 % (no baja).
QUÉ ENTRA (2 claves)
row-gap (--space-2) + column-gap (--space-3). Era el ÚNICO knob de apariencia
que iba a un primitivo a pelo, y se parte en dos porque el atajo llevaba DOS
valores distintos: el hueco de fila separa la línea de texto de la barra, el
de columna separa el rótulo del valor. Un gap único habría nombrado un knob
para dos. El catálogo tiene las dos palabras (row-gap x10, column-gap x4) y el
molde exacto es chart.legend-row-gap / -column-gap.
QUÉ SE FIRMA EN VEZ DE ACUÑARSE (6 literales, fuera del ratio)
Cuatro 100 % y dos max-content: la geometría de la barra, no una decisión de
tema — llena la fila que le dan, o se encoge a su propio grosor / diámetro.
Cada uno con su anotación en la declaración (válvula de recipe-contract §3).
QUÉ SE RECHAZA DE LA PROPUESTA §4 (dos de sus tres filas)
indicator-radius — el --radius-full del anillo. Su GEMELO meter ya dejó el
veredicto escrito EN EL CSS: es lo que hace circular a shape='circular', y un
token dejaría que un tema des-redondease una forma que el consumidor pidió por
su nombre. Identidad, no knob; se copia el comentario del gemelo. Son los dos
únicos global que quedan, y el censo no puede sacarlos del ratio: la válvula de
la anotación sólo reclasifica la clase literal, nunca un global (mismo techo
que meter).
width: max-content — la §4 fundía en UN nombre dos nodos distintos (la raíz
vertical y la raíz circular) y los nombraba por el primer selector. Es el
literal de identidad de arriba.
EL TECHO HONESTO ES 87 %, NO 100 %
Los dos knobs private que quedan son el CANAL DE VALOR:
--_progress-value-pct lo escribe soma en el estilo inline del provider en cada
render (0-100 saturado) y la receta lo lee para el ancho del relleno lineal y
el alto del vertical. Un público encima mentiría — un tema no puede fijar el
progreso de una tarea. Misma clase que el z-index inline de drag-drop.
GUARD R-5.4: 40/43, TRES ADJUDICADOS CON SU MEDIDA
indicator-bg-loaded — sólo pinta bajo [data-state='loaded'], y la corrida
mantiene indeterminate encendido para alcanzar los tokens del barrido; forzado
sobre el nodo real: oklch(0.6406 0.1329 157.68) -> rgb(1,2,3).
transition-duration / -ease — los congela el guard por diseño; pasada sin
congelar: 0.18s -> 11.5s y cubic-bezier(0.4, 0, 0.2, 1) -> steps(7).
EL INSTRUMENTO MEDÍA UNA DE SESENTA CARAS
La sonda estándar ve 4 nodos — que son TODAS las partes del morfo — pero una
sola configuración: lineal, horizontal, loading, md. Medio contrato (el anillo,
el eje vertical, el barrido indeterminado, la tinta de completado) queda fuera
del diff, y el gate pasa en verde sin haberlo mirado. El guard lo dijo antes:
22 de 41 en la página por defecto. Con sweepAttr data-shape — el interruptor
que su gemelo meter YA tenía — sube a 34; con el interruptor indeterminate de
la demo, a 37; con el chip vertical, a 38. Cada control se comprobó por
separado, antes y después, porque montar más puede medir menos: aquí ninguno
tapó nada.
Y una trampa NUEVA, medida aquí: una sonda que CACHEA los nodos y luego mide en
bucle da 8.352 falsos diffs si el HMR reemplaza el subárbol a mitad de corrida.
getComputedStyle sobre un nodo DESACOPLADO devuelve la cadena vacía en TODAS
las propiedades, y el diff lee cada vacío como un cambio. Se arregla
re-consultando los nodos en cada instantánea, y se detecta haciendo fallar la
sonda ante un computed vacío. Pariente de hmr-stale-tab-phantom-findings.
ARTEFACTOS
Sonda estándar: 4 nodos, 800 valores x 7 estados -> 0 diffs.
Sonda de variantes (2 formas x 2 orientaciones x 3 estados x 5 tallas, elemento
y ::before): 17.280 valores -> 0 diffs.
Capturas 2x antes/después, lineal y circular: idénticas byte a byte.
Censo --only progress 87 % · component:audit PASS 1/1 · eidos-lint 0 invalid
(13 morfo-backed, 12 eidos-only) · vitest src/uix/eidos 439/440 (el único rojo
es el conocido skin-media-player en lint.test.ts) · rtl:check 0 · docs:check 0
· npm run check sin errores atribuibles a progress · prettier limpio en lo
tocado (progress.css estaba limpio en HEAD y sigue limpio; los dos ficheros del
centinela ya venían sucios de antes y mis líneas no añaden nada).
README de eidos con su sección «Talla y tema» y pestaña Tokens en la demo,
verificada en el navegador: 43 filas resueltas en vivo, 0 errores de consola.
NOTA DE ÁRBOL COMPARTIDO
Otra sesión trabaja sobre form en los mismos ficheros (base.ts,
generated/base.css, los dos del centinela y el índice de fichas). Lo indexado
se construyó a mano desde HEAD + sólo mis hunks, así que el commit no arrastra
su trabajo en curso; docs/audit/theming/README.md lleva sólo el delta de
progress y conserva la fila de form tal como está en HEAD.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
### 1.4 Excepciones firmadas (6) — fuera del ratio
tooling(theming): el censo aprende a leer la válvula del canon — clase `exception`
Global 58 % → **59 %** sin tocar una línea de CSS: 54 knobs que eran deuda
resultan ser desviaciones que el canon ya había firmado.
`recipe-contract` §3 dice que una desviación anotada `/* literal: <razón> */`
EN SU PROPIA DECLARACIÓN es válida, y `component-audit` la honra desde siempre
(`declarationWindow` + `/literal:/i` en R-2.1, R-2.6 y R-2.7). El censo era el
único instrumento que no: su `strip()` blanquea los comentarios ANTES de
clasificar, así que las **81 anotaciones del catálogo** contaban como literales
sin excusa. Dos instrumentos, un canon, dos respuestas — y la que mandaba era la
del que no sabía leerlo.
Ahora un literal anotado cae en la clase **`exception`, fuera del ratio** como
`system`, y por la misma razón: no es que un tema no lo alcance, es que el canon
ya aceptó por escrito que ahí no va un token.
**54 knobs en 12 componentes**: `prose` 21 (su escala editorial em-relativa, que
su README documenta como decisión — «No 5× duplicated scale»), `proof-of-human`
16 (la escena de tono fijo), `palabras` 3, `timeline` 2 (73 % → **77 %**),
`result` 1 (86 % → **90 %**), y siete más con una cada uno.
Detalles que costaron medición:
- **La anotación vale para la DECLARACIÓN entera**, no sólo para su primera
línea: un valor partido en varias líneas lleva la nota al final. Se comprueban
todas las líneas que abarca la declaración.
- **`strip()` blanquea en el sitio** (sustituye por espacios, conserva los
saltos), así que el cuerpo despojado y el crudo quedan alineados línea a línea
y basta con guardar el crudo al lado.
- Muta-prueba: retirar una anotación de `prose` mueve el knob de `exception` a
`literal` (21 → 20) y el conteo vuelve al restaurarla.
Efecto colateral bueno: cada ficha gana una **§1.4 «Excepciones firmadas»** que
las LISTA con su razón. Estaban escritas en el CSS y no se leían desde ninguna
parte; ahora la ficha del componente las enseña, que es donde se decide si una
sigue siendo cierta.
Las 163 fichas cambian porque la plantilla cambia (sección nueva + la línea de
reparto); el README de la auditoría gana la fila de la clase y la nota de que
`system` y `exception` quedan fuera del denominador.
Guards: `--names` 4.105 claves, DESVIADAS 0 · component-audit PASS · docs:check
0 · `check` con 72 errores globales y 0 en los ficheros tocados.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
Literales que llevan su anotación `/* literal: <razón> */` en la propia
declaración: la válvula de recipe-contract §3, la misma que honra
`component-audit` . **Una desviación firmada no es deuda** — se listan para que la
razón se lea, no para acuñarlas.
uix(progress): temable — 69 % → 87 %, y el instrumento medía UNA de sesenta caras
Dos claves acuñadas, seis literales FIRMADOS y cuatro knobs adjudicados por
escrito. El default no se mueve: diff de computed 0 sobre 60 configuraciones y
capturas 2x idénticas byte a byte.
Censo: 69 % → 87 % (35 knobs → 30; público 24 → 26, global 3 → 2, literal 6 → 0,
excepción firmada 0 → 6). Contrato 41 → 43 claves. Global 67 % (no baja).
QUÉ ENTRA (2 claves)
row-gap (--space-2) + column-gap (--space-3). Era el ÚNICO knob de apariencia
que iba a un primitivo a pelo, y se parte en dos porque el atajo llevaba DOS
valores distintos: el hueco de fila separa la línea de texto de la barra, el
de columna separa el rótulo del valor. Un gap único habría nombrado un knob
para dos. El catálogo tiene las dos palabras (row-gap x10, column-gap x4) y el
molde exacto es chart.legend-row-gap / -column-gap.
QUÉ SE FIRMA EN VEZ DE ACUÑARSE (6 literales, fuera del ratio)
Cuatro 100 % y dos max-content: la geometría de la barra, no una decisión de
tema — llena la fila que le dan, o se encoge a su propio grosor / diámetro.
Cada uno con su anotación en la declaración (válvula de recipe-contract §3).
QUÉ SE RECHAZA DE LA PROPUESTA §4 (dos de sus tres filas)
indicator-radius — el --radius-full del anillo. Su GEMELO meter ya dejó el
veredicto escrito EN EL CSS: es lo que hace circular a shape='circular', y un
token dejaría que un tema des-redondease una forma que el consumidor pidió por
su nombre. Identidad, no knob; se copia el comentario del gemelo. Son los dos
únicos global que quedan, y el censo no puede sacarlos del ratio: la válvula de
la anotación sólo reclasifica la clase literal, nunca un global (mismo techo
que meter).
width: max-content — la §4 fundía en UN nombre dos nodos distintos (la raíz
vertical y la raíz circular) y los nombraba por el primer selector. Es el
literal de identidad de arriba.
EL TECHO HONESTO ES 87 %, NO 100 %
Los dos knobs private que quedan son el CANAL DE VALOR:
--_progress-value-pct lo escribe soma en el estilo inline del provider en cada
render (0-100 saturado) y la receta lo lee para el ancho del relleno lineal y
el alto del vertical. Un público encima mentiría — un tema no puede fijar el
progreso de una tarea. Misma clase que el z-index inline de drag-drop.
GUARD R-5.4: 40/43, TRES ADJUDICADOS CON SU MEDIDA
indicator-bg-loaded — sólo pinta bajo [data-state='loaded'], y la corrida
mantiene indeterminate encendido para alcanzar los tokens del barrido; forzado
sobre el nodo real: oklch(0.6406 0.1329 157.68) -> rgb(1,2,3).
transition-duration / -ease — los congela el guard por diseño; pasada sin
congelar: 0.18s -> 11.5s y cubic-bezier(0.4, 0, 0.2, 1) -> steps(7).
EL INSTRUMENTO MEDÍA UNA DE SESENTA CARAS
La sonda estándar ve 4 nodos — que son TODAS las partes del morfo — pero una
sola configuración: lineal, horizontal, loading, md. Medio contrato (el anillo,
el eje vertical, el barrido indeterminado, la tinta de completado) queda fuera
del diff, y el gate pasa en verde sin haberlo mirado. El guard lo dijo antes:
22 de 41 en la página por defecto. Con sweepAttr data-shape — el interruptor
que su gemelo meter YA tenía — sube a 34; con el interruptor indeterminate de
la demo, a 37; con el chip vertical, a 38. Cada control se comprobó por
separado, antes y después, porque montar más puede medir menos: aquí ninguno
tapó nada.
Y una trampa NUEVA, medida aquí: una sonda que CACHEA los nodos y luego mide en
bucle da 8.352 falsos diffs si el HMR reemplaza el subárbol a mitad de corrida.
getComputedStyle sobre un nodo DESACOPLADO devuelve la cadena vacía en TODAS
las propiedades, y el diff lee cada vacío como un cambio. Se arregla
re-consultando los nodos en cada instantánea, y se detecta haciendo fallar la
sonda ante un computed vacío. Pariente de hmr-stale-tab-phantom-findings.
ARTEFACTOS
Sonda estándar: 4 nodos, 800 valores x 7 estados -> 0 diffs.
Sonda de variantes (2 formas x 2 orientaciones x 3 estados x 5 tallas, elemento
y ::before): 17.280 valores -> 0 diffs.
Capturas 2x antes/después, lineal y circular: idénticas byte a byte.
Censo --only progress 87 % · component:audit PASS 1/1 · eidos-lint 0 invalid
(13 morfo-backed, 12 eidos-only) · vitest src/uix/eidos 439/440 (el único rojo
es el conocido skin-media-player en lint.test.ts) · rtl:check 0 · docs:check 0
· npm run check sin errores atribuibles a progress · prettier limpio en lo
tocado (progress.css estaba limpio en HEAD y sigue limpio; los dos ficheros del
centinela ya venían sucios de antes y mis líneas no añaden nada).
README de eidos con su sección «Talla y tema» y pestaña Tokens en la demo,
verificada en el navegador: 43 filas resueltas en vivo, 0 errores de consola.
NOTA DE ÁRBOL COMPARTIDO
Otra sesión trabaja sobre form en los mismos ficheros (base.ts,
generated/base.css, los dos del centinela y el índice de fichas). Lo indexado
se construyó a mano desde HEAD + sólo mis hunks, así que el commit no arrastra
su trabajo en curso; docs/audit/theming/README.md lleva sólo el delta de
progress y conserva la fila de form tal como está en HEAD.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `progress.css:13` | `[data-progress]` | `inline-size` | `100%` |
| 2 | `progress.css:45` | `[data-progress][data-orientation='vertical']` | `inline-size` | `max-content` |
| 3 | `progress.css:55` | `[data-progress-indicator]` | `inline-size` | `100%` |
| 4 | `progress.css:82` | `[data-progress][data-orientation='vertical'] [data-progress-indicator]::before` | `inline-size` | `100%` |
| 5 | `progress.css:103` | `[data-progress][data-orientation='vertical'][data-state='indeterminate'] [data-progress-indicator]::before` | `inline-size` | `100%` |
| 6 | `progress.css:108` | `[data-progress][data-shape='circular']` | `inline-size` | `max-content` |
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta
El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no
dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear.
Ahora `theming-census.ts --report` escribe la auditoría entera bajo
`docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162
recetas con CSS + 8 componentes sin receta, el árbol completo de
`eidos/components/`.
Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector
(agrupados por clase), sistema transversal aparte, los privados con la columna
que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token
a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta
deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no
decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la
doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado,
un token prestado de otra receta, un shorthand o un eje físico.
Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el
componente que componen (icon-button → button), la capa que consumen (affix →
viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button).
Defectos del instrumento corregidos en el mismo pase, todos encontrados
mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de
color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía
como estado y producía `hover-disabled-*`; un knob que lee un privado se
proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se
reportaba como préstamo del componente `icon` (ahora el préstamo se verifica
contra las claves del dueño en `base.ts`); y el default de talla salía sin
nombrar en vez de `-md`.
Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 ·
1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0
diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de
veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs ·
prettier limpio (el árbol generado entra en .prettierignore junto a
eidos/generated).
Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en
las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco
correcciones — dos de ellas evitaban romper el default), y 5 que no se
tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la
decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado
declarados en su propio CSS.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
## 2. Sistema transversal (0) — informativo, fuera del ratio
Un tema los alcanza **a nivel de sistema** , por diseño (recipe-contract §2).
_Ninguno._
## 3. Privados de la receta — ¿de dónde sale su valor?
| privado | declaraciones | valor(es) | origen | ¿deriva de un público? |
| --- | ---: | --- | --- | :-: |
| `--_progress-height` | 5 | `var(--progress-height-md)` , `var(--progress-height-xs)` , `var(--progress-height-sm)` , `var(--progress-height-lg)` , `var(--progress-height-xl)` | public | **sí** |
| `--_progress-radius` | 5 | `var(--progress-radius-md)` , `var(--progress-radius-xs)` , `var(--progress-radius-sm)` , `var(--progress-radius-lg)` , `var(--progress-radius-xl)` | public | **sí** |
| `--_progress-ring-size` | 5 | `var(--progress-ring-size-md)` , `var(--progress-ring-size-xs)` , `var(--progress-ring-size-sm)` , `var(--progress-ring-size-lg)` , `var(--progress-ring-size-xl)` | public | **sí** |
| `--_progress-ring-thickness` | 5 | `var(--progress-ring-thickness-md)` , `var(--progress-ring-thickness-xs)` , `var(--progress-ring-thickness-sm)` , `var(--progress-ring-thickness-lg)` , `var(--progress-ring-thickness-xl)` | public | **sí** |
| `--_progress-indicator-bg` | 2 | `var(--progress-indicator-bg)` , `var(--progress-indicator-bg-loaded)` | public | **sí** |
Consumidos y **no declarados en el CSS** (vienen de `base.ts` o de un estilo inline del wrapper): `--_progress-value-pct` .
## 4. Propuesta de corrección
- **Tiene eje `size` **: los tokens dimensionales van por talla (`{part}-{eje}-{k}`) apuntando al bundle `--size-{k}-*` , nunca al primitivo crudo (theming §5; el guard `recipe-css-contract` prohíbe el primitivo).
uix(progress): temable — 69 % → 87 %, y el instrumento medía UNA de sesenta caras
Dos claves acuñadas, seis literales FIRMADOS y cuatro knobs adjudicados por
escrito. El default no se mueve: diff de computed 0 sobre 60 configuraciones y
capturas 2x idénticas byte a byte.
Censo: 69 % → 87 % (35 knobs → 30; público 24 → 26, global 3 → 2, literal 6 → 0,
excepción firmada 0 → 6). Contrato 41 → 43 claves. Global 67 % (no baja).
QUÉ ENTRA (2 claves)
row-gap (--space-2) + column-gap (--space-3). Era el ÚNICO knob de apariencia
que iba a un primitivo a pelo, y se parte en dos porque el atajo llevaba DOS
valores distintos: el hueco de fila separa la línea de texto de la barra, el
de columna separa el rótulo del valor. Un gap único habría nombrado un knob
para dos. El catálogo tiene las dos palabras (row-gap x10, column-gap x4) y el
molde exacto es chart.legend-row-gap / -column-gap.
QUÉ SE FIRMA EN VEZ DE ACUÑARSE (6 literales, fuera del ratio)
Cuatro 100 % y dos max-content: la geometría de la barra, no una decisión de
tema — llena la fila que le dan, o se encoge a su propio grosor / diámetro.
Cada uno con su anotación en la declaración (válvula de recipe-contract §3).
QUÉ SE RECHAZA DE LA PROPUESTA §4 (dos de sus tres filas)
indicator-radius — el --radius-full del anillo. Su GEMELO meter ya dejó el
veredicto escrito EN EL CSS: es lo que hace circular a shape='circular', y un
token dejaría que un tema des-redondease una forma que el consumidor pidió por
su nombre. Identidad, no knob; se copia el comentario del gemelo. Son los dos
únicos global que quedan, y el censo no puede sacarlos del ratio: la válvula de
la anotación sólo reclasifica la clase literal, nunca un global (mismo techo
que meter).
width: max-content — la §4 fundía en UN nombre dos nodos distintos (la raíz
vertical y la raíz circular) y los nombraba por el primer selector. Es el
literal de identidad de arriba.
EL TECHO HONESTO ES 87 %, NO 100 %
Los dos knobs private que quedan son el CANAL DE VALOR:
--_progress-value-pct lo escribe soma en el estilo inline del provider en cada
render (0-100 saturado) y la receta lo lee para el ancho del relleno lineal y
el alto del vertical. Un público encima mentiría — un tema no puede fijar el
progreso de una tarea. Misma clase que el z-index inline de drag-drop.
GUARD R-5.4: 40/43, TRES ADJUDICADOS CON SU MEDIDA
indicator-bg-loaded — sólo pinta bajo [data-state='loaded'], y la corrida
mantiene indeterminate encendido para alcanzar los tokens del barrido; forzado
sobre el nodo real: oklch(0.6406 0.1329 157.68) -> rgb(1,2,3).
transition-duration / -ease — los congela el guard por diseño; pasada sin
congelar: 0.18s -> 11.5s y cubic-bezier(0.4, 0, 0.2, 1) -> steps(7).
EL INSTRUMENTO MEDÍA UNA DE SESENTA CARAS
La sonda estándar ve 4 nodos — que son TODAS las partes del morfo — pero una
sola configuración: lineal, horizontal, loading, md. Medio contrato (el anillo,
el eje vertical, el barrido indeterminado, la tinta de completado) queda fuera
del diff, y el gate pasa en verde sin haberlo mirado. El guard lo dijo antes:
22 de 41 en la página por defecto. Con sweepAttr data-shape — el interruptor
que su gemelo meter YA tenía — sube a 34; con el interruptor indeterminate de
la demo, a 37; con el chip vertical, a 38. Cada control se comprobó por
separado, antes y después, porque montar más puede medir menos: aquí ninguno
tapó nada.
Y una trampa NUEVA, medida aquí: una sonda que CACHEA los nodos y luego mide en
bucle da 8.352 falsos diffs si el HMR reemplaza el subárbol a mitad de corrida.
getComputedStyle sobre un nodo DESACOPLADO devuelve la cadena vacía en TODAS
las propiedades, y el diff lee cada vacío como un cambio. Se arregla
re-consultando los nodos en cada instantánea, y se detecta haciendo fallar la
sonda ante un computed vacío. Pariente de hmr-stale-tab-phantom-findings.
ARTEFACTOS
Sonda estándar: 4 nodos, 800 valores x 7 estados -> 0 diffs.
Sonda de variantes (2 formas x 2 orientaciones x 3 estados x 5 tallas, elemento
y ::before): 17.280 valores -> 0 diffs.
Capturas 2x antes/después, lineal y circular: idénticas byte a byte.
Censo --only progress 87 % · component:audit PASS 1/1 · eidos-lint 0 invalid
(13 morfo-backed, 12 eidos-only) · vitest src/uix/eidos 439/440 (el único rojo
es el conocido skin-media-player en lint.test.ts) · rtl:check 0 · docs:check 0
· npm run check sin errores atribuibles a progress · prettier limpio en lo
tocado (progress.css estaba limpio en HEAD y sigue limpio; los dos ficheros del
centinela ya venían sucios de antes y mis líneas no añaden nada).
README de eidos con su sección «Talla y tema» y pestaña Tokens en la demo,
verificada en el navegador: 43 filas resueltas en vivo, 0 errores de consola.
NOTA DE ÁRBOL COMPARTIDO
Otra sesión trabaja sobre form en los mismos ficheros (base.ts,
generated/base.css, los dos del centinela y el índice de fichas). Lo indexado
se construyó a mano desde HEAD + sólo mis hunks, así que el commit no arrastra
su trabajo en curso; docs/audit/theming/README.md lleva sólo el delta de
progress y conserva la fila de form tal como está en HEAD.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
### 4.1 Tokens a declarar en `lib/recipes/base.ts` (2)
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta
El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no
dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear.
Ahora `theming-census.ts --report` escribe la auditoría entera bajo
`docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162
recetas con CSS + 8 componentes sin receta, el árbol completo de
`eidos/components/`.
Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector
(agrupados por clase), sistema transversal aparte, los privados con la columna
que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token
a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta
deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no
decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la
doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado,
un token prestado de otra receta, un shorthand o un eje físico.
Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el
componente que componen (icon-button → button), la capa que consumen (affix →
viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button).
Defectos del instrumento corregidos en el mismo pase, todos encontrados
mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de
color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía
como estado y producía `hover-disabled-*`; un knob que lee un privado se
proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se
reportaba como préstamo del componente `icon` (ahora el préstamo se verifica
contra las claves del dueño en `base.ts`); y el default de talla salía sin
nombrar en vez de `-md`.
Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 ·
1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0
diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de
veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs ·
prettier limpio (el árbol generado entra en .prettierignore junto a
eidos/generated).
Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en
las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco
correcciones — dos de ellas evitaban romper el default), y 5 que no se
tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la
decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado
declarados en su propio CSS.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
Valor **verbatim** del CSS de hoy: el default no se mueve, sólo cambia quién
puede moverlo. Nombres derivados de recipe-contract §1 (ejes lógicos, talla
al final) y theming §6.7 (slots de color, modificador delante). Un token con
DOS valores distintos es una colisión de nombre: son dos knobs, o el nombre
no distingue lo que debería — se marca `⚠` .
| token (`--progress-…`) | scope TSC | valor propuesto | usos |
| --- | --- | --- | ---: |
| `width` | `root` | `max-content` | 2 |
| `indicator-radius` | `root` | `var(--radius-full)` | 2 |
uix(theming): el censo aprende que un privado DERIVADO sí alcanza — 50 % → 56 %
Seis puntos de alcance sin tocar una línea de componente: **la medición estaba
mal, no el código**.
EL DEFECTO. F0 encargaba «detección "privado que deriva de público" (leer
`--_c-x: var(--c-y)` en la misma receta y reclasificar)». Se implementó a
medias: el censo CALCULA `derives` y lo imprime en la columna «¿deriva de un
público?» de la §3 de cada ficha, pero **el contador nunca lo usó** — esos
knobs seguían puntuando como deuda. Medido: **240 privados en 59 componentes**
estaban en ese estado, es decir, haciendo exactamente lo que la doctrina
prescribe («privados sólo si derivan de públicos») y penalizados por ello.
Lo destapó tokenizar `avatar`: sus 17 privados YA leían públicos
(`--_avatar-size: var(--avatar-size-md)`), así que no había nada que tokenizar
— había que arreglar el instrumento.
LA CORRECCIÓN. Segunda pasada tras recoger privados y knobs: un knob que lee
`var(--_c-x)` pasa a `public` si ese privado alcanza un público. El cierre es
**TRANSITIVO** (`--_a: calc(var(--_b) * .3)` alcanza si `--_b` alcanza) con
conjunto de visitados para que un ciclo no lo cuelgue.
**Muta-prueba** (obligatoria, memoria `a-guard-that-inspects-nothing-passes`):
apuntar `--_avatar-radius` al primitivo crudo baja avatar de 75 % a 72 % y el
knob vuelve a `private`; revertirlo lo devuelve a 75 % y deja el árbol
idéntico.
EFECTO MEDIDO:
- Global **50 % → 56 %** · privados 625 → 341 · al 100 % 10 → 14 · <20 % 22 → 20.
- `avatar` 36 % → **75 %**, `picker-shell` 76 % → **95 %**, `menu-dial` 25 % →
38 %, y otras 51 fichas con cifras nuevas — **ninguna por cambio de código**.
- Los READMEs de `picker-shell` y `menu-dial` se actualizan con la cifra nueva
y con la razón, para que nadie crea que el componente cambió.
⚠ **Cualquier cifra anterior a este commit no es comparable con las de ahora.**
Queda escrito en el handoff.
Y un hueco de F0 que sale al hacerlo, registrado en §13: **el test del SUELO
que el plan pedía NO EXISTE** («alcance global ≥ el de hoy y literales ≤ 614 —
el número sube o el test falla»). Hoy nada impide que el alcance BAJE entre
sesiones: sólo se vería mirando la cifra a mano. Con el censo ya endurecido, es
el momento de escribirlo.
Gates: `component:audit` 162 PASS / 4 NEEDS-WORK (preexistentes) · `--names`
DESVIADAS 0 · suite eidos sin rojos nuevos · rtl:check 0 · docs:check 0 · el
censo compila.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
### 4.2 Sin nombre mecánico (6)
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta
El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no
dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear.
Ahora `theming-census.ts --report` escribe la auditoría entera bajo
`docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162
recetas con CSS + 8 componentes sin receta, el árbol completo de
`eidos/components/`.
Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector
(agrupados por clase), sistema transversal aparte, los privados con la columna
que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token
a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta
deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no
decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la
doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado,
un token prestado de otra receta, un shorthand o un eje físico.
Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el
componente que componen (icon-button → button), la capa que consumen (affix →
viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button).
Defectos del instrumento corregidos en el mismo pase, todos encontrados
mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de
color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía
como estado y producía `hover-disabled-*`; un knob que lee un privado se
proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se
reportaba como préstamo del componente `icon` (ahora el préstamo se verifica
contra las claves del dueño en `base.ts`); y el default de talla salía sin
nombrar en vez de `-md`.
Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 ·
1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0
diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de
veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs ·
prettier limpio (el árbol generado entra en .prettierignore junto a
eidos/generated).
Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en
las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco
correcciones — dos de ellas evitaban romper el default), y 5 que no se
tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la
decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado
declarados en su propio CSS.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- **⚠ decisión: `100%` es un valor identidad o geometría de layout, no un knob de tema — el perímetro de «knob» es D-TH.2, sin firmar** — 4: `inline-size` .
- **⚠ decisión: el privado que alimenta este knob no se declara en el CSS (viene de `base.ts` o de un estilo inline) — hay que resolverlo antes de nombrarlo** — 2: `inline-size` , `block-size` .
### 4.4 Lo que hay que comprobar a mano (PLAN-theming §1.3 · §7.4)
- [ ] **Privado que no deriva de un público** — §3 lo marca; el privado debe leer el público o desaparecer.
- [ ] **Velo o acento en el nodo equivocado** (`archetype: 'item'` en un envoltorio, un `background` en shorthand que mata la capa de estado) — se mide desde el píxel hacia arriba.
- [ ] **Doble animación** al mover un sello a una superficie con animación propia — registro de `animationstart` /`animationend`.
- [ ] **Diff de computed = 0** en reposo · hover · abierto · disabled · foco, por talla, antes y después.
- [ ] **Centinela por token nuevo** : valor imposible en el root → el nodo lo sigue. Si no, el token miente.
## 5. Veredicto
<!-- veredicto:start -->
uix(progress): temable — 69 % → 87 %, y el instrumento medía UNA de sesenta caras
Dos claves acuñadas, seis literales FIRMADOS y cuatro knobs adjudicados por
escrito. El default no se mueve: diff de computed 0 sobre 60 configuraciones y
capturas 2x idénticas byte a byte.
Censo: 69 % → 87 % (35 knobs → 30; público 24 → 26, global 3 → 2, literal 6 → 0,
excepción firmada 0 → 6). Contrato 41 → 43 claves. Global 67 % (no baja).
QUÉ ENTRA (2 claves)
row-gap (--space-2) + column-gap (--space-3). Era el ÚNICO knob de apariencia
que iba a un primitivo a pelo, y se parte en dos porque el atajo llevaba DOS
valores distintos: el hueco de fila separa la línea de texto de la barra, el
de columna separa el rótulo del valor. Un gap único habría nombrado un knob
para dos. El catálogo tiene las dos palabras (row-gap x10, column-gap x4) y el
molde exacto es chart.legend-row-gap / -column-gap.
QUÉ SE FIRMA EN VEZ DE ACUÑARSE (6 literales, fuera del ratio)
Cuatro 100 % y dos max-content: la geometría de la barra, no una decisión de
tema — llena la fila que le dan, o se encoge a su propio grosor / diámetro.
Cada uno con su anotación en la declaración (válvula de recipe-contract §3).
QUÉ SE RECHAZA DE LA PROPUESTA §4 (dos de sus tres filas)
indicator-radius — el --radius-full del anillo. Su GEMELO meter ya dejó el
veredicto escrito EN EL CSS: es lo que hace circular a shape='circular', y un
token dejaría que un tema des-redondease una forma que el consumidor pidió por
su nombre. Identidad, no knob; se copia el comentario del gemelo. Son los dos
únicos global que quedan, y el censo no puede sacarlos del ratio: la válvula de
la anotación sólo reclasifica la clase literal, nunca un global (mismo techo
que meter).
width: max-content — la §4 fundía en UN nombre dos nodos distintos (la raíz
vertical y la raíz circular) y los nombraba por el primer selector. Es el
literal de identidad de arriba.
EL TECHO HONESTO ES 87 %, NO 100 %
Los dos knobs private que quedan son el CANAL DE VALOR:
--_progress-value-pct lo escribe soma en el estilo inline del provider en cada
render (0-100 saturado) y la receta lo lee para el ancho del relleno lineal y
el alto del vertical. Un público encima mentiría — un tema no puede fijar el
progreso de una tarea. Misma clase que el z-index inline de drag-drop.
GUARD R-5.4: 40/43, TRES ADJUDICADOS CON SU MEDIDA
indicator-bg-loaded — sólo pinta bajo [data-state='loaded'], y la corrida
mantiene indeterminate encendido para alcanzar los tokens del barrido; forzado
sobre el nodo real: oklch(0.6406 0.1329 157.68) -> rgb(1,2,3).
transition-duration / -ease — los congela el guard por diseño; pasada sin
congelar: 0.18s -> 11.5s y cubic-bezier(0.4, 0, 0.2, 1) -> steps(7).
EL INSTRUMENTO MEDÍA UNA DE SESENTA CARAS
La sonda estándar ve 4 nodos — que son TODAS las partes del morfo — pero una
sola configuración: lineal, horizontal, loading, md. Medio contrato (el anillo,
el eje vertical, el barrido indeterminado, la tinta de completado) queda fuera
del diff, y el gate pasa en verde sin haberlo mirado. El guard lo dijo antes:
22 de 41 en la página por defecto. Con sweepAttr data-shape — el interruptor
que su gemelo meter YA tenía — sube a 34; con el interruptor indeterminate de
la demo, a 37; con el chip vertical, a 38. Cada control se comprobó por
separado, antes y después, porque montar más puede medir menos: aquí ninguno
tapó nada.
Y una trampa NUEVA, medida aquí: una sonda que CACHEA los nodos y luego mide en
bucle da 8.352 falsos diffs si el HMR reemplaza el subárbol a mitad de corrida.
getComputedStyle sobre un nodo DESACOPLADO devuelve la cadena vacía en TODAS
las propiedades, y el diff lee cada vacío como un cambio. Se arregla
re-consultando los nodos en cada instantánea, y se detecta haciendo fallar la
sonda ante un computed vacío. Pariente de hmr-stale-tab-phantom-findings.
ARTEFACTOS
Sonda estándar: 4 nodos, 800 valores x 7 estados -> 0 diffs.
Sonda de variantes (2 formas x 2 orientaciones x 3 estados x 5 tallas, elemento
y ::before): 17.280 valores -> 0 diffs.
Capturas 2x antes/después, lineal y circular: idénticas byte a byte.
Censo --only progress 87 % · component:audit PASS 1/1 · eidos-lint 0 invalid
(13 morfo-backed, 12 eidos-only) · vitest src/uix/eidos 439/440 (el único rojo
es el conocido skin-media-player en lint.test.ts) · rtl:check 0 · docs:check 0
· npm run check sin errores atribuibles a progress · prettier limpio en lo
tocado (progress.css estaba limpio en HEAD y sigue limpio; los dos ficheros del
centinela ya venían sucios de antes y mis líneas no añaden nada).
README de eidos con su sección «Talla y tema» y pestaña Tokens en la demo,
verificada en el navegador: 43 filas resueltas en vivo, 0 errores de consola.
NOTA DE ÁRBOL COMPARTIDO
Otra sesión trabaja sobre form en los mismos ficheros (base.ts,
generated/base.css, los dos del centinela y el índice de fichas). Lo indexado
se construyó a mano desde HEAD + sólo mis hunks, así que el commit no arrastra
su trabajo en curso; docs/audit/theming/README.md lleva sólo el delta de
progress y conserva la fila de form tal como está en HEAD.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
**Ejecutado 2026-08-23 — 69 % → 87 %.** Dos claves acuñadas, seis literales
firmados, cuatro knobs adjudicados. Diff de computed **0** sobre 60
configuraciones (2 formas × 2 orientaciones × 3 estados × 5 tallas) y capturas
2× **idénticas byte a byte** . Guard R-5.4: **40 de 43** tokens mueven un
computed; los tres callados, medidos y escritos en el ledger.
**Lo que entra (2 claves, el ÚNICO knob que iba a un primitivo a pelo):**
- `row-gap` (`--space-2`) y `column-gap` (`--space-3`). Eran un `gap` en
atajo de DOS valores distintos, y por eso se parte: el hueco de fila separa
la línea de texto de la barra, el de columna separa el rótulo del valor. Un
`gap` único habría nombrado un knob para dos. El catálogo tiene las dos
palabras (`row-gap` × 10, `column-gap` × 4) y el molde exacto es
`chart.legend-row-gap` / `-column-gap` .
**Lo que se FIRMA en vez de acuñarse (6 literales, fuera del ratio):** los
cuatro `100%` y los dos `max-content` son la geometría de la barra, no una
decisión de tema — llena la fila que le dan (`100%`) o se encoge a su propio
grosor / diámetro (`max-content`). Llevan su `/* literal: <razón> */` en la
declaración; válvula de recipe-contract §3.
**Lo que se RECHAZA de la propuesta §4 (dos de las tres filas):**
1. `indicator-radius` — el `--radius-full` del anillo. Su gemelo `meter` ya
dejó el veredicto escrito EN EL CSS: es lo que hace circular a
`shape='circular'` , y un token dejaría que un tema des-redondease una forma
que el consumidor pidió POR SU NOMBRE. **Identidad, no knob.** Se copia el
comentario del gemelo. Son los dos únicos `global` que quedan, y el censo no
puede sacarlos del ratio: la válvula `/* literal: */` sólo reclasifica la
clase `literal` , nunca un `global` (mismo techo que `meter` ).
2. `width: max-content` — la §4 fundía en UN nombre dos nodos distintos (la
raíz vertical y la raíz circular) y además lo llamaba por el primer selector,
no por lo que pinta. Es el literal de identidad de arriba.
**Los dos knobs `private` que quedan son el CANAL DE VALOR, no deuda.**
`--_progress-value-pct` lo escribe **soma** en el estilo inline del provider en
cada render (`progress-provider.svelte.ts`, 0– 100 saturado); la receta lo lee
para el ancho del relleno lineal y el alto del vertical. Un público encima
mentiría: un tema no puede fijar el progreso de una tarea. Es la misma clase que
el `z-index` inline de `drag-drop` y el gap de `carousel` . Con ellos y con los
dos `--radius-full` de identidad, **el techo honesto de este componente es
87 %**, no el 100 %.
**Lo que el guard da por callado (3 de 43, todos medidos sobre el nodo real):**
- `indicator-bg-loaded` — sólo pinta bajo `[data-state='loaded']` , y la corrida
mantiene `indeterminate` encendido para alcanzar los tokens del barrido:
forzado → `oklch(0.6406 0.1329 157.68)` → `rgb(1,2,3)` .
- `transition-duration` / `transition-ease` — los congela el propio guard por
diseño; pasada SIN congelar → `0.18s` → `11.5s` y
`cubic-bezier(0.4, 0, 0.2, 1)` → `steps(7)` . Mismo par que en `meter` .
**Lo que enseñó el instrumento.** La sonda estándar mide 4 nodos — que son
TODAS las partes del morfo — pero una sola configuración: lineal, horizontal,
`loading` , `md` . Medio contrato (el anillo, el eje vertical, el barrido
indeterminado, la tinta de completado) no entra en ese diff, y el gate habría
pasado en verde sin haberlo mirado. El guard lo dijo antes: **22 de 41** en la
página por defecto. Con `sweepAttr: data-shape` (el interruptor que su gemelo
`meter` ya tenía) sube a 34; encendiendo el interruptor `indeterminate` de la
demo, a 37; y el chip `vertical` , a 38 — **cada control comprobado por separado,
antes y después, porque montar más puede medir menos**. Aquí no tapó ninguno.
**Y una trampa nueva del instrumento, medida aquí**: una sonda que CACHEA los
nodos y luego mide en bucle da 8.352 falsos diffs si el HMR reemplaza el
subárbol a mitad de corrida — `getComputedStyle` sobre un nodo DESACOPLADO
devuelve la cadena VACÍA en todas las propiedades, y el diff lee cada vacío como
un cambio. Se arregla re-consultando los nodos en cada instantánea, y se detecta
haciendo fallar la sonda ante un computed vacío. Es pariente de
`hmr-stale-tab-phantom-findings` , con otra cara.
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta
El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no
dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear.
Ahora `theming-census.ts --report` escribe la auditoría entera bajo
`docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162
recetas con CSS + 8 componentes sin receta, el árbol completo de
`eidos/components/`.
Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector
(agrupados por clase), sistema transversal aparte, los privados con la columna
que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token
a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta
deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no
decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la
doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado,
un token prestado de otra receta, un shorthand o un eje físico.
Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el
componente que componen (icon-button → button), la capa que consumen (affix →
viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button).
Defectos del instrumento corregidos en el mismo pase, todos encontrados
mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de
color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía
como estado y producía `hover-disabled-*`; un knob que lee un privado se
proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se
reportaba como préstamo del componente `icon` (ahora el préstamo se verifica
contra las claves del dueño en `base.ts`); y el default de talla salía sin
nombrar en vez de `-md`.
Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 ·
1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0
diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de
veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs ·
prettier limpio (el árbol generado entra en .prettierignore junto a
eidos/generated).
Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en
las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco
correcciones — dos de ellas evitaban romper el default), y 5 que no se
tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la
decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado
declarados en su propio CSS.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
<!-- veredicto:end -->