docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica
El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes
corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro
del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y
no por la memoria de nadie.
`CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola
inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa
concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia
calendar, mandato de Field, el perímetro del knob para la familia tipográfica,
el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes →
después. El plan gana su §8 con el registro completo y corrige su cabecera, que
seguía diciendo que no había nada construido.
Y se commitea el instrumental, porque sin él el protocolo §7 es una intención:
la sonda de computed antes/después, el diff que es el gate (vacío o se para), el
centinela que pregunta si cada token alcanza de verdad, y la captura del stage.
Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen
dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev
server.
Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se
consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el
`animationend` para montar el panel, y si lo congelas mides un popup que no
existe); un token resuelto no se mueve desde `:root` por diseño; un panel
portalado no ve el ámbito del componente; la demo puede tapar el componente que
mides; y un valor centinela igual al real se lee como «no efecto».
Igual las cuatro trampas que costaron un commit cada una, con su guard al lado:
la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los
bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el
diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el
encabezado que un Edit se come al insertar una sección.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
/**
|
|
|
|
|
* __theming-probe — the before/after computed-style probe of PLAN-theming §7.2/§7.4.
|
|
|
|
|
*
|
|
|
|
|
* node scripts/__theming-probe.ts <component> <out.json> [url]
|
|
|
|
|
*
|
|
|
|
|
* Temporary instrument (the `__` prefix keeps it out of the tracked surface).
|
|
|
|
|
* Walks the component demo, and for every node matching `[data-{c}…]` records
|
|
|
|
|
* the knob computed values at rest, per size, hovered and focused. The size is
|
|
|
|
|
* forced by WRITING `data-size` on the nodes that already carry it — the recipe
|
|
|
|
|
* selects on that attribute, so the computed result is the same one the real
|
|
|
|
|
* interaction produces, without depending on each demo's chips.
|
|
|
|
|
*
|
|
|
|
|
* Run it with plain `node`, NOT with tsx: the tsx loader injects helpers that
|
|
|
|
|
* are not defined inside `page.evaluate`, and every probe throws.
|
|
|
|
|
*
|
|
|
|
|
* Must run from the repo ROOT (playwright does not resolve from the scratchpad)
|
|
|
|
|
* and against a live dev server; a hidden screen freezes rAF, so headless is the
|
|
|
|
|
* reliable surface.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import { writeFileSync } from 'node:fs';
|
|
|
|
|
import { chromium } from 'playwright';
|
|
|
|
|
|
|
|
|
|
const PROPS = [
|
|
|
|
|
'backgroundColor',
|
|
|
|
|
'backgroundImage',
|
|
|
|
|
'color',
|
|
|
|
|
'borderTopColor',
|
|
|
|
|
'borderTopWidth',
|
|
|
|
|
'borderTopLeftRadius',
|
|
|
|
|
'borderBottomRightRadius',
|
|
|
|
|
'paddingTop',
|
|
|
|
|
'paddingRight',
|
|
|
|
|
'paddingBottom',
|
|
|
|
|
'paddingLeft',
|
|
|
|
|
'rowGap',
|
|
|
|
|
'columnGap',
|
|
|
|
|
'fontSize',
|
|
|
|
|
'fontWeight',
|
|
|
|
|
'fontFamily',
|
|
|
|
|
'lineHeight',
|
|
|
|
|
'letterSpacing',
|
|
|
|
|
'blockSize',
|
|
|
|
|
'inlineSize',
|
|
|
|
|
'minBlockSize',
|
|
|
|
|
'minInlineSize',
|
|
|
|
|
'boxShadow',
|
|
|
|
|
'opacity',
|
|
|
|
|
'fill',
|
|
|
|
|
'stroke',
|
uix(onion-menu): temable — 29 % → 84 %, y una regla que el navegador DESCARTA
7 claves nuevas (12 → 19) y 5 literales anotados. Lo que queda fuera son TRES
filas y ninguna es deuda de tokenización: una es canal de valor y dos viven en
una regla inválida.
Acuñado el color del ANILLO DEL SECTOR —uno para los dos estados, porque el
propio comentario del CSS dice que el hover usa «el mismo color que el anillo de
selección, más claro» y lo que los separa es la opacidad, que ya tenía token—,
la tipografía de la etiqueta, el radio y la tinta del disparador, y las dos
piezas del glifo `+`→`×`. Dos limpiezas de paso: un
`var(--style-label-font-family, var(--style-label-font-family))` —respaldo de sí
mismo, el defecto que ya se cazó en context-menu— y dos `var(--radius-full, …)`
con un respaldo que no puede ocurrir.
Cinco literales pasan a excepción POR IDENTIDAD: dos `opacity: 1` que DESHACEN
un atenuado (sin atenuar es 1 por definición) y la caja del icono.
⚠ `.onion-menu-icon :global(svg)` NO EXISTE. `:global()` es un envoltorio de
SVELTE y esto es un CSS plano: el selector es inválido y el navegador descarta
la regla entera. Medido: el glifo computa 14px —el tamaño propio del Icon— y el
`70 %` que el fichero cree pintar no pinta. Acuñé un `icon-glyph-size`, el
centinela lo delató y lo RETIRÉ; los literales se quedan con el defecto escrito
encima, porque arreglar el selector empieza a pintar el 70 % y MUEVE PÍXEL.
Mismo bug en `timeline.css:331`; `image-picker.css` ya documentaba la trampa
—la lección estaba escrita y se repitió—. → §13.
Canal de valor: el fondo del disparador lo escribe el componente INLINE desde el
motor de color, como los rellenos de cada sector. No es superficie de tema.
TRES CEGUERAS DEL INSTRUMENTO, dos nuevas y genéricas:
1. EL GUARD CERRABA LA SUPERFICIE. Este menú nace ABIERTO y el clic de «abrir»
lo cerraba: 12 de 20 tokens leían muertos. Ahora, si hay `openMarker` y ya
casa, no se pulsa nada (8 → 14 de golpe).
2. Sus tripas cuelgan de CLASES, no de `data-onion-*`: sonda y guard las miden
con `extraNodes` — 37 nodos en reposo contra los 5 con atributo.
3. Congelar las transiciones NO BASTA cuando el nodo está ANIMADO:
`rim-glow-opacity` leía 0,482956 —el valor animado— y parecía muerto; con la
animación congelada computa 0,45 y sigue al token. → §13.
Los cuatro tokens del rim son del look `luminous`, que la demo del componente no
renderiza (es una prop de Svelte que emite OTRO marcado, así que no hay atributo
que forzar): medidos en `/active/docs/agnt`, que sí lo usa.
Gates: sonda 0 diffs en 7.328 valores × 8 estados (37 nodos) · centinela 14/19
con las cinco adjudicadas · censo 84 % · `component:audit` PASS · suite eidos
con el rojo conocido ajeno · `rtl:check` 0 · `docs:check` 0 · `check` sin
errores propios · capturas de reposo, sector en hover y disparador.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
'strokeWidth',
|
|
|
|
|
'strokeOpacity',
|
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica
El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes
corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro
del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y
no por la memoria de nadie.
`CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola
inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa
concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia
calendar, mandato de Field, el perímetro del knob para la familia tipográfica,
el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes →
después. El plan gana su §8 con el registro completo y corrige su cabecera, que
seguía diciendo que no había nada construido.
Y se commitea el instrumental, porque sin él el protocolo §7 es una intención:
la sonda de computed antes/después, el diff que es el gate (vacío o se para), el
centinela que pregunta si cada token alcanza de verdad, y la captura del stage.
Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen
dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev
server.
Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se
consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el
`animationend` para montar el panel, y si lo congelas mides un popup que no
existe); un token resuelto no se mueve desde `:root` por diseño; un panel
portalado no ve el ámbito del componente; la demo puede tapar el componente que
mides; y un valor centinela igual al real se lee como «no efecto».
Igual las cuatro trampas que costaron un commit cada una, con su guard al lado:
la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los
bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el
diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el
encabezado que un Edit se come al insertar una sección.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
'outlineColor',
|
|
|
|
|
'outlineWidth',
|
uix(text-gradient): temable — 67 % → 100 %, y la ficha se equivocaba en las TRES
7 claves en una entrada NUEVA de `recipes/base.ts`. Censo 67 % → **100 %**
(10 knobs, 10 públicos, 0 literales). Global 2.634 → 2.638 públicos.
Tercero de la familia text-* con la forma de text-circular / text-focus: cuatro
públicos «de facto» consumidos con fallback y **ninguno declarado**, más tres
literales. Sin eje `size` ni privados propios, así que declarar no puede matar
una escala.
LO QUE LA PROPUESTA §4 NO ACERTABA — y se resolvió midiendo contra el CATÁLOGO,
no contra el CSS de partida:
- **`weight` era deriva de nombre.** El catálogo tiene 77 claves `font-weight`
contra 4 `weight` a secas — y dos de esas cuatro las escribí ayer en esta
misma familia. Pasa a `font-weight`, y su valor al paso de escala que ya
pintaba: `500` ES `var(--font-weight-medium)`.
- **El `calc(100% - 2px)` del hueco no son dos knobs de eje físico** (la ficha
los marcaba con ⚠ «pártelo en ejes lógicos») sino UNO: los 2 px son el trazo
del marco asomando a cada lado. El knob es `border-width` —con
`var(--border-width)`, el paso `thin`, no un literal `1px` igual a un paso de
la escala— y el hueco DERIVA de él.
- **`blur(4px)` va a la escala de blur.** `blur` es familia MÉTRICA del eje
`scaling` (theming §23): un literal es ciego al zoom global. `--blur-sm` es
`calc(4px * var(--scaling))` — el mismo píxel al 100 y el correcto al 90/110.
Precedente: `dialog`/`drawer.overlay-blur`, también backdrop-filter.
Y el shorthand físico `padding: var(--space-1) var(--space-2)` se parte en ejes
lógicos (`border-padding-block` / `-inline`), que es la normalización firmada el
2026-07-06; el público de facto `border-padding` era un knob con dos ejes dentro.
FUERA DEL CONTRATO POR NATURALEZA: `--text-gradient-duration` y
`--_text-gradient-play-direction` los escribe el wrapper INLINE desde
`animationSpeed` y `yoyo`. Ningún token gana a un estilo inline, así que
declararlos sería acuñar dos nombres que mienten — misma clase que los cuatro
ausentes de `knob`, el `item-gap` de `carousel` y el `preview-z` de `drag-drop`.
INSTRUMENTO — dos puntos ciegos arreglados ANTES de medir (la lección de F2-B
aplicada por adelantado, no después):
- La demo arranca con `showBorder=false`, así que sonda y centinela veían **2
nodos** y NINGUNO de los tres tokens del marco. La sonda gana un mapa
`DEMO_VARIANTS` (chips de variante re-pulsados tras CADA recarga — recarga una
vez por talla) y el centinela una entrada `openWith` que no abre un panel sino
que ENCIENDE UNA VARIANTE: uso nuevo del mismo gancho, sin tocar su lógica.
- La sonda no leía `backdropFilter`, que `KNOB_PROPS` del censo sí cuenta y el
centinela ya leía: un token de fondo podía moverse entre las dos corridas sin
que el diff lo viera. Con la propiedad y la variante: **4 nodos**.
Hover: 0 nodos medidos, y es correcto — las cuatro partes son `<span>` y el
único efecto de hover es `animation-play-state`, que no es knob.
LO QUE ABRE (→ next-features §13): **la escala de `font-size` no tiene
`2xl`/`3xl`/`4xl`** —es `xxs..xxxl`— y tres sitios los referencian: el token
`text-focus.word-size` que subí ayer (`var(--font-size-4xl, 3rem)`, pintando el
fallback: segundo fantasma de la clase `--radius-xs`, y en el contrato) y los
escenarios de las demos de `text-gradient` y `text-blur`, que sin fallback dejan
la declaración INVÁLIDA y enseñan un efecto tipográfico a 14 px. Y
`--uix-font-size-*` no existe: 37 referencias en el harness del sitio, cero
declaraciones. Ninguno se toca aquí: se miden, se anotan y van a su commit.
Demo: pestaña **Tokens** (`TokensPanel`, contrato vivo) y fuera la tabla de
tokens escrita a mano de la pestaña API — era una segunda fuente de verdad y
este renombrado la dejaba obsoleta al instante.
Gates: sonda antes/después **0 diffs** en 720 valores × 7 estados (4 nodos) ·
centinela **7/7 sin una sola excepción** · censo --only 100 % · --names 7/7
conformes · component-audit PASS · eidos-lint 0 invalid · suite eidos con el
único rojo conocido (`skin-media-player`, lint.test.ts) · rtl:check 0 ·
docs:check 0 · `check` con 72 errores globales y **0 en los ficheros tocados**.
Prettier: los seis ficheros editados ya estaban sucios en HEAD (el repo tiene
2.392), no se reformatean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
'filter',
|
|
|
|
|
'backdropFilter'
|
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica
El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes
corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro
del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y
no por la memoria de nadie.
`CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola
inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa
concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia
calendar, mandato de Field, el perímetro del knob para la familia tipográfica,
el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes →
después. El plan gana su §8 con el registro completo y corrige su cabecera, que
seguía diciendo que no había nada construido.
Y se commitea el instrumental, porque sin él el protocolo §7 es una intención:
la sonda de computed antes/después, el diff que es el gate (vacío o se para), el
centinela que pregunta si cada token alcanza de verdad, y la captura del stage.
Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen
dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev
server.
Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se
consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el
`animationend` para montar el panel, y si lo congelas mides un popup que no
existe); un token resuelto no se mueve desde `:root` por diseño; un panel
portalado no ve el ámbito del componente; la demo puede tapar el componente que
mides; y un valor centinela igual al real se lee como «no efecto».
Igual las cuatro trampas que costaron un commit cada una, con su guard al lado:
la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los
bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el
diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el
encabezado que un Edit se come al insertar una sección.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const SIZES = ['xs', 'sm', 'md', 'lg', 'xl'];
|
|
|
|
|
|
uix(text-gradient): temable — 67 % → 100 %, y la ficha se equivocaba en las TRES
7 claves en una entrada NUEVA de `recipes/base.ts`. Censo 67 % → **100 %**
(10 knobs, 10 públicos, 0 literales). Global 2.634 → 2.638 públicos.
Tercero de la familia text-* con la forma de text-circular / text-focus: cuatro
públicos «de facto» consumidos con fallback y **ninguno declarado**, más tres
literales. Sin eje `size` ni privados propios, así que declarar no puede matar
una escala.
LO QUE LA PROPUESTA §4 NO ACERTABA — y se resolvió midiendo contra el CATÁLOGO,
no contra el CSS de partida:
- **`weight` era deriva de nombre.** El catálogo tiene 77 claves `font-weight`
contra 4 `weight` a secas — y dos de esas cuatro las escribí ayer en esta
misma familia. Pasa a `font-weight`, y su valor al paso de escala que ya
pintaba: `500` ES `var(--font-weight-medium)`.
- **El `calc(100% - 2px)` del hueco no son dos knobs de eje físico** (la ficha
los marcaba con ⚠ «pártelo en ejes lógicos») sino UNO: los 2 px son el trazo
del marco asomando a cada lado. El knob es `border-width` —con
`var(--border-width)`, el paso `thin`, no un literal `1px` igual a un paso de
la escala— y el hueco DERIVA de él.
- **`blur(4px)` va a la escala de blur.** `blur` es familia MÉTRICA del eje
`scaling` (theming §23): un literal es ciego al zoom global. `--blur-sm` es
`calc(4px * var(--scaling))` — el mismo píxel al 100 y el correcto al 90/110.
Precedente: `dialog`/`drawer.overlay-blur`, también backdrop-filter.
Y el shorthand físico `padding: var(--space-1) var(--space-2)` se parte en ejes
lógicos (`border-padding-block` / `-inline`), que es la normalización firmada el
2026-07-06; el público de facto `border-padding` era un knob con dos ejes dentro.
FUERA DEL CONTRATO POR NATURALEZA: `--text-gradient-duration` y
`--_text-gradient-play-direction` los escribe el wrapper INLINE desde
`animationSpeed` y `yoyo`. Ningún token gana a un estilo inline, así que
declararlos sería acuñar dos nombres que mienten — misma clase que los cuatro
ausentes de `knob`, el `item-gap` de `carousel` y el `preview-z` de `drag-drop`.
INSTRUMENTO — dos puntos ciegos arreglados ANTES de medir (la lección de F2-B
aplicada por adelantado, no después):
- La demo arranca con `showBorder=false`, así que sonda y centinela veían **2
nodos** y NINGUNO de los tres tokens del marco. La sonda gana un mapa
`DEMO_VARIANTS` (chips de variante re-pulsados tras CADA recarga — recarga una
vez por talla) y el centinela una entrada `openWith` que no abre un panel sino
que ENCIENDE UNA VARIANTE: uso nuevo del mismo gancho, sin tocar su lógica.
- La sonda no leía `backdropFilter`, que `KNOB_PROPS` del censo sí cuenta y el
centinela ya leía: un token de fondo podía moverse entre las dos corridas sin
que el diff lo viera. Con la propiedad y la variante: **4 nodos**.
Hover: 0 nodos medidos, y es correcto — las cuatro partes son `<span>` y el
único efecto de hover es `animation-play-state`, que no es knob.
LO QUE ABRE (→ next-features §13): **la escala de `font-size` no tiene
`2xl`/`3xl`/`4xl`** —es `xxs..xxxl`— y tres sitios los referencian: el token
`text-focus.word-size` que subí ayer (`var(--font-size-4xl, 3rem)`, pintando el
fallback: segundo fantasma de la clase `--radius-xs`, y en el contrato) y los
escenarios de las demos de `text-gradient` y `text-blur`, que sin fallback dejan
la declaración INVÁLIDA y enseñan un efecto tipográfico a 14 px. Y
`--uix-font-size-*` no existe: 37 referencias en el harness del sitio, cero
declaraciones. Ninguno se toca aquí: se miden, se anotan y van a su commit.
Demo: pestaña **Tokens** (`TokensPanel`, contrato vivo) y fuera la tabla de
tokens escrita a mano de la pestaña API — era una segunda fuente de verdad y
este renombrado la dejaba obsoleta al instante.
Gates: sonda antes/después **0 diffs** en 720 valores × 7 estados (4 nodos) ·
centinela **7/7 sin una sola excepción** · censo --only 100 % · --names 7/7
conformes · component-audit PASS · eidos-lint 0 invalid · suite eidos con el
único rojo conocido (`skin-media-player`, lint.test.ts) · rtl:check 0 ·
docs:check 0 · `check` con 72 errores globales y **0 en los ficheros tocados**.
Prettier: los seis ficheros editados ya estaban sucios en HEAD (el repo tiene
2.392), no se reformatean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
/**
|
|
|
|
|
* Demo controls that must be ON before measuring: a part the demo does not
|
|
|
|
|
* mount is a part the diff never compares, and a probe over two nodes passes
|
|
|
|
|
* in false (the F2-B lesson — count the nodes before believing a green gate).
|
|
|
|
|
* text-gradient boots with `showBorder=false`, so its whole frame — three of
|
|
|
|
|
* its seven tokens — is absent from the default stage.
|
|
|
|
|
*/
|
uix(prose): temable — 1 % → 83 %, la costura por ELEMENTO y dos cegueras del instrumento
35 claves nuevas (2 → 37). Censo prose 1 % → **83 %** con `global` a CERO;
global del eje **59 % → 60 %** (1.064 → 1.021 knobs `global` en el catálogo).
Prose estiliza HTML CRUDO (`h1`, `code`, `table`, `kbd`…) a través de
`:where([data-prose] el)`, así que sus «partes» son elementos y la costura va
POR PARTE, como en `result`: `heading-*` (+ `h1-font-weight`, lo único que
distingue al h1 de sus hermanos), `link-*`, `strong-`/`dt-`/`th-font-weight`,
`mark-*`, `blockquote-*`, `code-*`, `pre-*`, `kbd-*`, `media-radius`, `hr-*`,
`table-*`, más `fg`, `fg-muted` y el eje `font-size-{sm,md,lg}`. Los valores
siguen siendo los del canon; lo que faltaba era el knob (PLAN §2-A).
LA ESCALA EDITORIAL NO ENTRA, Y ES DOCTRINA FIRMADA: los `2.25em`, `1.5em`,
`0.9em`, sus interlineados y su tracking son `em`-relativos a propósito —un
`font-size` raíz re-deriva encabezados y márgenes, por eso NO hay una escala
duplicada por talla como en tailwind-typography (su README: «No 5× duplicated
scale»)— y van anotados `/* literal: */`, la válvula de recipe-contract §3. Son
las 21 excepciones firmadas que el censo aprendió a leer esta misma sesión.
Dos literales se cambiaron por su paso de escala, con el mismo valor: el `3px`
del borde-acento del blockquote ES `--border-width-thick`, y los `1px` de kbd,
`hr` y la tabla son `--border-width`.
EL INSTRUMENTO ESTABA CIEGO DE DOS MANERAS, arregladas ANTES de medir:
1. **Sonda y centinela filtran por `data-{c}-*`** y aquí NINGUNA parte lo lleva:
la sonda medía **1 nodo** y el guard daba **27 de 37** tokens por muertos.
Los dos ganan un selector de nodos extra (`EXTRA_NODES` en la sonda,
`extraNodes` en `COMPONENT_OVERRIDES`) — con él, 63 nodos y un gate que
significa algo.
2. **El centinela no leía el color de un borde que no fuera el de arriba.** El
blockquote tiñe su `border-inline-start` y la tabla sus bordes de bloque:
tres tokens vivos leían muertos. Añadidos los cuatro lados lógicos y
`borderBottomColor`.
OCHO ADJUDICACIONES, TODAS MEDIDAS: cinco las tapa el harness de la
documentación —`[data-uix-docs] code|pre` (0,2,0) gana a `:where(…)` (0,0,0),
que es la incidencia ya escrita para `code-block` un piso más arriba— y tres son
elementos que el HTML de ejemplo no usa (`<mark>`, `<dl>`), montados DENTRO del
contenedor real: un `<mark>` a pelo es exactamente lo que esta receta estiliza,
así que el nodo es fiel (la lección de `drag-drop` cumplida).
Gates: sonda antes/después **0 diffs en 11.910 valores** (63 nodos × 7 estados) ·
centinela 29/37 con las ocho adjudicadas · censo --only 83 % · `--names` 37/37
conformes · component-audit PASS · eidos-lint 0 invalid · suite eidos con el
único rojo conocido · rtl:check 0 · docs:check 0 · `check` con 72 errores
globales y 0 en los ficheros tocados. Componentes del ledger re-verificados tras
tocar el guard (text-gradient, chart, result, tree-view, textarea): sin
regresiones ni STALE.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
/**
|
|
|
|
|
* Components whose PARTS are not tagged `data-{c}-*`: prose styles bare HTML
|
|
|
|
|
* (h1, code, table…) through `:where([data-prose] el)`, so the attribute filter
|
|
|
|
|
* below sees exactly ONE node and the gate proves nothing. The selector's
|
|
|
|
|
* matches join the measured set.
|
|
|
|
|
*/
|
|
|
|
|
const EXTRA_NODES: Record<string, string> = {
|
uix(onion-menu): temable — 29 % → 84 %, y una regla que el navegador DESCARTA
7 claves nuevas (12 → 19) y 5 literales anotados. Lo que queda fuera son TRES
filas y ninguna es deuda de tokenización: una es canal de valor y dos viven en
una regla inválida.
Acuñado el color del ANILLO DEL SECTOR —uno para los dos estados, porque el
propio comentario del CSS dice que el hover usa «el mismo color que el anillo de
selección, más claro» y lo que los separa es la opacidad, que ya tenía token—,
la tipografía de la etiqueta, el radio y la tinta del disparador, y las dos
piezas del glifo `+`→`×`. Dos limpiezas de paso: un
`var(--style-label-font-family, var(--style-label-font-family))` —respaldo de sí
mismo, el defecto que ya se cazó en context-menu— y dos `var(--radius-full, …)`
con un respaldo que no puede ocurrir.
Cinco literales pasan a excepción POR IDENTIDAD: dos `opacity: 1` que DESHACEN
un atenuado (sin atenuar es 1 por definición) y la caja del icono.
⚠ `.onion-menu-icon :global(svg)` NO EXISTE. `:global()` es un envoltorio de
SVELTE y esto es un CSS plano: el selector es inválido y el navegador descarta
la regla entera. Medido: el glifo computa 14px —el tamaño propio del Icon— y el
`70 %` que el fichero cree pintar no pinta. Acuñé un `icon-glyph-size`, el
centinela lo delató y lo RETIRÉ; los literales se quedan con el defecto escrito
encima, porque arreglar el selector empieza a pintar el 70 % y MUEVE PÍXEL.
Mismo bug en `timeline.css:331`; `image-picker.css` ya documentaba la trampa
—la lección estaba escrita y se repitió—. → §13.
Canal de valor: el fondo del disparador lo escribe el componente INLINE desde el
motor de color, como los rellenos de cada sector. No es superficie de tema.
TRES CEGUERAS DEL INSTRUMENTO, dos nuevas y genéricas:
1. EL GUARD CERRABA LA SUPERFICIE. Este menú nace ABIERTO y el clic de «abrir»
lo cerraba: 12 de 20 tokens leían muertos. Ahora, si hay `openMarker` y ya
casa, no se pulsa nada (8 → 14 de golpe).
2. Sus tripas cuelgan de CLASES, no de `data-onion-*`: sonda y guard las miden
con `extraNodes` — 37 nodos en reposo contra los 5 con atributo.
3. Congelar las transiciones NO BASTA cuando el nodo está ANIMADO:
`rim-glow-opacity` leía 0,482956 —el valor animado— y parecía muerto; con la
animación congelada computa 0,45 y sigue al token. → §13.
Los cuatro tokens del rim son del look `luminous`, que la demo del componente no
renderiza (es una prop de Svelte que emite OTRO marcado, así que no hay atributo
que forzar): medidos en `/active/docs/agnt`, que sí lo usa.
Gates: sonda 0 diffs en 7.328 valores × 8 estados (37 nodos) · centinela 14/19
con las cinco adjudicadas · censo 84 % · `component:audit` PASS · suite eidos
con el rojo conocido ajeno · `rtl:check` 0 · `docs:check` 0 · `check` sin
errores propios · capturas de reposo, sector en hover y disparador.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
prose: '[data-prose] *',
|
|
|
|
|
// Its SVG innards hook on CLASSES, not on `data-onion-*`: without this the
|
|
|
|
|
// filter sees the five attribute nodes and misses every sector, label, icon
|
|
|
|
|
// and glyph — the whole surface the recipe paints.
|
|
|
|
|
'onion-menu': '.onion-menu-sector, .onion-menu-label, .onion-menu-icon, .onion-menu-icon svg, .onion-menu-trigger, .onion-menu-trigger-glyph'
|
uix(prose): temable — 1 % → 83 %, la costura por ELEMENTO y dos cegueras del instrumento
35 claves nuevas (2 → 37). Censo prose 1 % → **83 %** con `global` a CERO;
global del eje **59 % → 60 %** (1.064 → 1.021 knobs `global` en el catálogo).
Prose estiliza HTML CRUDO (`h1`, `code`, `table`, `kbd`…) a través de
`:where([data-prose] el)`, así que sus «partes» son elementos y la costura va
POR PARTE, como en `result`: `heading-*` (+ `h1-font-weight`, lo único que
distingue al h1 de sus hermanos), `link-*`, `strong-`/`dt-`/`th-font-weight`,
`mark-*`, `blockquote-*`, `code-*`, `pre-*`, `kbd-*`, `media-radius`, `hr-*`,
`table-*`, más `fg`, `fg-muted` y el eje `font-size-{sm,md,lg}`. Los valores
siguen siendo los del canon; lo que faltaba era el knob (PLAN §2-A).
LA ESCALA EDITORIAL NO ENTRA, Y ES DOCTRINA FIRMADA: los `2.25em`, `1.5em`,
`0.9em`, sus interlineados y su tracking son `em`-relativos a propósito —un
`font-size` raíz re-deriva encabezados y márgenes, por eso NO hay una escala
duplicada por talla como en tailwind-typography (su README: «No 5× duplicated
scale»)— y van anotados `/* literal: */`, la válvula de recipe-contract §3. Son
las 21 excepciones firmadas que el censo aprendió a leer esta misma sesión.
Dos literales se cambiaron por su paso de escala, con el mismo valor: el `3px`
del borde-acento del blockquote ES `--border-width-thick`, y los `1px` de kbd,
`hr` y la tabla son `--border-width`.
EL INSTRUMENTO ESTABA CIEGO DE DOS MANERAS, arregladas ANTES de medir:
1. **Sonda y centinela filtran por `data-{c}-*`** y aquí NINGUNA parte lo lleva:
la sonda medía **1 nodo** y el guard daba **27 de 37** tokens por muertos.
Los dos ganan un selector de nodos extra (`EXTRA_NODES` en la sonda,
`extraNodes` en `COMPONENT_OVERRIDES`) — con él, 63 nodos y un gate que
significa algo.
2. **El centinela no leía el color de un borde que no fuera el de arriba.** El
blockquote tiñe su `border-inline-start` y la tabla sus bordes de bloque:
tres tokens vivos leían muertos. Añadidos los cuatro lados lógicos y
`borderBottomColor`.
OCHO ADJUDICACIONES, TODAS MEDIDAS: cinco las tapa el harness de la
documentación —`[data-uix-docs] code|pre` (0,2,0) gana a `:where(…)` (0,0,0),
que es la incidencia ya escrita para `code-block` un piso más arriba— y tres son
elementos que el HTML de ejemplo no usa (`<mark>`, `<dl>`), montados DENTRO del
contenedor real: un `<mark>` a pelo es exactamente lo que esta receta estiliza,
así que el nodo es fiel (la lección de `drag-drop` cumplida).
Gates: sonda antes/después **0 diffs en 11.910 valores** (63 nodos × 7 estados) ·
centinela 29/37 con las ocho adjudicadas · censo --only 83 % · `--names` 37/37
conformes · component-audit PASS · eidos-lint 0 invalid · suite eidos con el
único rojo conocido · rtl:check 0 · docs:check 0 · `check` con 72 errores
globales y 0 en los ficheros tocados. Componentes del ledger re-verificados tras
tocar el guard (text-gradient, chart, result, tree-view, textarea): sin
regresiones ni STALE.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
};
|
|
|
|
|
|
uix(menubar): temable — 15 % → 88 %, y el plano `overlay` GANA a la receta
30 claves nuevas (5 → 35): la barra (superficie, marco, radio —uno solo, la
receta lo desacopla de la talla a propósito y el motor de forma deriva el del
disparador de él— y las tres escalas `gap`/`padding`/`height`), el disparador
(tinta, tipografía, escala de `font-size` y de `padding-inline`, el par de la
entrada ABIERTA y la tinta deshabilitada) y el Panel `role=dialog` (tinta +
escala). Global 62 % → 63 %.
Los pasos leen el BUNDLE (`--size-{k}-control-height` / `--size-{k}-font-size`),
no el primitivo crudo que el CSS venía consumiendo; y el
`calc(var(--space-1) * 0.5)` del paso `sm` es `var(--space-0-5)` exacto.
EL HUECO DEL PANEL DEJA DE REPETIR EL VALOR DEL PADDING. Las tres reglas de
`--floating-gap` compensan el sangrado del disparador dentro de la barra con lo
que era una COPIA del padding; ahora leen `--menubar-padding-{k}`, su fuente —
si un tema ensancha el padding, el panel deja de quedar a ras de la barra. No
pueden leer el privado `--_menubar-padding`: esas superficies viajan POR PORTAL
y un privado declarado en la raíz no cruza esa frontera.
⚠ EL PLANO `overlay` GANA A LA RECETA — cuarta prueba de §12.9 y la primera con
MECANISMO. Acuñé `panel-font-family` y `panel-line-height` sobre declaraciones
que la receta YA tenía, y el centinela las dio muertas. Medido:
`[data-depth='overlay']` declara las dos con la MISMA especificidad (0,1,0) y
más tarde en la cascada, así que gana; del mismo bloque, `color` y `font-size`
—que el plano no declara— sí alcanzan. Retiradas del contrato y devueltas a su
fuente literal, con la razón escrita en el CSS. Mientras §12.9 no se firme, un
token de tipografía en CUALQUIER superficie `overlay` miente, y son veinte.
EL HOVER NO SE ACUÑA: ES DEL EJE navigation-menu. `background:
var(--color-surface-overlay)` sobre el disparador mata la capa del sistema
—medido: `background-image` computa `none` en hover con `--state-hover` vivo—,
por las dos razones que `navigation-menu.css` ya documenta al retirar esta
MISMA regla: el shorthand resetea `background-image`, y la regla del arquetipo
va en `:where()`, especificidad CERO. Acuñar un `hover-trigger-bg` habría
fosilizado la invención.
Instrumento: la barra suelta DOS superficies y el Panel —el que lleva sus
knobs— cuelga de la entrada «Format», no de la primera; sonda (`OPEN_TRIGGER`)
y guard (`openWith`) aprenden a abrir la que toca. Y el guard necesitó
`openMarker`: su comprobación de «ya está abierto» busca `[data-{c}-content]`,
que aquí no existe, así que volvía a pulsar el disparador antes de cada token y
CERRABA el panel — media corrida contra una superficie cerrada.
Gates: sonda 0 diffs en 1.470 valores × 8 estados · centinela 34/35 (la
deshabilitada, adjudicada) · censo 88 % · `--names` 0 desviadas ·
`component:audit` PASS · `rtl:check` 0 · `docs:check` 0 · suite eidos con el
rojo conocido ajeno (`skin-media-player`) · `check` sin errores propios ·
capturas de barra, tallas sm/lg, menú abierto y panel.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
/**
|
|
|
|
|
* The trigger the OPEN pass must click. The default is the first trigger in the
|
|
|
|
|
* document, which is wrong whenever the surface worth measuring hangs off
|
|
|
|
|
* ANOTHER one: menubar drops a role=menu Content from its first entry and its
|
|
|
|
|
* own role=dialog Panel — the part that carries its public knobs — only from
|
|
|
|
|
* the "Format" entry.
|
|
|
|
|
*/
|
|
|
|
|
const OPEN_TRIGGER: Record<string, string> = {
|
|
|
|
|
menubar: "[data-menubar-trigger][data-menubar-value='format']"
|
|
|
|
|
};
|
|
|
|
|
|
uix(text-gradient): temable — 67 % → 100 %, y la ficha se equivocaba en las TRES
7 claves en una entrada NUEVA de `recipes/base.ts`. Censo 67 % → **100 %**
(10 knobs, 10 públicos, 0 literales). Global 2.634 → 2.638 públicos.
Tercero de la familia text-* con la forma de text-circular / text-focus: cuatro
públicos «de facto» consumidos con fallback y **ninguno declarado**, más tres
literales. Sin eje `size` ni privados propios, así que declarar no puede matar
una escala.
LO QUE LA PROPUESTA §4 NO ACERTABA — y se resolvió midiendo contra el CATÁLOGO,
no contra el CSS de partida:
- **`weight` era deriva de nombre.** El catálogo tiene 77 claves `font-weight`
contra 4 `weight` a secas — y dos de esas cuatro las escribí ayer en esta
misma familia. Pasa a `font-weight`, y su valor al paso de escala que ya
pintaba: `500` ES `var(--font-weight-medium)`.
- **El `calc(100% - 2px)` del hueco no son dos knobs de eje físico** (la ficha
los marcaba con ⚠ «pártelo en ejes lógicos») sino UNO: los 2 px son el trazo
del marco asomando a cada lado. El knob es `border-width` —con
`var(--border-width)`, el paso `thin`, no un literal `1px` igual a un paso de
la escala— y el hueco DERIVA de él.
- **`blur(4px)` va a la escala de blur.** `blur` es familia MÉTRICA del eje
`scaling` (theming §23): un literal es ciego al zoom global. `--blur-sm` es
`calc(4px * var(--scaling))` — el mismo píxel al 100 y el correcto al 90/110.
Precedente: `dialog`/`drawer.overlay-blur`, también backdrop-filter.
Y el shorthand físico `padding: var(--space-1) var(--space-2)` se parte en ejes
lógicos (`border-padding-block` / `-inline`), que es la normalización firmada el
2026-07-06; el público de facto `border-padding` era un knob con dos ejes dentro.
FUERA DEL CONTRATO POR NATURALEZA: `--text-gradient-duration` y
`--_text-gradient-play-direction` los escribe el wrapper INLINE desde
`animationSpeed` y `yoyo`. Ningún token gana a un estilo inline, así que
declararlos sería acuñar dos nombres que mienten — misma clase que los cuatro
ausentes de `knob`, el `item-gap` de `carousel` y el `preview-z` de `drag-drop`.
INSTRUMENTO — dos puntos ciegos arreglados ANTES de medir (la lección de F2-B
aplicada por adelantado, no después):
- La demo arranca con `showBorder=false`, así que sonda y centinela veían **2
nodos** y NINGUNO de los tres tokens del marco. La sonda gana un mapa
`DEMO_VARIANTS` (chips de variante re-pulsados tras CADA recarga — recarga una
vez por talla) y el centinela una entrada `openWith` que no abre un panel sino
que ENCIENDE UNA VARIANTE: uso nuevo del mismo gancho, sin tocar su lógica.
- La sonda no leía `backdropFilter`, que `KNOB_PROPS` del censo sí cuenta y el
centinela ya leía: un token de fondo podía moverse entre las dos corridas sin
que el diff lo viera. Con la propiedad y la variante: **4 nodos**.
Hover: 0 nodos medidos, y es correcto — las cuatro partes son `<span>` y el
único efecto de hover es `animation-play-state`, que no es knob.
LO QUE ABRE (→ next-features §13): **la escala de `font-size` no tiene
`2xl`/`3xl`/`4xl`** —es `xxs..xxxl`— y tres sitios los referencian: el token
`text-focus.word-size` que subí ayer (`var(--font-size-4xl, 3rem)`, pintando el
fallback: segundo fantasma de la clase `--radius-xs`, y en el contrato) y los
escenarios de las demos de `text-gradient` y `text-blur`, que sin fallback dejan
la declaración INVÁLIDA y enseñan un efecto tipográfico a 14 px. Y
`--uix-font-size-*` no existe: 37 referencias en el harness del sitio, cero
declaraciones. Ninguno se toca aquí: se miden, se anotan y van a su commit.
Demo: pestaña **Tokens** (`TokensPanel`, contrato vivo) y fuera la tabla de
tokens escrita a mano de la pestaña API — era una segunda fuente de verdad y
este renombrado la dejaba obsoleta al instante.
Gates: sonda antes/después **0 diffs** en 720 valores × 7 estados (4 nodos) ·
centinela **7/7 sin una sola excepción** · censo --only 100 % · --names 7/7
conformes · component-audit PASS · eidos-lint 0 invalid · suite eidos con el
único rojo conocido (`skin-media-player`, lint.test.ts) · rtl:check 0 ·
docs:check 0 · `check` con 72 errores globales y **0 en los ficheros tocados**.
Prettier: los seis ficheros editados ya estaban sucios en HEAD (el repo tiene
2.392), no se reformatean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
const DEMO_VARIANTS: Record<string, string[]> = {
|
|
|
|
|
'text-gradient': [
|
|
|
|
|
'[data-uix-control]:has([data-uix-control-label]:text-is("showBorder")) [data-uix-chip]:text-is("true")'
|
|
|
|
|
]
|
|
|
|
|
};
|
|
|
|
|
|
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica
El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes
corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro
del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y
no por la memoria de nadie.
`CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola
inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa
concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia
calendar, mandato de Field, el perímetro del knob para la familia tipográfica,
el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes →
después. El plan gana su §8 con el registro completo y corrige su cabecera, que
seguía diciendo que no había nada construido.
Y se commitea el instrumental, porque sin él el protocolo §7 es una intención:
la sonda de computed antes/después, el diff que es el gate (vacío o se para), el
centinela que pregunta si cada token alcanza de verdad, y la captura del stage.
Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen
dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev
server.
Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se
consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el
`animationend` para montar el panel, y si lo congelas mides un popup que no
existe); un token resuelto no se mueve desde `:root` por diseño; un panel
portalado no ve el ámbito del componente; la demo puede tapar el componente que
mides; y un valor centinela igual al real se lee como «no efecto».
Igual las cuatro trampas que costaron un commit cada una, con su guard al lado:
la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los
bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el
diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el
encabezado que un Edit se come al insertar una sección.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
async function main() {
|
|
|
|
|
const [component, out, urlArg] = process.argv.slice(2);
|
|
|
|
|
if (!component || !out) throw new Error('usage: <component> <out.json> [url]');
|
|
|
|
|
const url = urlArg ?? `http://localhost:5173/uix/components/${component}`;
|
|
|
|
|
|
|
|
|
|
const browser = await chromium.launch();
|
|
|
|
|
const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } });
|
uix(theming): revisión adversarial de F2-A — 8 hallazgos de 34, y nace R-5.4
Pase escéptico del bloque (§7.7, molde Sidebar) con medición propia: worktree
con SOLO el CSS revertido a la base del bloque (DOM de demo constante — prueba
más dura que la del autor), sonda x2 por componente para fijar el suelo de
ruido. 34 hipótesis · 26 refutadas · 8 reales.
Lo que NO se pudo romper: el «default idéntico» aguanta en los ocho (0 diffs;
command 8.091 · table 13.050 · media-player 3.277 · gradient-picker 609 ·
listbox 2.813 · carousel 2.842; los 3 de tree-grid y los 5 de feed salen
idénticos corriendo DOS veces el mismo código → instrumento); 0 tokens
huérfanos de 336; el bundle ES 1:1 con los primitivos; el «verificado a mano»
de gradient-picker era verdad (15/16 alcanzan al montar); sin doble animación;
eidos-lint 0 invalid.
Los 4 de código, arreglados con diff de computed VACÍO (re-medido: command
8.091 y carousel 2.842 valores, 0 diffs):
1. command: el re-point de `--command-radius` dentro del Dialog era una
declaración muerta con comentario falso — la misma regla pone el radio a 0
y el input lee `--command-input-radius`, nunca este token (medido:
`--dialog-content-radius: 9999px` no movía NADA; `--command-radius: 77px`
movía la paleta inline y no la del dialog). Retirado; comentarios y README
reescritos con lo medido.
2. carousel: la regla vertical del indicador activo llevaba el `2.25` a pelo
mientras la horizontal leía `active-indicator-scale` — el token alcanzaba
media superficie (scale:6 → horizontal 18→48px, vertical 18→18px). Ahora
lo leen las dos.
3. carousel: el trío `item-gap*` no podía ganar NUNCA — soma estampa `gap`
INLINE desde la prop (lo necesita para el flex-basis; reglas casadas por
CDP: la de la receta y encima INLINE gap:0rem). Retirados los 3 tokens y
las 2 declaraciones muertas (la base y el gap:0 de ghost): 0 diffs, que es
la prueba. Canal de valor de soma, como --gp-current-gradient. Censo
carousel 35 knobs · 23 públicos · 77 % · 27 claves; global 43 % intacto
(5.190 · 2.102).
4. table: `striped-row-bg` no alcanza NADA en la demo publicada —
`:nth-of-type(even)` cuenta los <tr> de RowDetail y las filas de datos
quedan impares (medido tr a tr: diez inmunes). Arreglarlo mueve píxel →
PENDIENTE DE FIRMA (candidato `:nth-child(even of [data-table-row])`);
adjudicado en el ledger mientras tanto.
El instrumento cargaba 22 falsos negativos de 26 «no effect», con tres causas
medidas, y la cuarta era de la sonda:
- el paso de abrir hacía CLIC en el input, y un clic de ratón sobre un input
de texto SÍ casa `:focus-visible` — la regla de foco pintaba el borde y el
token de reposo leía muerto (era el «falso negativo sin causa» de §13);
- el override se escribía solo en `[data-{c}]`, y las partes que cuelgan de
`{c}-root` no lo recibían;
- `::before`/`::after` eran invisibles (el aro de buffering, el spinner);
- la no-determinación de feed no era una animación sin localizar: era la demo
aún CARGANDO — el auto load-more mantiene [data-busy] ~3 s y la firma
`commit-settle` anima box-shadow sobre el nodo medido. Con la espera de
asentado: 0 diffs y 0 nodos ausentes sobre 6.264 valores, dos corridas.
Con eso NACE EL GUARD R-5.4 (la deuda que §13 pedía desde gradient-picker, y
que carousel convirtió en patrón — un estilo inline de otra capa es invisible
para todo análisis estático): `npm run theming:sentinel -- <c> <url>`
(scripts/theming-sentinel.ts, promovido de `__`; blur tras abrir, override en
todos los nodos del componente, pseudos, pase de hover, settle) + ledger
scripts/theming-sentinel-exceptions.ts con UNA razón medida por token — muerto
sin adjudicar = exit 1, excepción STALE = aviso. Los ocho componentes en
verde: command 50/57 · table 41/45 · media-player 46/61 · tree-grid 45/47 ·
gradient-picker 9/25 · listbox 20/21 · carousel 26/27 · feed 43/50, con 46
excepciones adjudicadas. El guard ya me corrigió a mí: cinco entradas mías
salieron STALE (los control-height-* de media-player SÍ alcanzan) y las borró;
y dos muertos nuevos los produjeron los propios arreglos (el blur apaga
focus-input-border; el settle desmonta el spinner de feed) — adjudicados con
su porqué. Doctrina en recipe-contract §4.
También: tres cifras de especificidad de next-features §12 recontadas (la
banda de tree-grid es (0,8,0); la tinta seleccionada del arquetipo es (0,4,0);
y el `item` de table vive en la fila Y EN LA CELDA — elementsFromPoint pone el
velo en la celda), el comentario de listbox que mandaba pisar el privado
`--_listbox-max-height` apunta ahora al público, y quedan registrados en §13
los dos `header-z` con literal '2' y el spinner de media-player a 720ms
mientras el gemelo de feed lleva tokens.
Guards: censo global 43 % intacto · audit --only command+carousel PASS ·
eidos-lint 0 invalid (command 16/12, carousel 29/9, listbox 12/7) · vitest
eidos 434/435 (el rojo es el conocido skin-media-player) · rtl 0/180 · docs
0/813 · check 0 errores en tocados (72 globales de otras sesiones) · prettier
limpio en lo nuevo (los desvíos de command.css/carousel.css ya estaban en
HEAD y no se tocan).
Pendiente de firma (CONTINUE §«PENDIENTE DE FIRMA»): el striped inerte, el
arquetipo en fila+celda, y que soma lea el gap de un token.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
|
|
|
|
// A demo that is still LOADING measures a different instant on every run:
|
|
|
|
|
// feed's auto load-more keeps [data-busy] for ~3s and its commit-settle
|
|
|
|
|
// firma animates box-shadow on the very node under measurement (5 phantom
|
|
|
|
|
// diffs between two runs of the same code, 2026-08-21). Wait for the busy
|
|
|
|
|
// window to close before every snapshot.
|
|
|
|
|
const settle = () =>
|
|
|
|
|
page
|
|
|
|
|
.waitForFunction(() => !document.querySelector('[data-busy]'), null, { timeout: 15000 })
|
|
|
|
|
.catch(() => {});
|
|
|
|
|
|
uix(text-gradient): temable — 67 % → 100 %, y la ficha se equivocaba en las TRES
7 claves en una entrada NUEVA de `recipes/base.ts`. Censo 67 % → **100 %**
(10 knobs, 10 públicos, 0 literales). Global 2.634 → 2.638 públicos.
Tercero de la familia text-* con la forma de text-circular / text-focus: cuatro
públicos «de facto» consumidos con fallback y **ninguno declarado**, más tres
literales. Sin eje `size` ni privados propios, así que declarar no puede matar
una escala.
LO QUE LA PROPUESTA §4 NO ACERTABA — y se resolvió midiendo contra el CATÁLOGO,
no contra el CSS de partida:
- **`weight` era deriva de nombre.** El catálogo tiene 77 claves `font-weight`
contra 4 `weight` a secas — y dos de esas cuatro las escribí ayer en esta
misma familia. Pasa a `font-weight`, y su valor al paso de escala que ya
pintaba: `500` ES `var(--font-weight-medium)`.
- **El `calc(100% - 2px)` del hueco no son dos knobs de eje físico** (la ficha
los marcaba con ⚠ «pártelo en ejes lógicos») sino UNO: los 2 px son el trazo
del marco asomando a cada lado. El knob es `border-width` —con
`var(--border-width)`, el paso `thin`, no un literal `1px` igual a un paso de
la escala— y el hueco DERIVA de él.
- **`blur(4px)` va a la escala de blur.** `blur` es familia MÉTRICA del eje
`scaling` (theming §23): un literal es ciego al zoom global. `--blur-sm` es
`calc(4px * var(--scaling))` — el mismo píxel al 100 y el correcto al 90/110.
Precedente: `dialog`/`drawer.overlay-blur`, también backdrop-filter.
Y el shorthand físico `padding: var(--space-1) var(--space-2)` se parte en ejes
lógicos (`border-padding-block` / `-inline`), que es la normalización firmada el
2026-07-06; el público de facto `border-padding` era un knob con dos ejes dentro.
FUERA DEL CONTRATO POR NATURALEZA: `--text-gradient-duration` y
`--_text-gradient-play-direction` los escribe el wrapper INLINE desde
`animationSpeed` y `yoyo`. Ningún token gana a un estilo inline, así que
declararlos sería acuñar dos nombres que mienten — misma clase que los cuatro
ausentes de `knob`, el `item-gap` de `carousel` y el `preview-z` de `drag-drop`.
INSTRUMENTO — dos puntos ciegos arreglados ANTES de medir (la lección de F2-B
aplicada por adelantado, no después):
- La demo arranca con `showBorder=false`, así que sonda y centinela veían **2
nodos** y NINGUNO de los tres tokens del marco. La sonda gana un mapa
`DEMO_VARIANTS` (chips de variante re-pulsados tras CADA recarga — recarga una
vez por talla) y el centinela una entrada `openWith` que no abre un panel sino
que ENCIENDE UNA VARIANTE: uso nuevo del mismo gancho, sin tocar su lógica.
- La sonda no leía `backdropFilter`, que `KNOB_PROPS` del censo sí cuenta y el
centinela ya leía: un token de fondo podía moverse entre las dos corridas sin
que el diff lo viera. Con la propiedad y la variante: **4 nodos**.
Hover: 0 nodos medidos, y es correcto — las cuatro partes son `<span>` y el
único efecto de hover es `animation-play-state`, que no es knob.
LO QUE ABRE (→ next-features §13): **la escala de `font-size` no tiene
`2xl`/`3xl`/`4xl`** —es `xxs..xxxl`— y tres sitios los referencian: el token
`text-focus.word-size` que subí ayer (`var(--font-size-4xl, 3rem)`, pintando el
fallback: segundo fantasma de la clase `--radius-xs`, y en el contrato) y los
escenarios de las demos de `text-gradient` y `text-blur`, que sin fallback dejan
la declaración INVÁLIDA y enseñan un efecto tipográfico a 14 px. Y
`--uix-font-size-*` no existe: 37 referencias en el harness del sitio, cero
declaraciones. Ninguno se toca aquí: se miden, se anotan y van a su commit.
Demo: pestaña **Tokens** (`TokensPanel`, contrato vivo) y fuera la tabla de
tokens escrita a mano de la pestaña API — era una segunda fuente de verdad y
este renombrado la dejaba obsoleta al instante.
Gates: sonda antes/después **0 diffs** en 720 valores × 7 estados (4 nodos) ·
centinela **7/7 sin una sola excepción** · censo --only 100 % · --names 7/7
conformes · component-audit PASS · eidos-lint 0 invalid · suite eidos con el
único rojo conocido (`skin-media-player`, lint.test.ts) · rtl:check 0 ·
docs:check 0 · `check` con 72 errores globales y **0 en los ficheros tocados**.
Prettier: los seis ficheros editados ya estaban sucios en HEAD (el repo tiene
2.392), no se reformatean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
// Every load resets the demo's controls, so the variant chips are re-clicked
|
|
|
|
|
// after each one — the probe reloads once per size, plus open and hover.
|
|
|
|
|
const variants = async () => {
|
|
|
|
|
for (const sel of DEMO_VARIANTS[component] ?? []) {
|
|
|
|
|
const el = page.locator(sel).first();
|
|
|
|
|
if (!(await el.count())) throw new Error(`variant selector matches nothing: ${sel}`);
|
|
|
|
|
await el.click({ timeout: 2000 });
|
|
|
|
|
await page.waitForTimeout(200);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
const prepare = async () => {
|
|
|
|
|
await settle();
|
|
|
|
|
await variants();
|
|
|
|
|
};
|
|
|
|
|
|
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica
El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes
corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro
del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y
no por la memoria de nadie.
`CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola
inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa
concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia
calendar, mandato de Field, el perímetro del knob para la familia tipográfica,
el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes →
después. El plan gana su §8 con el registro completo y corrige su cabecera, que
seguía diciendo que no había nada construido.
Y se commitea el instrumental, porque sin él el protocolo §7 es una intención:
la sonda de computed antes/después, el diff que es el gate (vacío o se para), el
centinela que pregunta si cada token alcanza de verdad, y la captura del stage.
Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen
dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev
server.
Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se
consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el
`animationend` para montar el panel, y si lo congelas mides un popup que no
existe); un token resuelto no se mueve desde `:root` por diseño; un panel
portalado no ve el ámbito del componente; la demo puede tapar el componente que
mides; y un valor centinela igual al real se lee como «no efecto».
Igual las cuatro trampas que costaron un commit cada una, con su guard al lado:
la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los
bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el
diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el
encabezado que un Edit se come al insertar una sección.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
await page.goto(url, { waitUntil: 'networkidle' });
|
|
|
|
|
await page.waitForTimeout(600);
|
uix(text-gradient): temable — 67 % → 100 %, y la ficha se equivocaba en las TRES
7 claves en una entrada NUEVA de `recipes/base.ts`. Censo 67 % → **100 %**
(10 knobs, 10 públicos, 0 literales). Global 2.634 → 2.638 públicos.
Tercero de la familia text-* con la forma de text-circular / text-focus: cuatro
públicos «de facto» consumidos con fallback y **ninguno declarado**, más tres
literales. Sin eje `size` ni privados propios, así que declarar no puede matar
una escala.
LO QUE LA PROPUESTA §4 NO ACERTABA — y se resolvió midiendo contra el CATÁLOGO,
no contra el CSS de partida:
- **`weight` era deriva de nombre.** El catálogo tiene 77 claves `font-weight`
contra 4 `weight` a secas — y dos de esas cuatro las escribí ayer en esta
misma familia. Pasa a `font-weight`, y su valor al paso de escala que ya
pintaba: `500` ES `var(--font-weight-medium)`.
- **El `calc(100% - 2px)` del hueco no son dos knobs de eje físico** (la ficha
los marcaba con ⚠ «pártelo en ejes lógicos») sino UNO: los 2 px son el trazo
del marco asomando a cada lado. El knob es `border-width` —con
`var(--border-width)`, el paso `thin`, no un literal `1px` igual a un paso de
la escala— y el hueco DERIVA de él.
- **`blur(4px)` va a la escala de blur.** `blur` es familia MÉTRICA del eje
`scaling` (theming §23): un literal es ciego al zoom global. `--blur-sm` es
`calc(4px * var(--scaling))` — el mismo píxel al 100 y el correcto al 90/110.
Precedente: `dialog`/`drawer.overlay-blur`, también backdrop-filter.
Y el shorthand físico `padding: var(--space-1) var(--space-2)` se parte en ejes
lógicos (`border-padding-block` / `-inline`), que es la normalización firmada el
2026-07-06; el público de facto `border-padding` era un knob con dos ejes dentro.
FUERA DEL CONTRATO POR NATURALEZA: `--text-gradient-duration` y
`--_text-gradient-play-direction` los escribe el wrapper INLINE desde
`animationSpeed` y `yoyo`. Ningún token gana a un estilo inline, así que
declararlos sería acuñar dos nombres que mienten — misma clase que los cuatro
ausentes de `knob`, el `item-gap` de `carousel` y el `preview-z` de `drag-drop`.
INSTRUMENTO — dos puntos ciegos arreglados ANTES de medir (la lección de F2-B
aplicada por adelantado, no después):
- La demo arranca con `showBorder=false`, así que sonda y centinela veían **2
nodos** y NINGUNO de los tres tokens del marco. La sonda gana un mapa
`DEMO_VARIANTS` (chips de variante re-pulsados tras CADA recarga — recarga una
vez por talla) y el centinela una entrada `openWith` que no abre un panel sino
que ENCIENDE UNA VARIANTE: uso nuevo del mismo gancho, sin tocar su lógica.
- La sonda no leía `backdropFilter`, que `KNOB_PROPS` del censo sí cuenta y el
centinela ya leía: un token de fondo podía moverse entre las dos corridas sin
que el diff lo viera. Con la propiedad y la variante: **4 nodos**.
Hover: 0 nodos medidos, y es correcto — las cuatro partes son `<span>` y el
único efecto de hover es `animation-play-state`, que no es knob.
LO QUE ABRE (→ next-features §13): **la escala de `font-size` no tiene
`2xl`/`3xl`/`4xl`** —es `xxs..xxxl`— y tres sitios los referencian: el token
`text-focus.word-size` que subí ayer (`var(--font-size-4xl, 3rem)`, pintando el
fallback: segundo fantasma de la clase `--radius-xs`, y en el contrato) y los
escenarios de las demos de `text-gradient` y `text-blur`, que sin fallback dejan
la declaración INVÁLIDA y enseñan un efecto tipográfico a 14 px. Y
`--uix-font-size-*` no existe: 37 referencias en el harness del sitio, cero
declaraciones. Ninguno se toca aquí: se miden, se anotan y van a su commit.
Demo: pestaña **Tokens** (`TokensPanel`, contrato vivo) y fuera la tabla de
tokens escrita a mano de la pestaña API — era una segunda fuente de verdad y
este renombrado la dejaba obsoleta al instante.
Gates: sonda antes/después **0 diffs** en 720 valores × 7 estados (4 nodos) ·
centinela **7/7 sin una sola excepción** · censo --only 100 % · --names 7/7
conformes · component-audit PASS · eidos-lint 0 invalid · suite eidos con el
único rojo conocido (`skin-media-player`, lint.test.ts) · rtl:check 0 ·
docs:check 0 · `check` con 72 errores globales y **0 en los ficheros tocados**.
Prettier: los seis ficheros editados ya estaban sucios en HEAD (el repo tiene
2.392), no se reformatean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
await prepare();
|
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica
El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes
corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro
del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y
no por la memoria de nadie.
`CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola
inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa
concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia
calendar, mandato de Field, el perímetro del knob para la familia tipográfica,
el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes →
después. El plan gana su §8 con el registro completo y corrige su cabecera, que
seguía diciendo que no había nada construido.
Y se commitea el instrumental, porque sin él el protocolo §7 es una intención:
la sonda de computed antes/después, el diff que es el gate (vacío o se para), el
centinela que pregunta si cada token alcanza de verdad, y la captura del stage.
Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen
dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev
server.
Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se
consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el
`animationend` para montar el panel, y si lo congelas mides un popup que no
existe); un token resuelto no se mueve desde `:root` por diseño; un panel
portalado no ve el ámbito del componente; la demo puede tapar el componente que
mides; y un valor centinela igual al real se lee como «no efecto».
Igual las cuatro trampas que costaron un commit cada una, con su guard al lado:
la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los
bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el
diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el
encabezado que un Edit se come al insertar una sección.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
|
|
|
|
const measure = (c: string, props: string[], size: string | null) =>
|
|
|
|
|
page.evaluate(
|
uix(prose): temable — 1 % → 83 %, la costura por ELEMENTO y dos cegueras del instrumento
35 claves nuevas (2 → 37). Censo prose 1 % → **83 %** con `global` a CERO;
global del eje **59 % → 60 %** (1.064 → 1.021 knobs `global` en el catálogo).
Prose estiliza HTML CRUDO (`h1`, `code`, `table`, `kbd`…) a través de
`:where([data-prose] el)`, así que sus «partes» son elementos y la costura va
POR PARTE, como en `result`: `heading-*` (+ `h1-font-weight`, lo único que
distingue al h1 de sus hermanos), `link-*`, `strong-`/`dt-`/`th-font-weight`,
`mark-*`, `blockquote-*`, `code-*`, `pre-*`, `kbd-*`, `media-radius`, `hr-*`,
`table-*`, más `fg`, `fg-muted` y el eje `font-size-{sm,md,lg}`. Los valores
siguen siendo los del canon; lo que faltaba era el knob (PLAN §2-A).
LA ESCALA EDITORIAL NO ENTRA, Y ES DOCTRINA FIRMADA: los `2.25em`, `1.5em`,
`0.9em`, sus interlineados y su tracking son `em`-relativos a propósito —un
`font-size` raíz re-deriva encabezados y márgenes, por eso NO hay una escala
duplicada por talla como en tailwind-typography (su README: «No 5× duplicated
scale»)— y van anotados `/* literal: */`, la válvula de recipe-contract §3. Son
las 21 excepciones firmadas que el censo aprendió a leer esta misma sesión.
Dos literales se cambiaron por su paso de escala, con el mismo valor: el `3px`
del borde-acento del blockquote ES `--border-width-thick`, y los `1px` de kbd,
`hr` y la tabla son `--border-width`.
EL INSTRUMENTO ESTABA CIEGO DE DOS MANERAS, arregladas ANTES de medir:
1. **Sonda y centinela filtran por `data-{c}-*`** y aquí NINGUNA parte lo lleva:
la sonda medía **1 nodo** y el guard daba **27 de 37** tokens por muertos.
Los dos ganan un selector de nodos extra (`EXTRA_NODES` en la sonda,
`extraNodes` en `COMPONENT_OVERRIDES`) — con él, 63 nodos y un gate que
significa algo.
2. **El centinela no leía el color de un borde que no fuera el de arriba.** El
blockquote tiñe su `border-inline-start` y la tabla sus bordes de bloque:
tres tokens vivos leían muertos. Añadidos los cuatro lados lógicos y
`borderBottomColor`.
OCHO ADJUDICACIONES, TODAS MEDIDAS: cinco las tapa el harness de la
documentación —`[data-uix-docs] code|pre` (0,2,0) gana a `:where(…)` (0,0,0),
que es la incidencia ya escrita para `code-block` un piso más arriba— y tres son
elementos que el HTML de ejemplo no usa (`<mark>`, `<dl>`), montados DENTRO del
contenedor real: un `<mark>` a pelo es exactamente lo que esta receta estiliza,
así que el nodo es fiel (la lección de `drag-drop` cumplida).
Gates: sonda antes/después **0 diffs en 11.910 valores** (63 nodos × 7 estados) ·
centinela 29/37 con las ocho adjudicadas · censo --only 83 % · `--names` 37/37
conformes · component-audit PASS · eidos-lint 0 invalid · suite eidos con el
único rojo conocido · rtl:check 0 · docs:check 0 · `check` con 72 errores
globales y 0 en los ficheros tocados. Componentes del ledger re-verificados tras
tocar el guard (text-gradient, chart, result, tree-view, textarea): sin
regresiones ni STALE.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
([kebab, keys, forced, extra]) => {
|
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica
El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes
corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro
del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y
no por la memoria de nadie.
`CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola
inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa
concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia
calendar, mandato de Field, el perímetro del knob para la familia tipográfica,
el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes →
después. El plan gana su §8 con el registro completo y corrige su cabecera, que
seguía diciendo que no había nada construido.
Y se commitea el instrumental, porque sin él el protocolo §7 es una intención:
la sonda de computed antes/después, el diff que es el gate (vacío o se para), el
centinela que pregunta si cada token alcanza de verdad, y la captura del stage.
Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen
dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev
server.
Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se
consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el
`animationend` para montar el panel, y si lo congelas mides un popup que no
existe); un token resuelto no se mueve desde `:root` por diseño; un panel
portalado no ve el ámbito del componente; la demo puede tapar el componente que
mides; y un valor centinela igual al real se lee como «no efecto».
Igual las cuatro trampas que costaron un commit cada una, con su guard al lado:
la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los
bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el
diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el
encabezado que un Edit se come al insertar una sección.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
const root = document.querySelector('[data-uix-stage]') ?? document.body;
|
uix(prose): temable — 1 % → 83 %, la costura por ELEMENTO y dos cegueras del instrumento
35 claves nuevas (2 → 37). Censo prose 1 % → **83 %** con `global` a CERO;
global del eje **59 % → 60 %** (1.064 → 1.021 knobs `global` en el catálogo).
Prose estiliza HTML CRUDO (`h1`, `code`, `table`, `kbd`…) a través de
`:where([data-prose] el)`, así que sus «partes» son elementos y la costura va
POR PARTE, como en `result`: `heading-*` (+ `h1-font-weight`, lo único que
distingue al h1 de sus hermanos), `link-*`, `strong-`/`dt-`/`th-font-weight`,
`mark-*`, `blockquote-*`, `code-*`, `pre-*`, `kbd-*`, `media-radius`, `hr-*`,
`table-*`, más `fg`, `fg-muted` y el eje `font-size-{sm,md,lg}`. Los valores
siguen siendo los del canon; lo que faltaba era el knob (PLAN §2-A).
LA ESCALA EDITORIAL NO ENTRA, Y ES DOCTRINA FIRMADA: los `2.25em`, `1.5em`,
`0.9em`, sus interlineados y su tracking son `em`-relativos a propósito —un
`font-size` raíz re-deriva encabezados y márgenes, por eso NO hay una escala
duplicada por talla como en tailwind-typography (su README: «No 5× duplicated
scale»)— y van anotados `/* literal: */`, la válvula de recipe-contract §3. Son
las 21 excepciones firmadas que el censo aprendió a leer esta misma sesión.
Dos literales se cambiaron por su paso de escala, con el mismo valor: el `3px`
del borde-acento del blockquote ES `--border-width-thick`, y los `1px` de kbd,
`hr` y la tabla son `--border-width`.
EL INSTRUMENTO ESTABA CIEGO DE DOS MANERAS, arregladas ANTES de medir:
1. **Sonda y centinela filtran por `data-{c}-*`** y aquí NINGUNA parte lo lleva:
la sonda medía **1 nodo** y el guard daba **27 de 37** tokens por muertos.
Los dos ganan un selector de nodos extra (`EXTRA_NODES` en la sonda,
`extraNodes` en `COMPONENT_OVERRIDES`) — con él, 63 nodos y un gate que
significa algo.
2. **El centinela no leía el color de un borde que no fuera el de arriba.** El
blockquote tiñe su `border-inline-start` y la tabla sus bordes de bloque:
tres tokens vivos leían muertos. Añadidos los cuatro lados lógicos y
`borderBottomColor`.
OCHO ADJUDICACIONES, TODAS MEDIDAS: cinco las tapa el harness de la
documentación —`[data-uix-docs] code|pre` (0,2,0) gana a `:where(…)` (0,0,0),
que es la incidencia ya escrita para `code-block` un piso más arriba— y tres son
elementos que el HTML de ejemplo no usa (`<mark>`, `<dl>`), montados DENTRO del
contenedor real: un `<mark>` a pelo es exactamente lo que esta receta estiliza,
así que el nodo es fiel (la lección de `drag-drop` cumplida).
Gates: sonda antes/después **0 diffs en 11.910 valores** (63 nodos × 7 estados) ·
centinela 29/37 con las ocho adjudicadas · censo --only 83 % · `--names` 37/37
conformes · component-audit PASS · eidos-lint 0 invalid · suite eidos con el
único rojo conocido · rtl:check 0 · docs:check 0 · `check` con 72 errores
globales y 0 en los ficheros tocados. Componentes del ledger re-verificados tras
tocar el guard (text-gradient, chart, result, tree-view, textarea): sin
regresiones ni STALE.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
const nodes = [...root.querySelectorAll<HTMLElement>('*')].filter(
|
|
|
|
|
(n) =>
|
|
|
|
|
[...n.attributes].some((a) => a.name.startsWith(`data-${kebab}`)) ||
|
|
|
|
|
(extra ? n.matches(extra as string) : false)
|
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica
El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes
corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro
del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y
no por la memoria de nadie.
`CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola
inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa
concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia
calendar, mandato de Field, el perímetro del knob para la familia tipográfica,
el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes →
después. El plan gana su §8 con el registro completo y corrige su cabecera, que
seguía diciendo que no había nada construido.
Y se commitea el instrumental, porque sin él el protocolo §7 es una intención:
la sonda de computed antes/después, el diff que es el gate (vacío o se para), el
centinela que pregunta si cada token alcanza de verdad, y la captura del stage.
Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen
dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev
server.
Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se
consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el
`animationend` para montar el panel, y si lo congelas mides un popup que no
existe); un token resuelto no se mueve desde `:root` por diseño; un panel
portalado no ve el ámbito del componente; la demo puede tapar el componente que
mides; y un valor centinela igual al real se lee como «no efecto».
Igual las cuatro trampas que costaron un commit cada una, con su guard al lado:
la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los
bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el
diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el
encabezado que un Edit se come al insertar una sección.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
);
|
|
|
|
|
if (forced)
|
|
|
|
|
for (const n of nodes)
|
|
|
|
|
if (n.hasAttribute('data-size')) n.setAttribute('data-size', forced);
|
|
|
|
|
const key = (n: HTMLElement) => {
|
|
|
|
|
const attrs = [...n.attributes]
|
|
|
|
|
.filter((a) => a.name.startsWith('data-') && !a.name.startsWith('data-event'))
|
|
|
|
|
.map((a) => (a.value ? `${a.name}=${a.value}` : a.name))
|
|
|
|
|
.sort()
|
|
|
|
|
.join('|');
|
|
|
|
|
return `${n.tagName.toLowerCase()}[${attrs}]`;
|
|
|
|
|
};
|
|
|
|
|
const seen = new Map<string, number>();
|
|
|
|
|
const rows: Record<string, Record<string, string>> = {};
|
|
|
|
|
for (const n of nodes) {
|
|
|
|
|
const base = key(n);
|
|
|
|
|
const i = (seen.get(base) ?? 0) + 1;
|
|
|
|
|
seen.set(base, i);
|
|
|
|
|
const cs = getComputedStyle(n);
|
|
|
|
|
const row: Record<string, string> = {};
|
|
|
|
|
for (const k of keys as string[]) row[k] = cs[k as keyof CSSStyleDeclaration] as string;
|
|
|
|
|
rows[`${base}#${i}`] = row;
|
|
|
|
|
}
|
|
|
|
|
return rows;
|
|
|
|
|
},
|
uix(prose): temable — 1 % → 83 %, la costura por ELEMENTO y dos cegueras del instrumento
35 claves nuevas (2 → 37). Censo prose 1 % → **83 %** con `global` a CERO;
global del eje **59 % → 60 %** (1.064 → 1.021 knobs `global` en el catálogo).
Prose estiliza HTML CRUDO (`h1`, `code`, `table`, `kbd`…) a través de
`:where([data-prose] el)`, así que sus «partes» son elementos y la costura va
POR PARTE, como en `result`: `heading-*` (+ `h1-font-weight`, lo único que
distingue al h1 de sus hermanos), `link-*`, `strong-`/`dt-`/`th-font-weight`,
`mark-*`, `blockquote-*`, `code-*`, `pre-*`, `kbd-*`, `media-radius`, `hr-*`,
`table-*`, más `fg`, `fg-muted` y el eje `font-size-{sm,md,lg}`. Los valores
siguen siendo los del canon; lo que faltaba era el knob (PLAN §2-A).
LA ESCALA EDITORIAL NO ENTRA, Y ES DOCTRINA FIRMADA: los `2.25em`, `1.5em`,
`0.9em`, sus interlineados y su tracking son `em`-relativos a propósito —un
`font-size` raíz re-deriva encabezados y márgenes, por eso NO hay una escala
duplicada por talla como en tailwind-typography (su README: «No 5× duplicated
scale»)— y van anotados `/* literal: */`, la válvula de recipe-contract §3. Son
las 21 excepciones firmadas que el censo aprendió a leer esta misma sesión.
Dos literales se cambiaron por su paso de escala, con el mismo valor: el `3px`
del borde-acento del blockquote ES `--border-width-thick`, y los `1px` de kbd,
`hr` y la tabla son `--border-width`.
EL INSTRUMENTO ESTABA CIEGO DE DOS MANERAS, arregladas ANTES de medir:
1. **Sonda y centinela filtran por `data-{c}-*`** y aquí NINGUNA parte lo lleva:
la sonda medía **1 nodo** y el guard daba **27 de 37** tokens por muertos.
Los dos ganan un selector de nodos extra (`EXTRA_NODES` en la sonda,
`extraNodes` en `COMPONENT_OVERRIDES`) — con él, 63 nodos y un gate que
significa algo.
2. **El centinela no leía el color de un borde que no fuera el de arriba.** El
blockquote tiñe su `border-inline-start` y la tabla sus bordes de bloque:
tres tokens vivos leían muertos. Añadidos los cuatro lados lógicos y
`borderBottomColor`.
OCHO ADJUDICACIONES, TODAS MEDIDAS: cinco las tapa el harness de la
documentación —`[data-uix-docs] code|pre` (0,2,0) gana a `:where(…)` (0,0,0),
que es la incidencia ya escrita para `code-block` un piso más arriba— y tres son
elementos que el HTML de ejemplo no usa (`<mark>`, `<dl>`), montados DENTRO del
contenedor real: un `<mark>` a pelo es exactamente lo que esta receta estiliza,
así que el nodo es fiel (la lección de `drag-drop` cumplida).
Gates: sonda antes/después **0 diffs en 11.910 valores** (63 nodos × 7 estados) ·
centinela 29/37 con las ocho adjudicadas · censo --only 83 % · `--names` 37/37
conformes · component-audit PASS · eidos-lint 0 invalid · suite eidos con el
único rojo conocido · rtl:check 0 · docs:check 0 · `check` con 72 errores
globales y 0 en los ficheros tocados. Componentes del ledger re-verificados tras
tocar el guard (text-gradient, chart, result, tree-view, textarea): sin
regresiones ni STALE.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
[c, props, size, EXTRA_NODES[c] ?? null] as const
|
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica
El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes
corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro
del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y
no por la memoria de nadie.
`CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola
inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa
concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia
calendar, mandato de Field, el perímetro del knob para la familia tipográfica,
el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes →
después. El plan gana su §8 con el registro completo y corrige su cabecera, que
seguía diciendo que no había nada construido.
Y se commitea el instrumental, porque sin él el protocolo §7 es una intención:
la sonda de computed antes/después, el diff que es el gate (vacío o se para), el
centinela que pregunta si cada token alcanza de verdad, y la captura del stage.
Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen
dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev
server.
Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se
consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el
`animationend` para montar el panel, y si lo congelas mides un popup que no
existe); un token resuelto no se mueve desde `:root` por diseño; un panel
portalado no ve el ámbito del componente; la demo puede tapar el componente que
mides; y un valor centinela igual al real se lee como «no efecto».
Igual las cuatro trampas que costaron un commit cada una, con su guard al lado:
la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los
bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el
diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el
encabezado que un Edit se come al insertar una sección.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const result: Record<string, unknown> = {};
|
|
|
|
|
result.rest = await measure(component, PROPS, null);
|
|
|
|
|
for (const size of SIZES) {
|
|
|
|
|
await page.reload({ waitUntil: 'networkidle' });
|
|
|
|
|
await page.waitForTimeout(400);
|
uix(text-gradient): temable — 67 % → 100 %, y la ficha se equivocaba en las TRES
7 claves en una entrada NUEVA de `recipes/base.ts`. Censo 67 % → **100 %**
(10 knobs, 10 públicos, 0 literales). Global 2.634 → 2.638 públicos.
Tercero de la familia text-* con la forma de text-circular / text-focus: cuatro
públicos «de facto» consumidos con fallback y **ninguno declarado**, más tres
literales. Sin eje `size` ni privados propios, así que declarar no puede matar
una escala.
LO QUE LA PROPUESTA §4 NO ACERTABA — y se resolvió midiendo contra el CATÁLOGO,
no contra el CSS de partida:
- **`weight` era deriva de nombre.** El catálogo tiene 77 claves `font-weight`
contra 4 `weight` a secas — y dos de esas cuatro las escribí ayer en esta
misma familia. Pasa a `font-weight`, y su valor al paso de escala que ya
pintaba: `500` ES `var(--font-weight-medium)`.
- **El `calc(100% - 2px)` del hueco no son dos knobs de eje físico** (la ficha
los marcaba con ⚠ «pártelo en ejes lógicos») sino UNO: los 2 px son el trazo
del marco asomando a cada lado. El knob es `border-width` —con
`var(--border-width)`, el paso `thin`, no un literal `1px` igual a un paso de
la escala— y el hueco DERIVA de él.
- **`blur(4px)` va a la escala de blur.** `blur` es familia MÉTRICA del eje
`scaling` (theming §23): un literal es ciego al zoom global. `--blur-sm` es
`calc(4px * var(--scaling))` — el mismo píxel al 100 y el correcto al 90/110.
Precedente: `dialog`/`drawer.overlay-blur`, también backdrop-filter.
Y el shorthand físico `padding: var(--space-1) var(--space-2)` se parte en ejes
lógicos (`border-padding-block` / `-inline`), que es la normalización firmada el
2026-07-06; el público de facto `border-padding` era un knob con dos ejes dentro.
FUERA DEL CONTRATO POR NATURALEZA: `--text-gradient-duration` y
`--_text-gradient-play-direction` los escribe el wrapper INLINE desde
`animationSpeed` y `yoyo`. Ningún token gana a un estilo inline, así que
declararlos sería acuñar dos nombres que mienten — misma clase que los cuatro
ausentes de `knob`, el `item-gap` de `carousel` y el `preview-z` de `drag-drop`.
INSTRUMENTO — dos puntos ciegos arreglados ANTES de medir (la lección de F2-B
aplicada por adelantado, no después):
- La demo arranca con `showBorder=false`, así que sonda y centinela veían **2
nodos** y NINGUNO de los tres tokens del marco. La sonda gana un mapa
`DEMO_VARIANTS` (chips de variante re-pulsados tras CADA recarga — recarga una
vez por talla) y el centinela una entrada `openWith` que no abre un panel sino
que ENCIENDE UNA VARIANTE: uso nuevo del mismo gancho, sin tocar su lógica.
- La sonda no leía `backdropFilter`, que `KNOB_PROPS` del censo sí cuenta y el
centinela ya leía: un token de fondo podía moverse entre las dos corridas sin
que el diff lo viera. Con la propiedad y la variante: **4 nodos**.
Hover: 0 nodos medidos, y es correcto — las cuatro partes son `<span>` y el
único efecto de hover es `animation-play-state`, que no es knob.
LO QUE ABRE (→ next-features §13): **la escala de `font-size` no tiene
`2xl`/`3xl`/`4xl`** —es `xxs..xxxl`— y tres sitios los referencian: el token
`text-focus.word-size` que subí ayer (`var(--font-size-4xl, 3rem)`, pintando el
fallback: segundo fantasma de la clase `--radius-xs`, y en el contrato) y los
escenarios de las demos de `text-gradient` y `text-blur`, que sin fallback dejan
la declaración INVÁLIDA y enseñan un efecto tipográfico a 14 px. Y
`--uix-font-size-*` no existe: 37 referencias en el harness del sitio, cero
declaraciones. Ninguno se toca aquí: se miden, se anotan y van a su commit.
Demo: pestaña **Tokens** (`TokensPanel`, contrato vivo) y fuera la tabla de
tokens escrita a mano de la pestaña API — era una segunda fuente de verdad y
este renombrado la dejaba obsoleta al instante.
Gates: sonda antes/después **0 diffs** en 720 valores × 7 estados (4 nodos) ·
centinela **7/7 sin una sola excepción** · censo --only 100 % · --names 7/7
conformes · component-audit PASS · eidos-lint 0 invalid · suite eidos con el
único rojo conocido (`skin-media-player`, lint.test.ts) · rtl:check 0 ·
docs:check 0 · `check` con 72 errores globales y **0 en los ficheros tocados**.
Prettier: los seis ficheros editados ya estaban sucios en HEAD (el repo tiene
2.392), no se reformatean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
await prepare();
|
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica
El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes
corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro
del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y
no por la memoria de nadie.
`CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola
inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa
concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia
calendar, mandato de Field, el perímetro del knob para la familia tipográfica,
el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes →
después. El plan gana su §8 con el registro completo y corrige su cabecera, que
seguía diciendo que no había nada construido.
Y se commitea el instrumental, porque sin él el protocolo §7 es una intención:
la sonda de computed antes/después, el diff que es el gate (vacío o se para), el
centinela que pregunta si cada token alcanza de verdad, y la captura del stage.
Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen
dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev
server.
Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se
consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el
`animationend` para montar el panel, y si lo congelas mides un popup que no
existe); un token resuelto no se mueve desde `:root` por diseño; un panel
portalado no ve el ámbito del componente; la demo puede tapar el componente que
mides; y un valor centinela igual al real se lee como «no efecto».
Igual las cuatro trampas que costaron un commit cada una, con su guard al lado:
la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los
bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el
diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el
encabezado que un Edit se come al insertar una sección.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
result[`size:${size}`] = await measure(component, PROPS, size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Open state: a portaled panel lives OUTSIDE the stage, so measure from the
|
|
|
|
|
// document once the trigger has opened it (§7.2 — probe the popup open).
|
|
|
|
|
await page.reload({ waitUntil: 'networkidle' });
|
|
|
|
|
await page.waitForTimeout(400);
|
uix(text-gradient): temable — 67 % → 100 %, y la ficha se equivocaba en las TRES
7 claves en una entrada NUEVA de `recipes/base.ts`. Censo 67 % → **100 %**
(10 knobs, 10 públicos, 0 literales). Global 2.634 → 2.638 públicos.
Tercero de la familia text-* con la forma de text-circular / text-focus: cuatro
públicos «de facto» consumidos con fallback y **ninguno declarado**, más tres
literales. Sin eje `size` ni privados propios, así que declarar no puede matar
una escala.
LO QUE LA PROPUESTA §4 NO ACERTABA — y se resolvió midiendo contra el CATÁLOGO,
no contra el CSS de partida:
- **`weight` era deriva de nombre.** El catálogo tiene 77 claves `font-weight`
contra 4 `weight` a secas — y dos de esas cuatro las escribí ayer en esta
misma familia. Pasa a `font-weight`, y su valor al paso de escala que ya
pintaba: `500` ES `var(--font-weight-medium)`.
- **El `calc(100% - 2px)` del hueco no son dos knobs de eje físico** (la ficha
los marcaba con ⚠ «pártelo en ejes lógicos») sino UNO: los 2 px son el trazo
del marco asomando a cada lado. El knob es `border-width` —con
`var(--border-width)`, el paso `thin`, no un literal `1px` igual a un paso de
la escala— y el hueco DERIVA de él.
- **`blur(4px)` va a la escala de blur.** `blur` es familia MÉTRICA del eje
`scaling` (theming §23): un literal es ciego al zoom global. `--blur-sm` es
`calc(4px * var(--scaling))` — el mismo píxel al 100 y el correcto al 90/110.
Precedente: `dialog`/`drawer.overlay-blur`, también backdrop-filter.
Y el shorthand físico `padding: var(--space-1) var(--space-2)` se parte en ejes
lógicos (`border-padding-block` / `-inline`), que es la normalización firmada el
2026-07-06; el público de facto `border-padding` era un knob con dos ejes dentro.
FUERA DEL CONTRATO POR NATURALEZA: `--text-gradient-duration` y
`--_text-gradient-play-direction` los escribe el wrapper INLINE desde
`animationSpeed` y `yoyo`. Ningún token gana a un estilo inline, así que
declararlos sería acuñar dos nombres que mienten — misma clase que los cuatro
ausentes de `knob`, el `item-gap` de `carousel` y el `preview-z` de `drag-drop`.
INSTRUMENTO — dos puntos ciegos arreglados ANTES de medir (la lección de F2-B
aplicada por adelantado, no después):
- La demo arranca con `showBorder=false`, así que sonda y centinela veían **2
nodos** y NINGUNO de los tres tokens del marco. La sonda gana un mapa
`DEMO_VARIANTS` (chips de variante re-pulsados tras CADA recarga — recarga una
vez por talla) y el centinela una entrada `openWith` que no abre un panel sino
que ENCIENDE UNA VARIANTE: uso nuevo del mismo gancho, sin tocar su lógica.
- La sonda no leía `backdropFilter`, que `KNOB_PROPS` del censo sí cuenta y el
centinela ya leía: un token de fondo podía moverse entre las dos corridas sin
que el diff lo viera. Con la propiedad y la variante: **4 nodos**.
Hover: 0 nodos medidos, y es correcto — las cuatro partes son `<span>` y el
único efecto de hover es `animation-play-state`, que no es knob.
LO QUE ABRE (→ next-features §13): **la escala de `font-size` no tiene
`2xl`/`3xl`/`4xl`** —es `xxs..xxxl`— y tres sitios los referencian: el token
`text-focus.word-size` que subí ayer (`var(--font-size-4xl, 3rem)`, pintando el
fallback: segundo fantasma de la clase `--radius-xs`, y en el contrato) y los
escenarios de las demos de `text-gradient` y `text-blur`, que sin fallback dejan
la declaración INVÁLIDA y enseñan un efecto tipográfico a 14 px. Y
`--uix-font-size-*` no existe: 37 referencias en el harness del sitio, cero
declaraciones. Ninguno se toca aquí: se miden, se anotan y van a su commit.
Demo: pestaña **Tokens** (`TokensPanel`, contrato vivo) y fuera la tabla de
tokens escrita a mano de la pestaña API — era una segunda fuente de verdad y
este renombrado la dejaba obsoleta al instante.
Gates: sonda antes/después **0 diffs** en 720 valores × 7 estados (4 nodos) ·
centinela **7/7 sin una sola excepción** · censo --only 100 % · --names 7/7
conformes · component-audit PASS · eidos-lint 0 invalid · suite eidos con el
único rojo conocido (`skin-media-player`, lint.test.ts) · rtl:check 0 ·
docs:check 0 · `check` con 72 errores globales y **0 en los ficheros tocados**.
Prettier: los seis ficheros editados ya estaban sucios en HEAD (el repo tiene
2.392), no se reformatean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
await prepare();
|
uix(menubar): temable — 15 % → 88 %, y el plano `overlay` GANA a la receta
30 claves nuevas (5 → 35): la barra (superficie, marco, radio —uno solo, la
receta lo desacopla de la talla a propósito y el motor de forma deriva el del
disparador de él— y las tres escalas `gap`/`padding`/`height`), el disparador
(tinta, tipografía, escala de `font-size` y de `padding-inline`, el par de la
entrada ABIERTA y la tinta deshabilitada) y el Panel `role=dialog` (tinta +
escala). Global 62 % → 63 %.
Los pasos leen el BUNDLE (`--size-{k}-control-height` / `--size-{k}-font-size`),
no el primitivo crudo que el CSS venía consumiendo; y el
`calc(var(--space-1) * 0.5)` del paso `sm` es `var(--space-0-5)` exacto.
EL HUECO DEL PANEL DEJA DE REPETIR EL VALOR DEL PADDING. Las tres reglas de
`--floating-gap` compensan el sangrado del disparador dentro de la barra con lo
que era una COPIA del padding; ahora leen `--menubar-padding-{k}`, su fuente —
si un tema ensancha el padding, el panel deja de quedar a ras de la barra. No
pueden leer el privado `--_menubar-padding`: esas superficies viajan POR PORTAL
y un privado declarado en la raíz no cruza esa frontera.
⚠ EL PLANO `overlay` GANA A LA RECETA — cuarta prueba de §12.9 y la primera con
MECANISMO. Acuñé `panel-font-family` y `panel-line-height` sobre declaraciones
que la receta YA tenía, y el centinela las dio muertas. Medido:
`[data-depth='overlay']` declara las dos con la MISMA especificidad (0,1,0) y
más tarde en la cascada, así que gana; del mismo bloque, `color` y `font-size`
—que el plano no declara— sí alcanzan. Retiradas del contrato y devueltas a su
fuente literal, con la razón escrita en el CSS. Mientras §12.9 no se firme, un
token de tipografía en CUALQUIER superficie `overlay` miente, y son veinte.
EL HOVER NO SE ACUÑA: ES DEL EJE navigation-menu. `background:
var(--color-surface-overlay)` sobre el disparador mata la capa del sistema
—medido: `background-image` computa `none` en hover con `--state-hover` vivo—,
por las dos razones que `navigation-menu.css` ya documenta al retirar esta
MISMA regla: el shorthand resetea `background-image`, y la regla del arquetipo
va en `:where()`, especificidad CERO. Acuñar un `hover-trigger-bg` habría
fosilizado la invención.
Instrumento: la barra suelta DOS superficies y el Panel —el que lleva sus
knobs— cuelga de la entrada «Format», no de la primera; sonda (`OPEN_TRIGGER`)
y guard (`openWith`) aprenden a abrir la que toca. Y el guard necesitó
`openMarker`: su comprobación de «ya está abierto» busca `[data-{c}-content]`,
que aquí no existe, así que volvía a pulsar el disparador antes de cada token y
CERRABA el panel — media corrida contra una superficie cerrada.
Gates: sonda 0 diffs en 1.470 valores × 8 estados · centinela 34/35 (la
deshabilitada, adjudicada) · censo 88 % · `--names` 0 desviadas ·
`component:audit` PASS · `rtl:check` 0 · `docs:check` 0 · suite eidos con el
rojo conocido ajeno (`skin-media-player`) · `check` sin errores propios ·
capturas de barra, tallas sm/lg, menú abierto y panel.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
const trigger = page
|
|
|
|
|
.locator(OPEN_TRIGGER[component] ?? `[data-${component}-trigger], [data-${component}-input]`)
|
|
|
|
|
.first();
|
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica
El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes
corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro
del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y
no por la memoria de nadie.
`CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola
inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa
concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia
calendar, mandato de Field, el perímetro del knob para la familia tipográfica,
el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes →
después. El plan gana su §8 con el registro completo y corrige su cabecera, que
seguía diciendo que no había nada construido.
Y se commitea el instrumental, porque sin él el protocolo §7 es una intención:
la sonda de computed antes/después, el diff que es el gate (vacío o se para), el
centinela que pregunta si cada token alcanza de verdad, y la captura del stage.
Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen
dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev
server.
Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se
consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el
`animationend` para montar el panel, y si lo congelas mides un popup que no
existe); un token resuelto no se mueve desde `:root` por diseño; un panel
portalado no ve el ámbito del componente; la demo puede tapar el componente que
mides; y un valor centinela igual al real se lee como «no efecto».
Igual las cuatro trampas que costaron un commit cada una, con su guard al lado:
la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los
bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el
diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el
encabezado que un Edit se come al insertar una sección.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
if (await trigger.count()) {
|
|
|
|
|
try {
|
uix(context-menu): temable — 53 % → 90 %, y el instrumento aprende a abrir un menú contextual
11 claves (18 → 29). Censo context-menu 53 % → **90 %** con `global` a CERO.
EL INSTRUMENTO NO SABÍA ABRIRLO. Un menú contextual se abre con clic DERECHO y
con nada más, así que sonda y guard medían UN nodo: el disparador. Los dos
aprenden el modo (`openBy: 'contextmenu'` en `COMPONENT_OVERRIDES`, clic derecho
en la sonda) y pasan a ver **14 nodos** en el estado abierto; el guard, de un
baremo ciego a **27/29**.
Cosido el cromo propio del panel y del disparador: tipografía y tinta del
contenido, tinta e interlineado de la fila, el galón del submenú (dos bordes
girados 45°, un solo trazo), el encabezado de grupo (tinta, peso y tracking de
versalitas) y el contorno DISCONTINUO que marca el disparador abierto — el estilo
`dashed` es identidad de la afordancia; el grosor y el color son knobs.
Dos limpiezas de paso: el `font-family` leía
`var(--style-label-font-family, var(--style-label-font-family))` —respaldo de sí
mismo— y el peso del encabezado llevaba un `500` muerto detrás de un token que ya
vale 500.
DOS ADJUDICACIONES MEDIDAS, y una es la TERCERA ocurrencia del mismo hallazgo:
`content-font-family` no puede ganar porque el plano `overlay` impone la
tipografía del panel (§12.9, pendiente de tu firma — ya medido en `popover` y en
`float-panel`). La otra es el ítem deshabilitado, que la demo no monta: forzado,
0.55 → 0.123.
Gates: sonda antes/después **0 diffs en 600 valores** (8 estados, 14 nodos en el
abierto) · centinela 27/29 · censo --only 90 % · `--names` sin desviadas ·
component-audit PASS · eidos-lint 0 invalid · suite eidos con el único rojo
conocido · rtl:check 0 · docs:check 0 · captura del panel mirada (encabezado en
versalitas, filas, casillas, galón del submenú y separadores en su sitio).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
// A context menu opens on RIGHT click and on nothing else.
|
|
|
|
|
await trigger.click({ button: component === 'context-menu' ? 'right' : 'left', timeout: 2000 });
|
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica
El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes
corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro
del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y
no por la memoria de nadie.
`CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola
inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa
concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia
calendar, mandato de Field, el perímetro del knob para la familia tipográfica,
el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes →
después. El plan gana su §8 con el registro completo y corrige su cabecera, que
seguía diciendo que no había nada construido.
Y se commitea el instrumental, porque sin él el protocolo §7 es una intención:
la sonda de computed antes/después, el diff que es el gate (vacío o se para), el
centinela que pregunta si cada token alcanza de verdad, y la captura del stage.
Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen
dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev
server.
Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se
consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el
`animationend` para montar el panel, y si lo congelas mides un popup que no
existe); un token resuelto no se mueve desde `:root` por diseño; un panel
portalado no ve el ámbito del componente; la demo puede tapar el componente que
mides; y un valor centinela igual al real se lee como «no efecto».
Igual las cuatro trampas que costaron un commit cada una, con su guard al lado:
la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los
bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el
diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el
encabezado que un Edit se come al insertar una sección.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
await page.waitForTimeout(500);
|
|
|
|
|
result.open = await page.evaluate(
|
|
|
|
|
([kebab, keys]) => {
|
|
|
|
|
const nodes = [...document.querySelectorAll<HTMLElement>('*')].filter((n) =>
|
|
|
|
|
[...n.attributes].some((a) => a.name.startsWith(`data-${kebab}`))
|
|
|
|
|
);
|
|
|
|
|
const seen = new Map<string, number>();
|
|
|
|
|
const rows: Record<string, Record<string, string>> = {};
|
|
|
|
|
for (const n of nodes) {
|
|
|
|
|
const attrs = [...n.attributes]
|
|
|
|
|
.filter((a) => a.name.startsWith('data-') && !a.name.startsWith('data-event'))
|
|
|
|
|
.map((a) => (a.value ? `${a.name}=${a.value}` : a.name))
|
|
|
|
|
.sort()
|
|
|
|
|
.join('|');
|
|
|
|
|
const base = `${n.tagName.toLowerCase()}[${attrs}]`;
|
|
|
|
|
const i = (seen.get(base) ?? 0) + 1;
|
|
|
|
|
seen.set(base, i);
|
|
|
|
|
const cs = getComputedStyle(n);
|
|
|
|
|
const row: Record<string, string> = {};
|
|
|
|
|
for (const k of keys as string[]) row[k] = cs[k as keyof CSSStyleDeclaration] as string;
|
|
|
|
|
rows[`${base}#${i}`] = row;
|
|
|
|
|
}
|
|
|
|
|
return rows;
|
|
|
|
|
},
|
|
|
|
|
[component, PROPS] as const
|
|
|
|
|
);
|
|
|
|
|
} catch {
|
|
|
|
|
/* no openable trigger — skip the state */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Hover + focus on every interactive part the recipe could paint.
|
|
|
|
|
await page.reload({ waitUntil: 'networkidle' });
|
|
|
|
|
await page.waitForTimeout(400);
|
uix(text-gradient): temable — 67 % → 100 %, y la ficha se equivocaba en las TRES
7 claves en una entrada NUEVA de `recipes/base.ts`. Censo 67 % → **100 %**
(10 knobs, 10 públicos, 0 literales). Global 2.634 → 2.638 públicos.
Tercero de la familia text-* con la forma de text-circular / text-focus: cuatro
públicos «de facto» consumidos con fallback y **ninguno declarado**, más tres
literales. Sin eje `size` ni privados propios, así que declarar no puede matar
una escala.
LO QUE LA PROPUESTA §4 NO ACERTABA — y se resolvió midiendo contra el CATÁLOGO,
no contra el CSS de partida:
- **`weight` era deriva de nombre.** El catálogo tiene 77 claves `font-weight`
contra 4 `weight` a secas — y dos de esas cuatro las escribí ayer en esta
misma familia. Pasa a `font-weight`, y su valor al paso de escala que ya
pintaba: `500` ES `var(--font-weight-medium)`.
- **El `calc(100% - 2px)` del hueco no son dos knobs de eje físico** (la ficha
los marcaba con ⚠ «pártelo en ejes lógicos») sino UNO: los 2 px son el trazo
del marco asomando a cada lado. El knob es `border-width` —con
`var(--border-width)`, el paso `thin`, no un literal `1px` igual a un paso de
la escala— y el hueco DERIVA de él.
- **`blur(4px)` va a la escala de blur.** `blur` es familia MÉTRICA del eje
`scaling` (theming §23): un literal es ciego al zoom global. `--blur-sm` es
`calc(4px * var(--scaling))` — el mismo píxel al 100 y el correcto al 90/110.
Precedente: `dialog`/`drawer.overlay-blur`, también backdrop-filter.
Y el shorthand físico `padding: var(--space-1) var(--space-2)` se parte en ejes
lógicos (`border-padding-block` / `-inline`), que es la normalización firmada el
2026-07-06; el público de facto `border-padding` era un knob con dos ejes dentro.
FUERA DEL CONTRATO POR NATURALEZA: `--text-gradient-duration` y
`--_text-gradient-play-direction` los escribe el wrapper INLINE desde
`animationSpeed` y `yoyo`. Ningún token gana a un estilo inline, así que
declararlos sería acuñar dos nombres que mienten — misma clase que los cuatro
ausentes de `knob`, el `item-gap` de `carousel` y el `preview-z` de `drag-drop`.
INSTRUMENTO — dos puntos ciegos arreglados ANTES de medir (la lección de F2-B
aplicada por adelantado, no después):
- La demo arranca con `showBorder=false`, así que sonda y centinela veían **2
nodos** y NINGUNO de los tres tokens del marco. La sonda gana un mapa
`DEMO_VARIANTS` (chips de variante re-pulsados tras CADA recarga — recarga una
vez por talla) y el centinela una entrada `openWith` que no abre un panel sino
que ENCIENDE UNA VARIANTE: uso nuevo del mismo gancho, sin tocar su lógica.
- La sonda no leía `backdropFilter`, que `KNOB_PROPS` del censo sí cuenta y el
centinela ya leía: un token de fondo podía moverse entre las dos corridas sin
que el diff lo viera. Con la propiedad y la variante: **4 nodos**.
Hover: 0 nodos medidos, y es correcto — las cuatro partes son `<span>` y el
único efecto de hover es `animation-play-state`, que no es knob.
LO QUE ABRE (→ next-features §13): **la escala de `font-size` no tiene
`2xl`/`3xl`/`4xl`** —es `xxs..xxxl`— y tres sitios los referencian: el token
`text-focus.word-size` que subí ayer (`var(--font-size-4xl, 3rem)`, pintando el
fallback: segundo fantasma de la clase `--radius-xs`, y en el contrato) y los
escenarios de las demos de `text-gradient` y `text-blur`, que sin fallback dejan
la declaración INVÁLIDA y enseñan un efecto tipográfico a 14 px. Y
`--uix-font-size-*` no existe: 37 referencias en el harness del sitio, cero
declaraciones. Ninguno se toca aquí: se miden, se anotan y van a su commit.
Demo: pestaña **Tokens** (`TokensPanel`, contrato vivo) y fuera la tabla de
tokens escrita a mano de la pestaña API — era una segunda fuente de verdad y
este renombrado la dejaba obsoleta al instante.
Gates: sonda antes/después **0 diffs** en 720 valores × 7 estados (4 nodos) ·
centinela **7/7 sin una sola excepción** · censo --only 100 % · --names 7/7
conformes · component-audit PASS · eidos-lint 0 invalid · suite eidos con el
único rojo conocido (`skin-media-player`, lint.test.ts) · rtl:check 0 ·
docs:check 0 · `check` con 72 errores globales y **0 en los ficheros tocados**.
Prettier: los seis ficheros editados ya estaban sucios en HEAD (el repo tiene
2.392), no se reformatean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
await prepare();
|
uix(prose): temable — 1 % → 83 %, la costura por ELEMENTO y dos cegueras del instrumento
35 claves nuevas (2 → 37). Censo prose 1 % → **83 %** con `global` a CERO;
global del eje **59 % → 60 %** (1.064 → 1.021 knobs `global` en el catálogo).
Prose estiliza HTML CRUDO (`h1`, `code`, `table`, `kbd`…) a través de
`:where([data-prose] el)`, así que sus «partes» son elementos y la costura va
POR PARTE, como en `result`: `heading-*` (+ `h1-font-weight`, lo único que
distingue al h1 de sus hermanos), `link-*`, `strong-`/`dt-`/`th-font-weight`,
`mark-*`, `blockquote-*`, `code-*`, `pre-*`, `kbd-*`, `media-radius`, `hr-*`,
`table-*`, más `fg`, `fg-muted` y el eje `font-size-{sm,md,lg}`. Los valores
siguen siendo los del canon; lo que faltaba era el knob (PLAN §2-A).
LA ESCALA EDITORIAL NO ENTRA, Y ES DOCTRINA FIRMADA: los `2.25em`, `1.5em`,
`0.9em`, sus interlineados y su tracking son `em`-relativos a propósito —un
`font-size` raíz re-deriva encabezados y márgenes, por eso NO hay una escala
duplicada por talla como en tailwind-typography (su README: «No 5× duplicated
scale»)— y van anotados `/* literal: */`, la válvula de recipe-contract §3. Son
las 21 excepciones firmadas que el censo aprendió a leer esta misma sesión.
Dos literales se cambiaron por su paso de escala, con el mismo valor: el `3px`
del borde-acento del blockquote ES `--border-width-thick`, y los `1px` de kbd,
`hr` y la tabla son `--border-width`.
EL INSTRUMENTO ESTABA CIEGO DE DOS MANERAS, arregladas ANTES de medir:
1. **Sonda y centinela filtran por `data-{c}-*`** y aquí NINGUNA parte lo lleva:
la sonda medía **1 nodo** y el guard daba **27 de 37** tokens por muertos.
Los dos ganan un selector de nodos extra (`EXTRA_NODES` en la sonda,
`extraNodes` en `COMPONENT_OVERRIDES`) — con él, 63 nodos y un gate que
significa algo.
2. **El centinela no leía el color de un borde que no fuera el de arriba.** El
blockquote tiñe su `border-inline-start` y la tabla sus bordes de bloque:
tres tokens vivos leían muertos. Añadidos los cuatro lados lógicos y
`borderBottomColor`.
OCHO ADJUDICACIONES, TODAS MEDIDAS: cinco las tapa el harness de la
documentación —`[data-uix-docs] code|pre` (0,2,0) gana a `:where(…)` (0,0,0),
que es la incidencia ya escrita para `code-block` un piso más arriba— y tres son
elementos que el HTML de ejemplo no usa (`<mark>`, `<dl>`), montados DENTRO del
contenedor real: un `<mark>` a pelo es exactamente lo que esta receta estiliza,
así que el nodo es fiel (la lección de `drag-drop` cumplida).
Gates: sonda antes/después **0 diffs en 11.910 valores** (63 nodos × 7 estados) ·
centinela 29/37 con las ocho adjudicadas · censo --only 83 % · `--names` 37/37
conformes · component-audit PASS · eidos-lint 0 invalid · suite eidos con el
único rojo conocido · rtl:check 0 · docs:check 0 · `check` con 72 errores
globales y 0 en los ficheros tocados. Componentes del ledger re-verificados tras
tocar el guard (text-gradient, chart, result, tree-view, textarea): sin
regresiones ni STALE.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
const targets = await page.evaluate(([kebab, extra]) => {
|
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica
El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes
corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro
del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y
no por la memoria de nadie.
`CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola
inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa
concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia
calendar, mandato de Field, el perímetro del knob para la familia tipográfica,
el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes →
después. El plan gana su §8 con el registro completo y corrige su cabecera, que
seguía diciendo que no había nada construido.
Y se commitea el instrumental, porque sin él el protocolo §7 es una intención:
la sonda de computed antes/después, el diff que es el gate (vacío o se para), el
centinela que pregunta si cada token alcanza de verdad, y la captura del stage.
Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen
dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev
server.
Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se
consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el
`animationend` para montar el panel, y si lo congelas mides un popup que no
existe); un token resuelto no se mueve desde `:root` por diseño; un panel
portalado no ve el ámbito del componente; la demo puede tapar el componente que
mides; y un valor centinela igual al real se lee como «no efecto».
Igual las cuatro trampas que costaron un commit cada una, con su guard al lado:
la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los
bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el
diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el
encabezado que un Edit se come al insertar una sección.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
const root = document.querySelector('[data-uix-stage]') ?? document.body;
|
|
|
|
|
return [...root.querySelectorAll<HTMLElement>('*')]
|
uix(prose): temable — 1 % → 83 %, la costura por ELEMENTO y dos cegueras del instrumento
35 claves nuevas (2 → 37). Censo prose 1 % → **83 %** con `global` a CERO;
global del eje **59 % → 60 %** (1.064 → 1.021 knobs `global` en el catálogo).
Prose estiliza HTML CRUDO (`h1`, `code`, `table`, `kbd`…) a través de
`:where([data-prose] el)`, así que sus «partes» son elementos y la costura va
POR PARTE, como en `result`: `heading-*` (+ `h1-font-weight`, lo único que
distingue al h1 de sus hermanos), `link-*`, `strong-`/`dt-`/`th-font-weight`,
`mark-*`, `blockquote-*`, `code-*`, `pre-*`, `kbd-*`, `media-radius`, `hr-*`,
`table-*`, más `fg`, `fg-muted` y el eje `font-size-{sm,md,lg}`. Los valores
siguen siendo los del canon; lo que faltaba era el knob (PLAN §2-A).
LA ESCALA EDITORIAL NO ENTRA, Y ES DOCTRINA FIRMADA: los `2.25em`, `1.5em`,
`0.9em`, sus interlineados y su tracking son `em`-relativos a propósito —un
`font-size` raíz re-deriva encabezados y márgenes, por eso NO hay una escala
duplicada por talla como en tailwind-typography (su README: «No 5× duplicated
scale»)— y van anotados `/* literal: */`, la válvula de recipe-contract §3. Son
las 21 excepciones firmadas que el censo aprendió a leer esta misma sesión.
Dos literales se cambiaron por su paso de escala, con el mismo valor: el `3px`
del borde-acento del blockquote ES `--border-width-thick`, y los `1px` de kbd,
`hr` y la tabla son `--border-width`.
EL INSTRUMENTO ESTABA CIEGO DE DOS MANERAS, arregladas ANTES de medir:
1. **Sonda y centinela filtran por `data-{c}-*`** y aquí NINGUNA parte lo lleva:
la sonda medía **1 nodo** y el guard daba **27 de 37** tokens por muertos.
Los dos ganan un selector de nodos extra (`EXTRA_NODES` en la sonda,
`extraNodes` en `COMPONENT_OVERRIDES`) — con él, 63 nodos y un gate que
significa algo.
2. **El centinela no leía el color de un borde que no fuera el de arriba.** El
blockquote tiñe su `border-inline-start` y la tabla sus bordes de bloque:
tres tokens vivos leían muertos. Añadidos los cuatro lados lógicos y
`borderBottomColor`.
OCHO ADJUDICACIONES, TODAS MEDIDAS: cinco las tapa el harness de la
documentación —`[data-uix-docs] code|pre` (0,2,0) gana a `:where(…)` (0,0,0),
que es la incidencia ya escrita para `code-block` un piso más arriba— y tres son
elementos que el HTML de ejemplo no usa (`<mark>`, `<dl>`), montados DENTRO del
contenedor real: un `<mark>` a pelo es exactamente lo que esta receta estiliza,
así que el nodo es fiel (la lección de `drag-drop` cumplida).
Gates: sonda antes/después **0 diffs en 11.910 valores** (63 nodos × 7 estados) ·
centinela 29/37 con las ocho adjudicadas · censo --only 83 % · `--names` 37/37
conformes · component-audit PASS · eidos-lint 0 invalid · suite eidos con el
único rojo conocido · rtl:check 0 · docs:check 0 · `check` con 72 errores
globales y 0 en los ficheros tocados. Componentes del ledger re-verificados tras
tocar el guard (text-gradient, chart, result, tree-view, textarea): sin
regresiones ni STALE.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
.filter(
|
|
|
|
|
(n) =>
|
|
|
|
|
[...n.attributes].some((a) => a.name.startsWith(`data-${kebab}`)) ||
|
|
|
|
|
(extra ? n.matches(extra) : false)
|
|
|
|
|
)
|
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica
El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes
corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro
del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y
no por la memoria de nadie.
`CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola
inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa
concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia
calendar, mandato de Field, el perímetro del knob para la familia tipográfica,
el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes →
después. El plan gana su §8 con el registro completo y corrige su cabecera, que
seguía diciendo que no había nada construido.
Y se commitea el instrumental, porque sin él el protocolo §7 es una intención:
la sonda de computed antes/después, el diff que es el gate (vacío o se para), el
centinela que pregunta si cada token alcanza de verdad, y la captura del stage.
Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen
dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev
server.
Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se
consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el
`animationend` para montar el panel, y si lo congelas mides un popup que no
existe); un token resuelto no se mueve desde `:root` por diseño; un panel
portalado no ve el ámbito del componente; la demo puede tapar el componente que
mides; y un valor centinela igual al real se lee como «no efecto».
Igual las cuatro trampas que costaron un commit cada una, con su guard al lado:
la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los
bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el
diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el
encabezado que un Edit se come al insertar una sección.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
.map((n, i) => ({ i, tag: n.tagName.toLowerCase() }))
|
uix(theming): revisión adversarial de F2-A — 8 hallazgos de 34, y nace R-5.4
Pase escéptico del bloque (§7.7, molde Sidebar) con medición propia: worktree
con SOLO el CSS revertido a la base del bloque (DOM de demo constante — prueba
más dura que la del autor), sonda x2 por componente para fijar el suelo de
ruido. 34 hipótesis · 26 refutadas · 8 reales.
Lo que NO se pudo romper: el «default idéntico» aguanta en los ocho (0 diffs;
command 8.091 · table 13.050 · media-player 3.277 · gradient-picker 609 ·
listbox 2.813 · carousel 2.842; los 3 de tree-grid y los 5 de feed salen
idénticos corriendo DOS veces el mismo código → instrumento); 0 tokens
huérfanos de 336; el bundle ES 1:1 con los primitivos; el «verificado a mano»
de gradient-picker era verdad (15/16 alcanzan al montar); sin doble animación;
eidos-lint 0 invalid.
Los 4 de código, arreglados con diff de computed VACÍO (re-medido: command
8.091 y carousel 2.842 valores, 0 diffs):
1. command: el re-point de `--command-radius` dentro del Dialog era una
declaración muerta con comentario falso — la misma regla pone el radio a 0
y el input lee `--command-input-radius`, nunca este token (medido:
`--dialog-content-radius: 9999px` no movía NADA; `--command-radius: 77px`
movía la paleta inline y no la del dialog). Retirado; comentarios y README
reescritos con lo medido.
2. carousel: la regla vertical del indicador activo llevaba el `2.25` a pelo
mientras la horizontal leía `active-indicator-scale` — el token alcanzaba
media superficie (scale:6 → horizontal 18→48px, vertical 18→18px). Ahora
lo leen las dos.
3. carousel: el trío `item-gap*` no podía ganar NUNCA — soma estampa `gap`
INLINE desde la prop (lo necesita para el flex-basis; reglas casadas por
CDP: la de la receta y encima INLINE gap:0rem). Retirados los 3 tokens y
las 2 declaraciones muertas (la base y el gap:0 de ghost): 0 diffs, que es
la prueba. Canal de valor de soma, como --gp-current-gradient. Censo
carousel 35 knobs · 23 públicos · 77 % · 27 claves; global 43 % intacto
(5.190 · 2.102).
4. table: `striped-row-bg` no alcanza NADA en la demo publicada —
`:nth-of-type(even)` cuenta los <tr> de RowDetail y las filas de datos
quedan impares (medido tr a tr: diez inmunes). Arreglarlo mueve píxel →
PENDIENTE DE FIRMA (candidato `:nth-child(even of [data-table-row])`);
adjudicado en el ledger mientras tanto.
El instrumento cargaba 22 falsos negativos de 26 «no effect», con tres causas
medidas, y la cuarta era de la sonda:
- el paso de abrir hacía CLIC en el input, y un clic de ratón sobre un input
de texto SÍ casa `:focus-visible` — la regla de foco pintaba el borde y el
token de reposo leía muerto (era el «falso negativo sin causa» de §13);
- el override se escribía solo en `[data-{c}]`, y las partes que cuelgan de
`{c}-root` no lo recibían;
- `::before`/`::after` eran invisibles (el aro de buffering, el spinner);
- la no-determinación de feed no era una animación sin localizar: era la demo
aún CARGANDO — el auto load-more mantiene [data-busy] ~3 s y la firma
`commit-settle` anima box-shadow sobre el nodo medido. Con la espera de
asentado: 0 diffs y 0 nodos ausentes sobre 6.264 valores, dos corridas.
Con eso NACE EL GUARD R-5.4 (la deuda que §13 pedía desde gradient-picker, y
que carousel convirtió en patrón — un estilo inline de otra capa es invisible
para todo análisis estático): `npm run theming:sentinel -- <c> <url>`
(scripts/theming-sentinel.ts, promovido de `__`; blur tras abrir, override en
todos los nodos del componente, pseudos, pase de hover, settle) + ledger
scripts/theming-sentinel-exceptions.ts con UNA razón medida por token — muerto
sin adjudicar = exit 1, excepción STALE = aviso. Los ocho componentes en
verde: command 50/57 · table 41/45 · media-player 46/61 · tree-grid 45/47 ·
gradient-picker 9/25 · listbox 20/21 · carousel 26/27 · feed 43/50, con 46
excepciones adjudicadas. El guard ya me corrigió a mí: cinco entradas mías
salieron STALE (los control-height-* de media-player SÍ alcanzan) y las borró;
y dos muertos nuevos los produjeron los propios arreglos (el blur apaga
focus-input-border; el settle desmonta el spinner de feed) — adjudicados con
su porqué. Doctrina en recipe-contract §4.
También: tres cifras de especificidad de next-features §12 recontadas (la
banda de tree-grid es (0,8,0); la tinta seleccionada del arquetipo es (0,4,0);
y el `item` de table vive en la fila Y EN LA CELDA — elementsFromPoint pone el
velo en la celda), el comentario de listbox que mandaba pisar el privado
`--_listbox-max-height` apunta ahora al público, y quedan registrados en §13
los dos `header-z` con literal '2' y el spinner de media-player a 720ms
mientras el gemelo de feed lleva tokens.
Guards: censo global 43 % intacto · audit --only command+carousel PASS ·
eidos-lint 0 invalid (command 16/12, carousel 29/9, listbox 12/7) · vitest
eidos 434/435 (el rojo es el conocido skin-media-player) · rtl 0/180 · docs
0/813 · check 0 errores en tocados (72 globales de otras sesiones) · prettier
limpio en lo nuevo (los desvíos de command.css/carousel.css ya estaban en
HEAD y no se tocan).
Pendiente de firma (CONTINUE §«PENDIENTE DE FIRMA»): el striped inerte, el
arquetipo en fila+celda, y que soma lea el gap de un token.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
.filter((t) => ['button', 'a', 'input', 'li', 'div', 'tr'].includes(t.tag))
|
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica
El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes
corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro
del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y
no por la memoria de nadie.
`CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola
inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa
concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia
calendar, mandato de Field, el perímetro del knob para la familia tipográfica,
el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes →
después. El plan gana su §8 con el registro completo y corrige su cabecera, que
seguía diciendo que no había nada construido.
Y se commitea el instrumental, porque sin él el protocolo §7 es una intención:
la sonda de computed antes/después, el diff que es el gate (vacío o se para), el
centinela que pregunta si cada token alcanza de verdad, y la captura del stage.
Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen
dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev
server.
Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se
consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el
`animationend` para montar el panel, y si lo congelas mides un popup que no
existe); un token resuelto no se mueve desde `:root` por diseño; un panel
portalado no ve el ámbito del componente; la demo puede tapar el componente que
mides; y un valor centinela igual al real se lee como «no efecto».
Igual las cuatro trampas que costaron un commit cada una, con su guard al lado:
la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los
bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el
diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el
encabezado que un Edit se come al insertar una sección.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
.slice(0, 24);
|
uix(prose): temable — 1 % → 83 %, la costura por ELEMENTO y dos cegueras del instrumento
35 claves nuevas (2 → 37). Censo prose 1 % → **83 %** con `global` a CERO;
global del eje **59 % → 60 %** (1.064 → 1.021 knobs `global` en el catálogo).
Prose estiliza HTML CRUDO (`h1`, `code`, `table`, `kbd`…) a través de
`:where([data-prose] el)`, así que sus «partes» son elementos y la costura va
POR PARTE, como en `result`: `heading-*` (+ `h1-font-weight`, lo único que
distingue al h1 de sus hermanos), `link-*`, `strong-`/`dt-`/`th-font-weight`,
`mark-*`, `blockquote-*`, `code-*`, `pre-*`, `kbd-*`, `media-radius`, `hr-*`,
`table-*`, más `fg`, `fg-muted` y el eje `font-size-{sm,md,lg}`. Los valores
siguen siendo los del canon; lo que faltaba era el knob (PLAN §2-A).
LA ESCALA EDITORIAL NO ENTRA, Y ES DOCTRINA FIRMADA: los `2.25em`, `1.5em`,
`0.9em`, sus interlineados y su tracking son `em`-relativos a propósito —un
`font-size` raíz re-deriva encabezados y márgenes, por eso NO hay una escala
duplicada por talla como en tailwind-typography (su README: «No 5× duplicated
scale»)— y van anotados `/* literal: */`, la válvula de recipe-contract §3. Son
las 21 excepciones firmadas que el censo aprendió a leer esta misma sesión.
Dos literales se cambiaron por su paso de escala, con el mismo valor: el `3px`
del borde-acento del blockquote ES `--border-width-thick`, y los `1px` de kbd,
`hr` y la tabla son `--border-width`.
EL INSTRUMENTO ESTABA CIEGO DE DOS MANERAS, arregladas ANTES de medir:
1. **Sonda y centinela filtran por `data-{c}-*`** y aquí NINGUNA parte lo lleva:
la sonda medía **1 nodo** y el guard daba **27 de 37** tokens por muertos.
Los dos ganan un selector de nodos extra (`EXTRA_NODES` en la sonda,
`extraNodes` en `COMPONENT_OVERRIDES`) — con él, 63 nodos y un gate que
significa algo.
2. **El centinela no leía el color de un borde que no fuera el de arriba.** El
blockquote tiñe su `border-inline-start` y la tabla sus bordes de bloque:
tres tokens vivos leían muertos. Añadidos los cuatro lados lógicos y
`borderBottomColor`.
OCHO ADJUDICACIONES, TODAS MEDIDAS: cinco las tapa el harness de la
documentación —`[data-uix-docs] code|pre` (0,2,0) gana a `:where(…)` (0,0,0),
que es la incidencia ya escrita para `code-block` un piso más arriba— y tres son
elementos que el HTML de ejemplo no usa (`<mark>`, `<dl>`), montados DENTRO del
contenedor real: un `<mark>` a pelo es exactamente lo que esta receta estiliza,
así que el nodo es fiel (la lección de `drag-drop` cumplida).
Gates: sonda antes/después **0 diffs en 11.910 valores** (63 nodos × 7 estados) ·
centinela 29/37 con las ocho adjudicadas · censo --only 83 % · `--names` 37/37
conformes · component-audit PASS · eidos-lint 0 invalid · suite eidos con el
único rojo conocido · rtl:check 0 · docs:check 0 · `check` con 72 errores
globales y 0 en los ficheros tocados. Componentes del ledger re-verificados tras
tocar el guard (text-gradient, chart, result, tree-view, textarea): sin
regresiones ni STALE.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
}, [component, EXTRA_NODES[component] ?? null] as const);
|
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica
El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes
corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro
del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y
no por la memoria de nadie.
`CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola
inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa
concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia
calendar, mandato de Field, el perímetro del knob para la familia tipográfica,
el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes →
después. El plan gana su §8 con el registro completo y corrige su cabecera, que
seguía diciendo que no había nada construido.
Y se commitea el instrumental, porque sin él el protocolo §7 es una intención:
la sonda de computed antes/después, el diff que es el gate (vacío o se para), el
centinela que pregunta si cada token alcanza de verdad, y la captura del stage.
Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen
dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev
server.
Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se
consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el
`animationend` para montar el panel, y si lo congelas mides un popup que no
existe); un token resuelto no se mueve desde `:root` por diseño; un panel
portalado no ve el ámbito del componente; la demo puede tapar el componente que
mides; y un valor centinela igual al real se lee como «no efecto».
Igual las cuatro trampas que costaron un commit cada una, con su guard al lado:
la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los
bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el
diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el
encabezado que un Edit se come al insertar una sección.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
|
|
|
|
const hovered: Record<string, Record<string, string>> = {};
|
|
|
|
|
for (const t of targets) {
|
|
|
|
|
const handle = await page.evaluateHandle(
|
uix(prose): temable — 1 % → 83 %, la costura por ELEMENTO y dos cegueras del instrumento
35 claves nuevas (2 → 37). Censo prose 1 % → **83 %** con `global` a CERO;
global del eje **59 % → 60 %** (1.064 → 1.021 knobs `global` en el catálogo).
Prose estiliza HTML CRUDO (`h1`, `code`, `table`, `kbd`…) a través de
`:where([data-prose] el)`, así que sus «partes» son elementos y la costura va
POR PARTE, como en `result`: `heading-*` (+ `h1-font-weight`, lo único que
distingue al h1 de sus hermanos), `link-*`, `strong-`/`dt-`/`th-font-weight`,
`mark-*`, `blockquote-*`, `code-*`, `pre-*`, `kbd-*`, `media-radius`, `hr-*`,
`table-*`, más `fg`, `fg-muted` y el eje `font-size-{sm,md,lg}`. Los valores
siguen siendo los del canon; lo que faltaba era el knob (PLAN §2-A).
LA ESCALA EDITORIAL NO ENTRA, Y ES DOCTRINA FIRMADA: los `2.25em`, `1.5em`,
`0.9em`, sus interlineados y su tracking son `em`-relativos a propósito —un
`font-size` raíz re-deriva encabezados y márgenes, por eso NO hay una escala
duplicada por talla como en tailwind-typography (su README: «No 5× duplicated
scale»)— y van anotados `/* literal: */`, la válvula de recipe-contract §3. Son
las 21 excepciones firmadas que el censo aprendió a leer esta misma sesión.
Dos literales se cambiaron por su paso de escala, con el mismo valor: el `3px`
del borde-acento del blockquote ES `--border-width-thick`, y los `1px` de kbd,
`hr` y la tabla son `--border-width`.
EL INSTRUMENTO ESTABA CIEGO DE DOS MANERAS, arregladas ANTES de medir:
1. **Sonda y centinela filtran por `data-{c}-*`** y aquí NINGUNA parte lo lleva:
la sonda medía **1 nodo** y el guard daba **27 de 37** tokens por muertos.
Los dos ganan un selector de nodos extra (`EXTRA_NODES` en la sonda,
`extraNodes` en `COMPONENT_OVERRIDES`) — con él, 63 nodos y un gate que
significa algo.
2. **El centinela no leía el color de un borde que no fuera el de arriba.** El
blockquote tiñe su `border-inline-start` y la tabla sus bordes de bloque:
tres tokens vivos leían muertos. Añadidos los cuatro lados lógicos y
`borderBottomColor`.
OCHO ADJUDICACIONES, TODAS MEDIDAS: cinco las tapa el harness de la
documentación —`[data-uix-docs] code|pre` (0,2,0) gana a `:where(…)` (0,0,0),
que es la incidencia ya escrita para `code-block` un piso más arriba— y tres son
elementos que el HTML de ejemplo no usa (`<mark>`, `<dl>`), montados DENTRO del
contenedor real: un `<mark>` a pelo es exactamente lo que esta receta estiliza,
así que el nodo es fiel (la lección de `drag-drop` cumplida).
Gates: sonda antes/después **0 diffs en 11.910 valores** (63 nodos × 7 estados) ·
centinela 29/37 con las ocho adjudicadas · censo --only 83 % · `--names` 37/37
conformes · component-audit PASS · eidos-lint 0 invalid · suite eidos con el
único rojo conocido · rtl:check 0 · docs:check 0 · `check` con 72 errores
globales y 0 en los ficheros tocados. Componentes del ledger re-verificados tras
tocar el guard (text-gradient, chart, result, tree-view, textarea): sin
regresiones ni STALE.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
([kebab, idx, extra]) => {
|
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica
El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes
corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro
del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y
no por la memoria de nadie.
`CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola
inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa
concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia
calendar, mandato de Field, el perímetro del knob para la familia tipográfica,
el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes →
después. El plan gana su §8 con el registro completo y corrige su cabecera, que
seguía diciendo que no había nada construido.
Y se commitea el instrumental, porque sin él el protocolo §7 es una intención:
la sonda de computed antes/después, el diff que es el gate (vacío o se para), el
centinela que pregunta si cada token alcanza de verdad, y la captura del stage.
Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen
dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev
server.
Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se
consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el
`animationend` para montar el panel, y si lo congelas mides un popup que no
existe); un token resuelto no se mueve desde `:root` por diseño; un panel
portalado no ve el ámbito del componente; la demo puede tapar el componente que
mides; y un valor centinela igual al real se lee como «no efecto».
Igual las cuatro trampas que costaron un commit cada una, con su guard al lado:
la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los
bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el
diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el
encabezado que un Edit se come al insertar una sección.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
const root = document.querySelector('[data-uix-stage]') ?? document.body;
|
uix(prose): temable — 1 % → 83 %, la costura por ELEMENTO y dos cegueras del instrumento
35 claves nuevas (2 → 37). Censo prose 1 % → **83 %** con `global` a CERO;
global del eje **59 % → 60 %** (1.064 → 1.021 knobs `global` en el catálogo).
Prose estiliza HTML CRUDO (`h1`, `code`, `table`, `kbd`…) a través de
`:where([data-prose] el)`, así que sus «partes» son elementos y la costura va
POR PARTE, como en `result`: `heading-*` (+ `h1-font-weight`, lo único que
distingue al h1 de sus hermanos), `link-*`, `strong-`/`dt-`/`th-font-weight`,
`mark-*`, `blockquote-*`, `code-*`, `pre-*`, `kbd-*`, `media-radius`, `hr-*`,
`table-*`, más `fg`, `fg-muted` y el eje `font-size-{sm,md,lg}`. Los valores
siguen siendo los del canon; lo que faltaba era el knob (PLAN §2-A).
LA ESCALA EDITORIAL NO ENTRA, Y ES DOCTRINA FIRMADA: los `2.25em`, `1.5em`,
`0.9em`, sus interlineados y su tracking son `em`-relativos a propósito —un
`font-size` raíz re-deriva encabezados y márgenes, por eso NO hay una escala
duplicada por talla como en tailwind-typography (su README: «No 5× duplicated
scale»)— y van anotados `/* literal: */`, la válvula de recipe-contract §3. Son
las 21 excepciones firmadas que el censo aprendió a leer esta misma sesión.
Dos literales se cambiaron por su paso de escala, con el mismo valor: el `3px`
del borde-acento del blockquote ES `--border-width-thick`, y los `1px` de kbd,
`hr` y la tabla son `--border-width`.
EL INSTRUMENTO ESTABA CIEGO DE DOS MANERAS, arregladas ANTES de medir:
1. **Sonda y centinela filtran por `data-{c}-*`** y aquí NINGUNA parte lo lleva:
la sonda medía **1 nodo** y el guard daba **27 de 37** tokens por muertos.
Los dos ganan un selector de nodos extra (`EXTRA_NODES` en la sonda,
`extraNodes` en `COMPONENT_OVERRIDES`) — con él, 63 nodos y un gate que
significa algo.
2. **El centinela no leía el color de un borde que no fuera el de arriba.** El
blockquote tiñe su `border-inline-start` y la tabla sus bordes de bloque:
tres tokens vivos leían muertos. Añadidos los cuatro lados lógicos y
`borderBottomColor`.
OCHO ADJUDICACIONES, TODAS MEDIDAS: cinco las tapa el harness de la
documentación —`[data-uix-docs] code|pre` (0,2,0) gana a `:where(…)` (0,0,0),
que es la incidencia ya escrita para `code-block` un piso más arriba— y tres son
elementos que el HTML de ejemplo no usa (`<mark>`, `<dl>`), montados DENTRO del
contenedor real: un `<mark>` a pelo es exactamente lo que esta receta estiliza,
así que el nodo es fiel (la lección de `drag-drop` cumplida).
Gates: sonda antes/después **0 diffs en 11.910 valores** (63 nodos × 7 estados) ·
centinela 29/37 con las ocho adjudicadas · censo --only 83 % · `--names` 37/37
conformes · component-audit PASS · eidos-lint 0 invalid · suite eidos con el
único rojo conocido · rtl:check 0 · docs:check 0 · `check` con 72 errores
globales y 0 en los ficheros tocados. Componentes del ledger re-verificados tras
tocar el guard (text-gradient, chart, result, tree-view, textarea): sin
regresiones ni STALE.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
const all = [...root.querySelectorAll<HTMLElement>('*')].filter(
|
|
|
|
|
(n) =>
|
|
|
|
|
[...n.attributes].some((a) => a.name.startsWith(`data-${kebab}`)) ||
|
|
|
|
|
(extra ? n.matches(extra as string) : false)
|
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica
El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes
corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro
del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y
no por la memoria de nadie.
`CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola
inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa
concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia
calendar, mandato de Field, el perímetro del knob para la familia tipográfica,
el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes →
después. El plan gana su §8 con el registro completo y corrige su cabecera, que
seguía diciendo que no había nada construido.
Y se commitea el instrumental, porque sin él el protocolo §7 es una intención:
la sonda de computed antes/después, el diff que es el gate (vacío o se para), el
centinela que pregunta si cada token alcanza de verdad, y la captura del stage.
Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen
dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev
server.
Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se
consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el
`animationend` para montar el panel, y si lo congelas mides un popup que no
existe); un token resuelto no se mueve desde `:root` por diseño; un panel
portalado no ve el ámbito del componente; la demo puede tapar el componente que
mides; y un valor centinela igual al real se lee como «no efecto».
Igual las cuatro trampas que costaron un commit cada una, con su guard al lado:
la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los
bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el
diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el
encabezado que un Edit se come al insertar una sección.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
);
|
|
|
|
|
return all[idx as number] ?? null;
|
|
|
|
|
},
|
uix(prose): temable — 1 % → 83 %, la costura por ELEMENTO y dos cegueras del instrumento
35 claves nuevas (2 → 37). Censo prose 1 % → **83 %** con `global` a CERO;
global del eje **59 % → 60 %** (1.064 → 1.021 knobs `global` en el catálogo).
Prose estiliza HTML CRUDO (`h1`, `code`, `table`, `kbd`…) a través de
`:where([data-prose] el)`, así que sus «partes» son elementos y la costura va
POR PARTE, como en `result`: `heading-*` (+ `h1-font-weight`, lo único que
distingue al h1 de sus hermanos), `link-*`, `strong-`/`dt-`/`th-font-weight`,
`mark-*`, `blockquote-*`, `code-*`, `pre-*`, `kbd-*`, `media-radius`, `hr-*`,
`table-*`, más `fg`, `fg-muted` y el eje `font-size-{sm,md,lg}`. Los valores
siguen siendo los del canon; lo que faltaba era el knob (PLAN §2-A).
LA ESCALA EDITORIAL NO ENTRA, Y ES DOCTRINA FIRMADA: los `2.25em`, `1.5em`,
`0.9em`, sus interlineados y su tracking son `em`-relativos a propósito —un
`font-size` raíz re-deriva encabezados y márgenes, por eso NO hay una escala
duplicada por talla como en tailwind-typography (su README: «No 5× duplicated
scale»)— y van anotados `/* literal: */`, la válvula de recipe-contract §3. Son
las 21 excepciones firmadas que el censo aprendió a leer esta misma sesión.
Dos literales se cambiaron por su paso de escala, con el mismo valor: el `3px`
del borde-acento del blockquote ES `--border-width-thick`, y los `1px` de kbd,
`hr` y la tabla son `--border-width`.
EL INSTRUMENTO ESTABA CIEGO DE DOS MANERAS, arregladas ANTES de medir:
1. **Sonda y centinela filtran por `data-{c}-*`** y aquí NINGUNA parte lo lleva:
la sonda medía **1 nodo** y el guard daba **27 de 37** tokens por muertos.
Los dos ganan un selector de nodos extra (`EXTRA_NODES` en la sonda,
`extraNodes` en `COMPONENT_OVERRIDES`) — con él, 63 nodos y un gate que
significa algo.
2. **El centinela no leía el color de un borde que no fuera el de arriba.** El
blockquote tiñe su `border-inline-start` y la tabla sus bordes de bloque:
tres tokens vivos leían muertos. Añadidos los cuatro lados lógicos y
`borderBottomColor`.
OCHO ADJUDICACIONES, TODAS MEDIDAS: cinco las tapa el harness de la
documentación —`[data-uix-docs] code|pre` (0,2,0) gana a `:where(…)` (0,0,0),
que es la incidencia ya escrita para `code-block` un piso más arriba— y tres son
elementos que el HTML de ejemplo no usa (`<mark>`, `<dl>`), montados DENTRO del
contenedor real: un `<mark>` a pelo es exactamente lo que esta receta estiliza,
así que el nodo es fiel (la lección de `drag-drop` cumplida).
Gates: sonda antes/después **0 diffs en 11.910 valores** (63 nodos × 7 estados) ·
centinela 29/37 con las ocho adjudicadas · censo --only 83 % · `--names` 37/37
conformes · component-audit PASS · eidos-lint 0 invalid · suite eidos con el
único rojo conocido · rtl:check 0 · docs:check 0 · `check` con 72 errores
globales y 0 en los ficheros tocados. Componentes del ledger re-verificados tras
tocar el guard (text-gradient, chart, result, tree-view, textarea): sin
regresiones ni STALE.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
[component, t.i, EXTRA_NODES[component] ?? null] as const
|
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica
El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes
corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro
del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y
no por la memoria de nadie.
`CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola
inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa
concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia
calendar, mandato de Field, el perímetro del knob para la familia tipográfica,
el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes →
después. El plan gana su §8 con el registro completo y corrige su cabecera, que
seguía diciendo que no había nada construido.
Y se commitea el instrumental, porque sin él el protocolo §7 es una intención:
la sonda de computed antes/después, el diff que es el gate (vacío o se para), el
centinela que pregunta si cada token alcanza de verdad, y la captura del stage.
Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen
dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev
server.
Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se
consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el
`animationend` para montar el panel, y si lo congelas mides un popup que no
existe); un token resuelto no se mueve desde `:root` por diseño; un panel
portalado no ve el ámbito del componente; la demo puede tapar el componente que
mides; y un valor centinela igual al real se lee como «no efecto».
Igual las cuatro trampas que costaron un commit cada una, con su guard al lado:
la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los
bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el
diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el
encabezado que un Edit se come al insertar una sección.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
);
|
|
|
|
|
const el = handle.asElement();
|
|
|
|
|
if (!el) continue;
|
|
|
|
|
try {
|
|
|
|
|
await el.hover({ timeout: 1500 });
|
|
|
|
|
await page.waitForTimeout(400);
|
|
|
|
|
const row = await el.evaluate((n, keys) => {
|
|
|
|
|
const cs = getComputedStyle(n as HTMLElement);
|
|
|
|
|
const attrs = [...(n as HTMLElement).attributes]
|
|
|
|
|
.filter((a) => a.name.startsWith('data-') && !a.name.startsWith('data-event'))
|
|
|
|
|
.map((a) => (a.value ? `${a.name}=${a.value}` : a.name))
|
|
|
|
|
.sort()
|
|
|
|
|
.join('|');
|
|
|
|
|
const out: Record<string, string> = { __key: attrs };
|
|
|
|
|
for (const k of keys as string[]) out[k] = cs[k as keyof CSSStyleDeclaration] as string;
|
|
|
|
|
return out;
|
|
|
|
|
}, PROPS);
|
|
|
|
|
hovered[`${t.i}:${row.__key}`] = row;
|
|
|
|
|
} catch {
|
|
|
|
|
/* not hoverable (offscreen / covered) — skip */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
result.hover = hovered;
|
|
|
|
|
|
|
|
|
|
await browser.close();
|
|
|
|
|
writeFileSync(out, JSON.stringify(result, null, '\t'), 'utf8');
|
|
|
|
|
const count = Object.keys(result.rest as object).length;
|
|
|
|
|
console.log(`probe ${component}: ${count} nodes at rest, ${Object.keys(hovered).length} hovered`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
main();
|