astra
alpha-0.1-background
alpha-0.1-dir-prefs
alpha-0.1-sec-dom
menubar-v4-safe
active-uix
morfo-runtime
morfo-driven-soma
semantuix
glm-5
main
sium-v1.0
${ noResults }
23 Commits (fb2e12ea9fa42c4b01dbcef544a96078289fe238)
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
b8eccc1c4f |
uix(toast): temable — 98 % → 100 %, y el plano se lleva la TARJETA ENTERA
El contrato ya tenía sus 85 claves y `global` era CERO: no se acuña ninguna.
Lo único fuera de alcance era un literal, y era identidad.
Lo que entra:
- `opacity: 1` de la tarjeta firmado con su anotación `/* literal: */`
(recipe-contract §3): es el polo EN REPOSO del par abierto/cerrado, y el
que lleva tokens es el otro (`closed-offset`, `closed-scale`). «Presente
del todo» no tiene un segundo valor que sostener. 98 % → 100 %.
- Guard R-5.4: entrada nueva en `COMPONENT_OVERRIDES` que dispara la tarjeta
con el botón del demo — y que DELIBERADAMENTE no pone `openMarker`. Una
tarjeta se auto-descarta a los 5 s, así que «¿ya está abierta?» no tiene
respuesta útil: con `openMarker` tres corridas sobre código idéntico
discreparon en QUÉ token leía muerto. Sin él, el guard cae al marcador por
defecto (una parte que este componente no tiene) y dispara una tarjeta
nueva antes de CADA token: **51/85 en tres corridas seguidas**, determinista.
- Ledger: 34 adjudicaciones, ninguna retirada, todas MEDIDAS.
- La sonda aprende a disparar el toast (`DEMO_VARIANTS`): medía UN nodo (el
viewport) porque un toast no EXISTE hasta que algo lo lanza. Ahora 8.
EL HALLAZGO — §12.9 con una cara nueva y con coste de PRODUCTO:
`toast-item.svelte` sella `data-depth='overlay'`, y el plano declara
`background`, el ATAJO `border` y `box-shadow` con la misma especificidad
(0,1,0) y más tarde. Medido sobre la tarjeta real con transiciones congeladas:
escribir `--toast-{tono}-surface` / `-border`, `--toast-provider-border-width`,
`--toast-accent-width` o `--toast-provider-shadow` no mueve NADA; escribir
`--depth-overlay-surface` / `-border` / `-shadow` y `--border-width` en la
misma raíz lo repinta todo. Son 21 claves de 85, y a diferencia de `tooltip`
esto se VE: un toast `risk` y uno `fulfill` visten la misma tarjeta gris, y la
franja de acento —un `border-inline-start` que el atajo posterior pisa en los
cuatro lados— no aparece nunca en reposo.
Matiz que sólo aparece midiendo: los seis `{tono}-accent` NO están muertos.
Bajo `[data-loading]` la regla del pulso es (0,2,0) y su `@keyframes` gana a
toda declaración normal — 100 fotogramas del pulso real: sin escribir, el
color interpolado corre en oklab L 0.5032..0.8514; con el token en rgb(1,2,3),
0.0823..0.5032. La misma clave miente en reposo y dice la verdad en animación.
§12.9 sigue SIN FIRMAR, así que las 21 se ADJUDICAN con `PLANE_SUPERSEDED` y
la incidencia se registra en `next-features.md` §12. No se toca desde este eje.
Las 13 restantes: la pareja de transición (el guard congela `transition`),
`closed-offset` / `closed-scale` (estado de salida + `transform`, que el guard
no fotografía), los tres del anillo de foco de la acción (el guard hace
`blur`), los cinco `{tono}-action-hover-bg` (el guard estampa el tono en su
pasada estática y no en la de hover) y `provider-max-width`, cuyo centinela de
1234px se capa contra el `min(100%, …)` — medido desde el otro lado, 120px
encoge la tarjeta de 420 a 280.
Censo: 98 % → 100 % (45 knobs · 45 públicos · 0 globales · 0 privados ·
1 excepción firmada). Global 68 %, al 100 % 42 → 45. Sin cambios en
`recipes/base.ts` ni en `generated/base.css`.
Diff de computed: **0 diffs sobre 1.760 valores en 7 estados** (8 nodos en
reposo, 7 en hover). Capturas 2× de la tarjeta byte a byte idénticas — y van
por un script propio, porque su viewport es `position: fixed` y cae fuera del
recorte del escenario que usa `__shot`.
Más README con su sección «Talla y tema», pestaña `Tokens` en la demo (85
claves, verificada en el navegador) y ficha con veredicto §5.
Guards: centinela 51/85 verde (exit 0, sin STALE) · `component-audit --only
toast` PASS · `eidos-lint toast` 0 invalid (24 morfo-backed, 6 eidos-only) ·
`vitest run src/uix/eidos` 35/36 (el rojo conocido es `skin-media-player`) ·
`rtl:check` 0 · `docs:check` 0 · `check` COMPLETED sin un solo error en
ficheros de toast · prettier limpio sobre el contenido normalizado a LF.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
10832bef4a |
fix(theming): la sonda no congelaba NADA — 18 diffs fantasma sobre código idéntico
El gate del protocolo §7 es «el diff de computed es CERO», y la sonda que lo mide no inyectaba ninguna congelación: una propiedad en transición devolvía el valor VIEJO y una bajo animación en BUCLE devolvía donde el bucle estuviera. Ruido puro sobre el único artefacto que prueba que un commit no mueve píxel. Lo destapó chat-typing, cuyos tres puntos montan el bucle pulse del sistema: 18 diffs de opacity corriendo la sonda DOS VECES sobre el mismo código. Con la congelación de transition Y animation antes de cada instantánea: 0 diffs sobre 1.568 valores. Un token que SEA la motion lee congelado y se mide a mano sin congelar, que es el idioma que el ledger ya usa para transition-duration / -ease. Queda registrado en §13 lo que NO arreglo aquí: el guard congela transition pero no animation, y cambiarlo obliga a re-verificar el ledger entero porque los tokens que son la animación pasarían a leer muertos. Misma clase que «el guard no fotografía transform», mismo bloqueo. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
d39147c119 |
uix(chat-typing): temable — 89 % → 100 %, con UNA clave y siete de sus ocho ya vivas
Protocolo PLAN-theming §7 sobre `chat-typing`. Censo 89 % → **100 %**, contrato 7 → **8** claves, centinela **8/8** con **cero adjudicadas**. ## Lo que entra: una clave, y es la COSTURA `dot-radius` (`var(--radius-full)`), el único knob que leía el sistema a pelo. El valor sigue siendo el del sistema; el knob pasa a ser del componente. **El nombre sale del CATÁLOGO, no del generador.** La ficha proponía `indicator-radius` —nombra por el primer selector de la regla, `[data-chat-typing-indicator] > span`— cuando el nodo que PINTA es el punto y el contrato ya habla `dot-size` / `dot-gap` / `dot-fg`. Es el defecto de `tree-view` («root-bg» para una guía de indentación) en pequeño. ## Lo que el instrumento no veía: 3 nodos de 8 Los tres puntos son `> span` DESNUDOS del indicador —sin `data-chat-typing-*`, la clase de la pastilla del separador de `chat-log`— y la demo arranca con NADIE escribiendo, así que `data-state='idle'` esconde a todos los hijos (presencia = visibilidad, N-7). Con el selector extra y un typer encendido por `prepareWith`: **8 nodos**, y el centinela ya daba **7/7 antes de tocar el código**. ## El diff de computed NO cerró en cero, y es RUIDO reproducido 18 diffs, los 18 de `opacity` y los 18 en los tres `span[data-animation-style=pulse]`. Sus puntos montan el loop `pulse` del SISTEMA, que anima `opacity` sin parar, así que cada lectura cae en una fase distinta: corriendo la sonda dos veces sobre el MISMO código salen los mismos 18 (control `after` vs `after2`). Ninguna otra propiedad se mueve, la clave tocada es `border-radius`, y las capturas 2x antes/después son **idénticas byte a byte** (1.568 valores · 7 estados · 8 nodos). ## Guards censo `--only chat-typing` 100 % (global 68 %, no baja; los que llegan al 100 % pasan de 36 a 42) · `component-audit` PASS · `eidos-lint` 0 invalid (8 morfo-backed, 0 eidos-only) · `vitest run src/uix/eidos` sin rojos nuevos (el único vivo es el conocido `skin-media-player`) · `rtl:check` 0 · `docs:check` 0 · `check` COMPLETED sin un solo error del componente · prettier limpio sobre el contenido normalizado a LF. README con su sección «Talla y tema», demo con pestaña `Tokens` (8 claves, verificada en navegador) y ficha con su veredicto §5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
f882e1b9d9 |
uix(chat-message): temable — 84 % → 100 %, y las TREINTA muertas eran ejes que la demo no enciende
Protocolo PLAN-theming §7 sobre `chat-message`, el mayor de la familia chat.
Censo 84 % → **100 %**, contrato 75 → **81** claves, centinela **77/81** con 4
adjudicadas. El default NO se mueve: diff de computed VACÍO (4.192 valores · 8
estados · 16 nodos) y capturas 2x antes/después **idénticas byte a byte**, en
reposo y con la barra tapback abierta.
## Otra vez, lo caro fue VER
El guard leía **45/75** antes de tocar una línea de código. Una fila de chat es
una PILA de superficies opt-in (reply citado, read-by, estado de entrega,
mención) y de ejes de PRESENTACIÓN (`run`, `direction`, `emphasized`) que la
demo arranca en su posición más pobre: `solo` + `in`, sin ninguna de las cuatro.
Con las superficies encendidas por `prepareWith` y los cinco ejes barridos como
PRODUCTO: **71/75 antes de acuñar nada**. La sonda pasó de 12 nodos a 16 en
reposo, y a 24 con el panel abierto.
## Lo que entra (6 claves, la COSTURA de los seis globales)
`focus-radius` · `mentioned-fg` · `reply-fg` · `reaction-fg` ·
`reaction-inner-gap` · `quick-react-radius`. El valor sigue siendo el del
sistema; el knob pasa a ser del componente.
**Los nombres salen del CATÁLOGO, no del generador.** La ficha proponía `radius`
(colisiona con `emphasize-radius`, que ES la esquina en reposo de la MISMA
fila), `bubble-fg` (el knob es de la MENCIÓN: `mentioned-bg` y
`mentioned-accent` ya existían), `reaction-width` + `reaction-height` para el
glifo (`reaction-height` ya existe y es la altura del CHIP) y
`quick-reactions-width` para un `max-content` que es identidad.
## El caso que obligó a decidir: `reaction-gap` ya existía con otro sentido
La receta la pinta en la fila `reactions` — el hueco ENTRE chips — mientras el
knob nuevo es el ritmo INTERNO del chip, el emoji y su cuenta. Mismo valor
(`--space-1`), dos papeles: un chip más denso no es una fila más apretada.
Reusar la clave habría cambiado en silencio lo que mueve un tema que ya la usa,
así que entra como `reaction-inner-gap`. El resto del bloque `{parte}-gap` de
esta receta (`meta-gap`, `read-by-gap`, `header-gap`, `actions-gap`) significa
«dentro de la parte»: la anómala es la vieja, y renombrarla es churn con riesgo.
## Lo que SALE: una regla que no ha pintado nunca
`[data-chat-message-reaction] svg, [data-chat-message-reaction-add] svg` fijaba
`1.1em` en los dos ejes «porque un svg de sólo viewBox computa 0x0». El glifo
por defecto es el `Icon` compuesto, que emite `style="width:
var(--icon-size-sm); height: ..."` EN LÍNEA, y un estilo en línea gana a todo
selector. Medido: **16 px** (el paso `sm` del icono), no los 15,4 px que `1.1em`
daría sobre los 14 px heredados. La otra mitad del selector no tiene nodo: los
chips llevan un emoji de texto. **Retirada: 0 diffs.** Tercera vez en la familia
(chat-log y chat-composer, ayer).
## Identidades firmadas (4, fuera del ratio)
`inline-size: max-content` de la barra tapback (la barra ES su fila de emoji),
`line-height: 1` de la celda (la celda es la caja del glifo) y los dos
`opacity: 1` de la pastilla de acciones (el final del fundido 0 → 1).
## Adjudicadas (4) — las cuatro son límites del INSTRUMENTO, medidas a mano
- `bubble-tail-radius` — el guard fotografía UNA esquina (`borderTopLeftRadius`)
y la cola es la de ABAJO. Medido en las 8 combinaciones dirección x run:
4 px → 1234 px en `solo` y `last`, en las dos direcciones. Su hermana
`bubble-radius-run` lee viva porque `middle` aplana la esquina START, que sí
está en la lista.
- `emphasize-duration` — ES la transición que el guard congela para medir todo
lo demás. Sin congelar: 0,24 s → 4,321 s.
- `focus-radius` — la fila sólo es tabulable compuesta DENTRO de un Feed (soma
fusiona la identidad de `Feed.Article` cuando hay Feed ancestro) y su propia
demo la monta suelta: `:focus-visible` no tiene nodo ahí. Medido en la demo de
`chat-log`, donde las filas SÍ son artículos de feed (`tabindex=0`): reposo
6 px, enfocada 4 px → 1234 px, por foco programático y por recorrido con Tab.
- `quick-react-hover-scale` — `transform` no está en la lista de propiedades del
guard (punto ciego ya adjudicado en background / rating-group / card) y la
celda es un botón PORTALADO con regla `:hover`. Con puntero real:
`matrix(1.18…)` → `matrix(7.77…)`.
## Un defecto real medido y NO corregido aquí (mueve píxel)
El chip de añadir reacción lleva también `data-popover-trigger`, y
`[data-popover-trigger]:not([data-archetype='field-trigger'])` pesa (0,2,0)
contra los (0,1,0) de `[data-chat-message-reaction-add]`: su fondo, borde,
radio, tinta, altura, padding y tamaño de letra los pinta `popover.css`. Se ve
en la captura — los dos chips de reacción son píldoras redondas y el de añadir
es un cuadrado gris. Los `reaction-*` no mienten (alcanzan sobre los chips
normales), así que no hay nada que retirar: es una decisión de diseño. Clase
`gradient-picker` / `emoji-picker`. Queda REGISTRADO, no tocado (D-TH.5).
## Guards
censo `--only chat-message` 100 % (global 68 %, no baja) · `component-audit`
PASS · `eidos-lint` 0 invalid (37 morfo-backed / 41 eidos-only) · `vitest run
src/uix/eidos` sin rojos nuevos (el único vivo es el conocido
`skin-media-player`) · `rtl:check` 0 · `docs:check` 0 · `check` COMPLETED sin un
solo error del componente · prettier limpio sobre el contenido normalizado a LF.
README con su sección «Talla y tema», demo con pestaña `Tokens` (81 claves,
verificada en navegador) y ficha con su veredicto §5.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
5fd2fcdd86 |
uix(fab): temable — 88 % → 100 %, y el modo va DETRÁS, no delante
Le faltaban DOS knobs y los dos son del pill extendido: el FAB circular no declara ni `padding-inline` ni `gap`, pero la regla `[data-button][data-fab][data-extended]` sí, y leía `var(--space-5)` / `var(--space-2)` A PELO. La COSTURA del PLAN §2-A: el valor sigue siendo del sistema, el knob pasa a ser del componente. `global` a CERO. Claves que entran (2, defaults verbatim): - `--fab-padding-inline-extended: var(--space-5)` - `--fab-gap-extended: var(--space-2)` El nombre lleva el cualificador DETRÁS. `extended` es un MODO del FAB, no un estado interactivo (el vocabulario cerrado es hover · active · selected · disabled · checked · open · focus · invalid · current), así que cae en la otra mitad de la frase firmada en D-TH.6 —«delante lo interactivo, detrás lo dimensional y contextual»—, igual que `sidebar.width-icon` y `radio-group.gap-vertical`. Un `--fab-padding-inline` a secas habría prometido a TODO FAB un padding que sólo tiene el pill. Lo que NO se acuña, y por qué el 100 % no lleva las tres claves de la §4: el `font-size: 1em` del glifo es identidad de contexto y ya lleva su anotación `/* literal: */` (fuera del ratio por recipe-contract §3), y la colocación flotante es de la capa `viewport-placement`, cuya ranura de override Fab escribe sin acuñar vocabulario paralelo (`--fab-offset` / `--fab-z` se retiraron por eso en 2026-08-15). La composición NO le quita nada, y valía la pena medirlo: el mismo `<button>` lleva `data-button` Y `data-fab`, la forma exacta del hallazgo de `gradient-picker` (36 claves muertas bajo `popover.css`). Aquí no ocurre por construcción — las reglas dimensionales del FAB casan `[data-button][data-fab][data-fab-size]` (0,3,0) contra los (0,2,0) de `[data-button][data-size]`, así que ganan sea cual sea el orden de carga de los chunks. Medido: padding computado 20px = `--space-5`, no el del Button. Tampoco lleva `data-depth`, así que §12.9 no le aplica. Censo: 16 knobs · público 14 → 16 · global 2 → 0 · excepción 1 (fuera del ratio) · 88 % → 100 %. Contrato 10 → 12 claves. Diff de computed: VACÍO en las dos formas. 416 valores × 7 estados en el escenario por defecto (circular, sonda estándar) y 188 valores × 4 tallas + hover en el pill extendido, con una sonda de un solo uso porque la demo arranca circular. Capturas 2× antes/después byte a byte IDÉNTICAS en las dos. No se tocó ninguna regla `:hover`, así que el paso 12 no aplica. Centinela R-5.4: **11/12**, 1 adjudicada. `lift` es la laguna del INSTRUMENTO, no del token: la receta lo consume con la propiedad `translate` (no `transform`, para no pisar el press-squeeze del Button) dentro de una regla `:hover`, y el guard ni fotografía `translate` —la misma laguna ya adjudicada tres veces sobre `transform` en background / rating-group / card— ni pasa el ratón en su pasada estática. Medido a mano con puntero real: `--fab-lift: 1234px` escrito ANTES de entrar da `translate: 0px -1234px` a los 300 ms → alcanza. Y trae una trampa nueva: el centinela SE AUTO-CANCELA — un lift de ese tamaño se lleva el nodo de debajo del cursor, `:hover` cae y el valor vuelve a `none` hacia t+900 ms. Añadirle una pasada de hover al guard no lo mediría. El instrumento aprendió dos cosas de este componente, las dos por el §2 «cuenta los nodos»: un FAB es UN nodo, así que su segunda superficie pintada —el hueco de icono del Button compuesto— entra por `EXTRA_NODES` (1 → 2 nodos); y su eje de talla es `data-fab-size`, NO `data-size`, así que el barrido genérico del guard no sellaba nada. Con el barrido cartesiano `data-fab-size` × `data-extended` el guard pasa de **3/12 a 11/12** sin adjudicar una sola clave de más. Los dos interruptores son entradas por componente: no tocan a nadie más y no exigen re-verificar el ledger. Guards: censo `--only fab` 100 % (global 68 %, no baja) · `--names` 12 conformes, 0 desviadas · `component-audit --only fab` PASS · `eidos-lint fab` 0 invalid (14 eidos-only) · `vitest run src/uix/eidos` 35/36 ficheros, el único rojo es el conocido `skin-media-player` en `lint.test.ts` · `rtl:check` 0 · `docs:check` 0 · `npm run check` COMPLETED, cero errores en los ficheros tocados (los 72 globales son de otras sesiones) · prettier limpio sobre el contenido normalizado a LF. README con su sección «Talla y tema» (tabla derivada del contrato), pestaña `Tokens` en la demo verificada en navegador (12 claves, cero errores de página) y veredicto §5 escrito en la ficha. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
71a097ddca |
uix(chat-composer): temable — 79 % → 100 %, y file-upload le gana el disabled desde FUERA
Protocolo PLAN-theming §7 sobre `chat-composer`. Censo 79 % → **100 %**,
contrato 31 → **33** claves, centinela **29/33** con 4 adjudicadas. El default
no se mueve.
## La mitad de su contrato no la montaba la demo
El guard leía 14 muertas de 31, y once eran la barra de contexto (`context-*`,
opt-in tras un chip) más la bandeja de adjuntos (`attachments-gap`, opt-in tras
un botón). Con las dos encendidas por `prepareWith`: **29/33 antes de tocar el
código**. La sonda mide ya 9 nodos, con los dos glifos.
## Lo que entra: DOS claves donde el generador pedía cuatro
`context-close-size` y `context-close-radius`. El chip de descarte es un
control CUADRADO, así que es UNA clave para los dos ejes — la forma que
`send-size` ya usa en esta misma receta. El aviso del generador ("`var(--size-
xs-control-height)` / `1em`, dos valores") era un falso conflicto: metía en el
mismo saco la CAJA (26 px) y su GLIFO.
## Lo que SALE: cuatro declaraciones que no han pintado nunca
`[data-chat-composer-context-close] > svg` (`1em`) y
`[data-chat-composer-send] > svg` (`1.1em`). Los dos glifos son `Icon`
compuestos y el `Icon` emite `style="width: var(--icon-size-{k}); height: ..."`
INLINE: ningún selector gana a un estilo en línea. Medido — el cierre pinta
14 px (`--icon-size-xs`) contra los 13,3 px que daría `1em`, y el envío 16 px
(`--icon-size-sm`) contra los 14,7 px de `1.1em`. Retiradas: **0 diffs**.
Misma lección que `chat-log` esta misma tarde, con dos glifos en vez de uno.
## Y una colisión ENTRE RECETAS, que no es del instrumento
`--chat-composer-disabled-opacity` no alcanza, y no por un hábito del guard:
`file-upload` barre DESCENDIENTES. `[data-file-upload] [data-disabled]` pesa
(0,2,0) — lo mismo que `[data-chat-composer-send][data-disabled]` — y su receta
se emite MÁS TARDE, así que gana sobre cualquier nodo deshabilitado dentro de
un FileUpload. Que es exactamente donde vive el composer en su composición
DOCUMENTADA (su propio README y su demo lo envuelven en uno).
Medido sobre los nodos REALES quitando el atributo del ancestro: fuera del
envoltorio alcanza (0.4 → 0.123 en el send Y en el shell). Los dos defaults son
`--opacity-disabled`, así que la colisión es INVISIBLE hasta que un tema mueva
uno de los dos. **No se arregla aquí** —tocar `file-upload` es cascadear—: se
adjudica con su medida.
Las otras tres adjudicaciones son hábitos conocidos del guard: `focus-border`
(sólo bajo `:focus-within`, y el guard blurea tras abrir; enfocado el textarea
real, `color(srgb 0.745 0.577 0.894 / 0.48)` → `rgb(1, 2, 3)`) y el par
`transition-duration` / `-ease`, que ES la transición que el guard congela para
poder medir todo lo demás (0,12 s → 4,321 s sin congelar).
## Identidad firmada (1)
El `inline-size: 100%` del textarea, que ocupa su celda de la rejilla.
## El diff de computed y su residuo, medido
2.208 valores, 8 estados, 9 nodos. Idénticos salvo 2-4 de
`[data-chat-composer-attach]` `blockSize`, que flota entre 30 px y 36 px —
**y flota igual corriendo dos veces el MISMO código** (control after vs after2:
2 diffs, mismo nodo, misma propiedad). La causa, medida: en una recarga de cada
ocho el `Button size="sm" variant="ghost"` que la demo compone en esa ranura
computa 36 px **y fondo `oklch(0.5556 0.1829 305.86)`** —el sólido primario—
con sus `data-size` y `data-variant` puestos: las reglas de talla y variante de
`button` llegan tarde en el servidor de desarrollo. Un nodo que esta receta no
dimensiona, y una carrera de CSS del dev server. Es también lo que salió en la
captura "antes" (el clip sujetapapeles relleno), y por eso la de "después" se
tomó cuatro veces: las cuatro idénticas entre sí.
## Guards
censo `--only chat-composer` 100 % (global 68 %, no baja) · `component-audit`
PASS · `eidos-lint` 0 invalid · `vitest run src/uix/eidos` sin rojos nuevos (el
único vivo es el conocido `skin-media-player`) · `rtl:check` 0 · `docs:check` 0
· `check` sin un solo error del componente · prettier limpio sobre el contenido
normalizado a LF.
README con su sección «Talla y tema», demo con pestaña `Tokens` verificada en
navegador, ficha con su veredicto §5 y registro en PLAN §8.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
fd8f01bace |
uix(chat-log): temable — 77 % → 100 %, y el instrumento daba DIECISÉIS falsos muertos de 31
Protocolo PLAN-theming §7 sobre `chat-log`. Censo 77 % → **100 %**, contrato
31 → **35** claves, centinela **34/35** con 1 adjudicada. El default NO se
mueve: diff de computed VACÍO (1.920 valores · 7 estados · 9 nodos), capturas
2x antes/después idénticas.
## Lo caro no fue acuñar: fue VER
El guard leía **15/31** antes de tocar una línea de código, y las dieciséis
"muertas" estaban las dieciséis VIVAS. Tres cegueras, ninguna del componente:
1. **El viewport que scrollea es del componente COMPUESTO.** El log pinta su
`padding-inline` / `padding-block` sobre `[data-virtual-list-viewport]`, un
nodo sin un solo `data-chat-log-*`: fuera del filtro de la sonda y del
guard. Cuarta forma del mismo punto ciego (prose · navigation-menu ·
waveform).
2. **La demo no montaba TRES de las seis partes.** Separador de día y divisor
de no-leídos no existían en el escenario, y la fila de evento vivía en el
índice 0 de una lista virtualizada anclada al FINAL — fuera de la ventana.
Once claves sin nodo que pintar. La demo los monta ya cerca del final
(instrumento: lo que la demo no monta, nadie lo mide).
3. **La pastilla del separador es un `> span` sin atributo** — seis claves más.
Con las tres arregladas, **31/31 antes de acuñar nada**.
## Lo que entra (4 claves, la COSTURA de los cuatro globales)
`divider-padding-block` · `event-radius` · `pill-gap` · `to-latest-radius`.
El valor sigue siendo el del sistema; el knob pasa a ser del componente.
Los nombres salen del CATÁLOGO, no del generador: la ficha proponía
`divider-unread-padding-block` y `pill-new-gap` —nombra por el atributo de la
parte— cuando el contrato lleva `divider-fg` / `-line` / `-font-size` / `-gap`
y `pill-bg` / `-fg` / `-radius`. La familia manda.
## Lo que SALE: una regla que no ha pintado nunca
`[data-chat-log-to-latest] svg { block-size: 1em; inline-size: 1em }` no puede
ganar: el `Icon` compuesto emite `style="width: var(--icon-size-sm); height:
..."` EN LÍNEA, y un estilo en línea gana a todo selector. El glifo mide 16 px
(paso `sm` del icono), no los 14 px que `1em` daría sobre la fuente heredada
del botón. **Retirada: 0 diffs**, que es la prueba de que estaba muerta. Es la
clase `carousel.item-gap` / `drag-drop.preview-z`, con una vuelta más: aquí el
inline lo escribe un COMPONENTE COMPUESTO, no soma.
## Identidades firmadas (3, fuera del ratio)
`block-size: 100%` del shell y del viewport (el log ocupa la caja que la app le
da) e `inline-size: fit-content` de la pill (la dimensiona su contenido).
## Adjudicada (1)
`event-radius` — sólo pinta bajo `:focus-visible` y el guard hace blur tras
abrir. Medido enfocando con TECLADO la fila real, con `transition` congelada:
0 px en reposo, 4 px enfocada, 1234 px con el centinela.
## Guards
censo `--only chat-log` 100 % (global 67 %, no baja) · `component-audit` PASS ·
`eidos-lint` 0 invalid · `vitest run src/uix/eidos` sin rojos nuevos (los dos
vivos son ajenos: `skin-media-player` y `radio-group.row-min-height-touch`) ·
`rtl:check` 0 · `docs:check` 0 · `check` sin un solo error del componente ·
prettier limpio sobre el contenido normalizado a LF.
README con su sección «Talla y tema», demo con pestaña `Tokens` verificada en
navegador, ficha con su veredicto §5 y registro en PLAN §8.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
f24abdb60e |
uix(search-field): temable — 71 % → 100 %, y sus 10 tokens sin medir NO mienten
Cero claves nuevas. Su 29 % de deuda eran CUATRO literales que son identidad, y ahora van firmados. Diff de computed 0 sobre 1.216 valores en 8 estados y 5 nodos; R-5.4 de 7/17 a 17/17 SIN una sola adjudicación, dos corridas de acuerdo; capturas 2× antes/después idénticas byte a byte. LOS CUATRO LITERALES SON IDENTIDAD, NO KNOBS El line-height: 1 del icono —un glifo solo no tiene interlineado, la misma anotación que su hermana field.css—, el 100 % doble del ::before —el anillo ES la caja del indicador, que ya mide por --_search-field-loading-size— y el border-radius: 50 %. Ese último la §4 pedía acuñarlo como loading-indicator-radius: sería un knob que SÓLO puede romper el componente. El arco es un BORDE que gira; con cualquier otro valor la ruleta deja de leerse como una. Es la clase 4 de F2-B, «el 50 % de un círculo», al pie de la letra. DIEZ DE SUS DIECISIETE TOKENS NO SE HABÍAN MEDIDO NUNCA El centinela leía 7/17 porque la demo no monta dos estados: el indicador de carga sólo existe con loading (ocho tokens) y la X sólo se desvanece con el campo VACÍO, y el escenario arranca con valor (clear-hidden-opacity). Los dos interruptores entran como prepareWith, y EL ORDEN IMPORTA: el clic en la X tiene que ir PRIMERO, porque la propia regla que enciende le quita después el pointer-events al botón que la disparó. La sonda necesita el de loading por lo mismo: 4 → 5 nodos, sin perder ninguno. LOS DOS PRIVADOS NO SON DEUDA --_search-field-icon-size y --_search-field-loading-size son el conmutador por talla y sus cinco fuentes son, cada una, una clave pública. Se quedan: 58 de las 162 recetas resuelven así su eje size —incluidas las cerradas al 100 % este mes, empty-state y banner—, y convertirlas al ámbito size: del TSC cambiaría la especificidad de la emisión sin ganar un punto de alcance. ⚠ UN DEFAULT MEDIDO, ANOTADO, NO CAMBIADO Las dos escalas del mismo renglón divergen en los dos últimos peldaños. Medido en Chrome con transiciones y animación congeladas, forzando data-size sobre el nodo real, icono → indicador: xs 14→14, sm 16→16, md 18→18, lg 20→18, xl 32→20. El contrato lo declara de frente (loading-indicator-size-lg apunta a --size-md-icon-size y -xl a --size-lg-icon-size), así que en xl la ruleta es DOCE píxeles menor que la lupa que tiene al lado. No es deuda de alcance —las diez claves alcanzan—, es diseño que huele a copia a medias. Mueve píxel: D-TH.5 lo saca de este eje. Escrito en el veredicto §5 y en el README. CIFRAS Y GUARDS Censo 71 % → 100 % (15 knobs → 11: público 10, literal 4 → 0, excepción firmada 0 → 4; sistema 1, fuera del ratio). Contrato 17 claves, intacto — no hay cambio en base.ts ni en generated/. component-audit PASS · eidos-lint invalid 0 (9 morfo-backed, 4 eidos-only) · vitest src/uix/eidos 35/36 (único rojo skin-media-player, preexistente) · rtl:check 0 · docs:check 0 · npm run check sin errores en ficheros de este componente. README con su sección «Talla y tema» y pestaña Tokens en la demo (17 claves, verificada con clic de Playwright tras networkidle). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
f68bac4a66 |
uix(avatar): temable — 75 % → 90 %, y su contrato era INVISIBLE para los dos instrumentos
Protocolo PLAN-theming §7 sobre `avatar` (incluye `AvatarGroup`, que comparte
receta y fichero). Censo 75 % → **90 %**, contrato 84 → 88 claves, centinela
**83/88** con 5 adjudicadas. El default NO se mueve.
## Lo primero: el instrumento no veía el contrato
La entrada `avatar` de `recipes/base.ts` es la ÚNICA construida por una IIFE (un
helper local genera sus 24 ámbitos compuestos), así que su mapa vive en el
`return {`, un tabulador más adentro. Consecuencia medida:
- el censo la leía como «sin entrada en base.ts» — 84 claves invisibles;
- el centinela **moría** con `no recipe block for avatar`: el componente no se
podía medir, y su gate no significaba nada.
Los dos lectores leen ya la IIFE (dedentan el `return`). Sin este arreglo, todo
lo demás de este commit habría sido humo.
## Lo que entra al contrato (6 claves)
`group-overlap-{xs,sm,md,lg,xl,xxl}` — la escala de solape de `AvatarGroup`.
Había UNA clave, `--avatar-group-overlap`, y la receta la RE-DECLARABA en seis
bloques `[data-size]`: sentada en el elemento, ganaba siempre al `:root` donde
escribe un tema. Medido: `37px` desde el asiento del tema no movía el margen; el
mismo valor escrito sobre el nodo, sí. Ahora el paso viaja por
`--_avatar-group-overlap` y los seis alcanzan desde `:root` (−8,4 · −11,2 · −14 ·
−16,8 · −22,4 · −33,6 px → 37 px, uno a uno).
## Lo que se retira (2 declaraciones muertas, diff 0 las dos)
- `group-max`: el envoltorio escribía `--avatar-group-max` INLINE y la receta
declaraba su default `99`. **No lo leía nadie** — el tope se aplica con
`data-has-max` + `:nth-child(n + M)` porque una variable no entra en
`:nth-child()`, y lo dice el propio comentario del CSS. Retirado de los dos
sitios; el `+3` del grupo sigue exactamente donde estaba.
- el respaldo `, white` de `--_avatar-badge-fg`: el contrato ya declara
`--avatar-badge-color-custom-contrast: white`, así que era inalcanzable y sólo
podía envejecer contra su token. Comprobado en la rama custom: la tinta sigue
computando `rgb(255, 255, 255)`.
## Seis literales firmados (salen del ratio)
Los cuatro `100 %` de Image y Fallback son IDENTIDAD — la parte ES la superficie
del avatar, no una talla propia — y los dos `line-height: 1` mantienen el glifo
centrado por la caja flex.
## Lo que se queda privado (el techo real es 90 %)
`--_avatar-bg`, `--_avatar-fg` y `--_avatar-badge-bg` son un CONMUTADOR: cambian
de fuente con la variante (solid · soft · outline) y su valor sale del forward de
paleta THM-2 que la capa de color alimenta por instancia desde `[data-color]`.
Un público encima dejaría que un tema los fijara y matara el `color=` de cada
avatar.
## Artefactos
- Sonda: **0 diffs** en TRES bases — demo de avatar con insignia y anillo (576
valores), la misma en modo fallback (384) y demo de AvatarGroup (3.072).
- Capturas 2× antes/después de las dos demos: **idénticas al byte**.
- Centinela R-5.4: 83/88, 5 adjudicadas con su medida en el ledger (`size-xxl` y
`font-size-xxl` viven en el paso `xxl`, que el barrido del guard no alcanza —
el límite que `metrics` ya registró; `radius-none` y `ring-width-sm` son «sólo
el paso en vigor pinta»; `group-overlap-xxl` junta las dos cosas).
- El guard aprende `avatar`: la insignia y el anillo son OPT-IN, el barrido de
`data-variant` destapa las 16 claves `*-outline-*`, y `AvatarGroup` se mide en
su propia ruta (21 muertos → 5).
- `eidos-lint` 0 invalid · `vitest src/uix/eidos` sin rojos nuevos (el conocido
`skin-media-player`) · `rtl:check` 0 · `docs:check` 0 · `npm run check`
COMPLETED sin un solo error en `avatar`.
- README «Talla y tema» + pestaña `Tokens` (88 filas, verificada en el navegador
con `networkidle` y clic real).
## Lo que NO se arregla aquí (anotado)
- **R-5.3 preexistente**: cuatro claves `*-color-custom*` donde la gramática
exige `fg`. Son escotillas que el envoltorio escribe INLINE, así que
renombrarlas cambia el contrato público de tres canales: se lista, el audit
sigue en NEEDS-WORK por esto y sólo por esto.
- **Falso POSITIVO nuevo del centinela** (next-features §13): escribe el token
en `:root` y también sobre cada nodo, y para una propiedad personalizada que
la receta re-declara en el ELEMENTO ese inline gana justo lo que un tema
pierde. Es lo que hacía que `--avatar-group-overlap` leyera vivo.
- **El barrido de tallas del guard para en `xl`**: añadir `xxl` dejaría STALE
las seis excepciones de `metrics`, así que se hace con la re-verificación del
ledger entero.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
1a41312b68 |
uix(image-picker): temable — 71 % → 100 %, y el instrumento medía UN nodo
Una clave nueva (13 → 14), cuatro literales firmados y el interruptor que hace
medible el componente. Diff de computed 0 sobre 1.344 valores en 7 estados y 6
nodos; R-5.4 de 13/13 a 14/14 SIN una sola adjudicación; capturas 2×
antes/después idénticas.
EL PICKER ARRANCA VACÍO Y LA SONDA VEÍA UN NODO
Todo lo que la receta pinta —preview, canvas, toolbar y los dos botones de
icono— sólo existe en el estado ready, al que no llega ningún trigger: hay que
cargar un fichero. Con el chip «Load sample image» de la demo la sonda pasa de 1
a 6 nodos. La sonda lo enciende por DEMO_VARIANTS y el centinela por
prepareWith; éste necesita además openMarker, porque su re-apertura por token
volvería a pulsar el chip y remontaría la vista previa a mitad de corrida.
LA CLAVE ES hover-button-bg, Y EL NOMBRE LO DECIDIÓ EL CATÁLOGO
Era el único knob que iba a un primitivo global a pelo. La §4 lo nombraba
hover-rotate-bg por el PRIMER selector de la regla, y miente dos veces: pinta
rotate Y remove, y el contrato de este componente ya llama button-* a ese par
desde que nació. hover-{parte}-{slot} es la forma de las 60 claves hover-* de
base.ts.
NO ES EL VELO DEL SISTEMA — MEDIDO DESDE EL PÍXEL HACIA ARRIBA
Los dos botones flotan SOBRE la foto: su relleno de reposo es translúcido
(color-mix 88 %) y el hover lo vuelve OPACO. Es un cambio de RELLENO, no una
capa encima. Con las transiciones congeladas, elementsFromPoint sobre el centro
del botón: el cambio cae en el <button> —el nodo con forma, radio 6 px, de
srgb .988/.88 a oklch(.9911 0 0)— y NINGÚN ancestro recibe background-image,
porque el arquetipo action no trae velo (archetypes.css sólo vela trigger, item
y option). No se bendice un duplicado, que es lo que paró el hover-* de listbox.
LOS CUATRO LITERALES SE FIRMAN, NO SE ACUÑAN
El 100 % doble del hijo del canvas es identidad (la <Image> ES la caja del
canvas, ya con inset: 0), el line-height: 1 es un botón de un solo glifo, y el
Highlight de forced-colors es la paleta del sistema operativo: un valor de tema
ahí lo sustituiría el UA. Válvula de recipe-contract §3 — salen del ratio como
ausencia ESCRITA.
DEFECTO REAL, ANOTADO, NO ARREGLADO
Ese hover neutro es una invención por componente y §38 / R-4.3 mandan que el
hover neutro sea la capa --state-*. Migrarlo cambia el default (velo translúcido
en vez de relleno opaco) y D-TH.5 lo prohíbe aquí. Y no basta con cambiar la
receta: el arquetipo del par es action, que HOY no recibe velo — o action gana
velo en archetypes.css (mueve píxel en todo el catálogo) o el par cambia de
arquetipo, que es morfo. Escrito en el veredicto §5 y en el README.
CIFRAS Y GUARDS
Censo 71 % → 100 % (20 knobs → 16: público 12 → 13, global 1 → 0, literal 4 → 0,
excepción firmada 0 → 4; sistema 3, fuera del ratio). Contrato 13 → 14 claves.
component-audit PASS · eidos-lint invalid 0 (12 morfo-backed, 5 eidos-only) ·
vitest src/uix/eidos 35/36 (único rojo skin-media-player, preexistente) ·
rtl:check 0 · docs:check 0 · npm run check sin errores en ficheros de este
componente. README con su sección «Talla y tema» y pestaña Tokens en la demo
(14 claves, verificada con clic de Playwright tras networkidle).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
4c5cfd8ba7 |
uix(color-swatch): temable — 70 % → 100 %, y el color del chip NO es un knob
Cuatro claves nuevas por una retirada (12 → 14 en el contrato), dos adjudicaciones
medidas. Diff de computed 0 sobre 832 valores en 15 estados y DOS rutas; capturas
2× byte a byte idénticas.
LA §4 PEDÍA UN NOMBRE Y LO QUE FALTABA ERA UNA ESCALA
La ficha marcaba `radius` con ⚠ y tres valores en colisión. No es una colisión: es
el eje `rounded` con cuatro pasos, y la clave que ya estaba en el contrato ERA el
paso `sm` bajo un nombre que no lo dice. Contado en base.ts: siete recetas
escriben `radius-{k}`, y una es `badge` — el gemelo que lee ESTE MISMO
`data-rounded` con el mismo conmutador privado. Así que entra la escala entera
(radius-sm/md/lg/full) más `--_color-swatch-radius`, exactamente la forma que su
propio eje `size` usaba dos líneas más arriba. Dejar `radius` al lado de
`radius-md` habría dejado el paso `sm` llamándose `radius`: un nombre que miente.
`fill` SALE DEL CONTRATO: ES UN CANAL DE VALOR, NO UN KNOB DE TEMA
El wrapper escribe `--color-swatch-fill: {color}` INLINE en cada render desde una
prop obligatoria. Medido sobre un chip real con transiciones congeladas: escribir
el token en :root deja el ::before en rgb(124, 58, 237) — la declaración inline
gana siempre. Un público encima sólo podía mentir. La forma correcta ya estaba
escrita en el guard de la capa: recipe-css-contract sanciona `var(--x, default)`
para las vars que el runtime escribe («el respaldo ES el default»), que es justo
lo que la receta tenía. Por eso el respaldo NO se retira: al salir del contrato
deja de ser código muerto y pasa a ser el default.
⚠ EL CENTINELA DABA `fill` POR VIVO — ES UN FALSO POSITIVO SUYO
El guard escribe el token en :root Y en el `style` inline de cada nodo del
componente, que es exactamente donde vive el canal de valor: lo pisa y lee
«mueve». Es la cara opuesta de drag-drop.preview-z, donde la adjudicación se
había medido sobre un nodo fabricado. Regla: un token que el componente escribe
INLINE no se puede juzgar con el guard — se mide a mano escribiendo sólo en :root.
EL INSTRUMENTO ESTABA CIEGO, DOS VECES
1. color-swatch NO TIENE RUTA DE DEMO (404): es el QUINTO canónico en ese estado,
tras picker-shell, mockup, surface y audio-player. La sonda medía 1 nodo. Se
mide donde de verdad se renderiza — gradient-builder (3 chips en el stage) y
color-picker (el del trigger).
2. El eje `rounded` no lo monta NADIE: ningún consumidor del repo pasa la prop, así
que los tres pasos que no son el default no tenían nodo que pintar. El guard los
barre con sweepAttr: data-rounded, como meter hace con su shape.
LAS DOS ADJUDICACIONES SON EL ANILLO OPT-IN
border-width y border-fg: `bordered` nace apagado y ningún consumidor lo enciende,
así que su ::after no existe en ninguna página. Forzado data-bordered sobre un chip
real, los dos llegan (inset 0 0 0 1px → 1234px; oklch(0.931 0 0) → rgb(1, 2, 3)).
ARTEFACTOS
Sonda antes/después en dos rutas (256 valores × 8 estados en color-picker, 576 × 7
en gradient-builder) → 0 diffs, 0 nodos perdidos · el eje `rounded` medido a mano
forzando el atributo: 4 / 4 / 6 / 10 / 9999 px antes y después · capturas 2×
idénticas byte a byte · R-5.4 12/14 con las dos adjudicadas, exit 0 · censo --only
70 % → 100 % (knobs 10 → 7: los tres border-radius por variante pasan a declarar
el privado) · component:audit PASS · eidos-lint 2 morfo-backed / 8 eidos-only / 0
invalid · rtl:check 0 · docs:check 0 · suite eidos 439/440 (el rojo conocido,
skin-media-player) · npm run check COMPLETED sin errores en ficheros tocados.
LO QUE NO ENTRA Y POR QUÉ
- Paso 9 del protocolo (pestaña Tokens en la demo): no hay demo que tocar.
- docs/audit/theming/README.md: es un agregado global y otra sesión tiene form y
progress en vuelo en el mismo árbol; se regenerará con su commit. Por lo mismo,
de base.ts, generated/base.css, theming-sentinel.ts y el ledger sólo entran mis
hunks (verificado con git diff --cached).
Defectos anotados, no arreglados (mueven documentación ajena a este cambio): la
cabecera del CSS, el docblock del .svelte y types.ts siguen diciendo que la talla
sale de `--icon-size-*`, cuando desde la extracción tiene escala propia.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
271fcd4df8 |
uix(badge): temable — 59 % → 75 %, y subir un privado al contrato lo somete a reglas nuevas
Seis claves nuevas (65 → 71), dos literales firmados, cinco privados
adjudicados. Diff de computed 0 sobre 992 valores en 7 estados.
LA ALTURA DEL CHIP ERA EL HUECO
Badge monta su altura en la escala de CONTROL a propósito —para que `md`
signifique lo mismo lleve o no un control dentro (el ✕ removible es un
IconButton: medido, el mismo badge md daba 30px sin él y 46px con él)— pero lo
hacía desde un privado con cuatro fuentes por talla que leía --control-height-*
A PELO. Eso es una ESCALA, no un conmutador: sube al TSC como
min-block-size-{xs,sm,md,lg} + el resuelto, el molde que sus vecinas
padding-inline / gap / font-size ya tenían.
Y AL ENTRAR EN EL CONTRATO, LA SUITE LO CORRIGIÓ
El valor verbatim del CSS era el primitivo CRUDO (--control-height-{k}) y
recipe-css-contract exige la coordenada del BUNDLE (--size-{k}-control-height,
theming §5). Es la trampa que el handoff nombra —«la propuesta hereda el
incumplimiento del CSS de partida»— y que ya mordió a code-block: SUBIR UN
PRIVADO AL CONTRATO SOMETE SU VALOR A REGLAS QUE EN EL CSS NO SE LE APLICABAN.
Corregido; el diff sigue en 0.
dot-radius ES EL PAR QUE FALTABA
Redondo es el DEFECTO, no la definición: knob.indicator-radius vale
--radius-sm, así que el catálogo ya trata el radio de un marcador como knob.
Los dos 100% del glifo son identidad (el icono ES su ranura), firmados.
LOS CINCO PRIVADOS SON LAS TRES CLASES DE F2-B EN UN SOLO COMPONENTE
--_badge-bg / --_badge-fg son un CONMUTADOR (cambian de fuente con la variante:
track · solid · transparent, y aplanarlos obligaría a duplicar cada regla);
--_badge-fill-finish es el canal que el generador de degradados deriva de la
paleta de ESTA instancia; y --_badge-palette-* son forwards THM-2 que la capa de
color alimenta por instancia — un público encima dejaría que un tema los fijara
y matara el color= de cada chip.
LAS 40 CLAVES DE TONO NO MUEVEN NADA, Y YA SABÍAMOS POR QUÉ
El bloque genérico [data-badge][data-color] del forward de paleta se emite EL
ÚLTIMO y gana por orden a igual especificidad: la pintura sale del --palette-*
global. Es el hallazgo de cascada de paleta medido en button, badge y callout el
mismo día (§13, pendiente de firma) y entra como EXCEPCIÓN POR PATRÓN — una
razón arquitectónica escrita UNA vez, no cuarenta accidentes. Con ella y los dos
pasos de radius adjudicados (forzando data-rounded: 4px → 37px y 10px → 37px),
el guard queda en 29/71 y limpio.
EL INSTRUMENTO NO VEÍA TRES DE SUS CINCO PARTES
Dot, icon y remove son interruptores OPT-IN apagados por defecto: la sonda medía
2 nodos —raíz y etiqueta— y ninguna de las partes que la receta pinta.
Encendidos, 5. Misma clase que las capas opt-in de background.
⚠ component:audit da NEEDS-WORK y es PREEXISTENTE: R-1.5, sin tratamiento de
foco en la receta (grep focus-visible = 0 tanto en HEAD como después). El ✕ trae
el suyo del IconButton que compone; el chip no es focalizable. No se toca aquí:
darle foco mueve píxel y es decisión de diseño.
VERIFICACIÓN
Sonda antes/después 0 diffs (992 valores · 7 estados · 5 nodos con las tres
partes encendidas) · R-5.4 29/71 con el resto adjudicado (dos entradas propias +
una excepción por patrón), cero STALE · censo 75 % · eidos-lint 5 morfo-backed /
14 eidos-only / 0 invalid / 0 class-hooks · rtl:check 0 · docs:check 0 · suite
eidos 439/440 (el rojo es skin-media-player, el conocido).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
11b92ded6f |
uix(waveform): temable — 55 % → 86 %, y el playhead era el ÚNICO primitivo crudo
UNA clave nueva, cuatro literales firmados, un préstamo adjudicado. Diff de computed 0 sobre 1.600 valores en 7 estados; R-5.4 11/11, CERO adjudicaciones. LA CLAVE NUEVA NO LA PIDIÓ EL CENSO, LA PIDIÓ EL FICHERO `playhead-bg`. Todas las demás líneas del re-tinte del Slider embebido leen ya un token propio (--slider-secondary-bg: var(--waveform-buffered-fg)) o una identidad (transparent / none / 0px / 1); el thumb era la ÚNICA que pasaba un primitivo CRUDO (--color-content-primary) a un componente compuesto. Con eso, repintar el playhead exigía mover --slider-thumb-bg (todos los sliders de la app) o --color-content-primary (todo). Ahora es el par natural de `playhead-width`, que ya existía. El molde es el re-tinte de media-player, que reenvía --media-player-* en cada línea. El censo no lo veía: una custom property reenviada a un componente compuesto no entra en KNOB_PROPS (D-TH.2). La clase entera queda anotada en §13 — toda receta que re-tinta un embebido puede pasarle primitivos crudos sin que nada lo vea. EL 14 % QUE QUEDA ES UNA FILA, Y ES DOCTRINA La onda deshabilitada lee var(--slider-disabled-opacity). La prueba de si un token es tuyo no es qué valor tiene sino QUÉ DECIDE: aquí decide cuánto se apaga un control deshabilitado, y eso es del Slider — la onda vive fuera de su caja pero es la misma superficie a la vista. Un --waveform-disabled-opacity daría un segundo mando para UNA decisión y permitiría desincronizar cromo y pintura. Préstamo con dueño: se tema en la ficha de slider, como el 82 % de time-picker se tema en la de field. LOS CUATRO LITERALES SON IDENTIDAD, Y AHORA LO DICEN La pintura ES la caja del provider (100% sobre un position:absolute; inset:0), el área de arrastre ES la caja entera («scrubbing means grabbing anywhere on it»), y un playhead abarca todo el alto por definición. Anotación /* literal: */ en su declaración: desviación firmada, fuera del ratio. EL INSTRUMENTO, CIEGO POR UNA CAUSA NUEVA: EL COMPONENTE COMPUESTO La sonda medía 4 nodos —provider, svg y los dos paths— y NINGUNO era el playhead: el cromo entero es un Slider embebido cuyos nodos llevan data-slider*. Con extraNodes, 8. Es la TERCERA cara del mismo agujero: HTML crudo (prose), partes que compone el consumidor (navigation-menu) y partes de un componente compuesto (waveform). En §13, con la pregunta de fondo: hoy hay que sospecharlo y contar los nodos, el instrumento no lo detecta solo. VERIFICACIÓN Sonda antes/después 0 diffs (1.600 valores · 7 estados · 8 nodos) · R-5.4 11/11 sin adjudicaciones · component:audit PASS · censo 86 % con la única fila restante explicada · eidos-lint 4 morfo-backed / 16 eidos-only / 0 invalid / 0 class-hooks · rtl:check 0 · docs:check 0 · suite eidos 439/440 (el rojo es skin-media-player, el conocido) · Chrome: playhead 2px × 40px, onda 544 × 40, trazo 0.6 — idénticos. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
85988785d4 |
uix(navigation-menu): temable — 44 % → 100 %, y el guard cerraba el panel que medía
Bloque B1 de D-TH.4. Contrato 26 → 44 claves: 19 nuevas y UNA retirada.
Diff de computed 0 sobre 2.752 valores en 8 estados; R-5.4 43/44 con una
adjudicación escrita. Global 65 % (al 100 % 25 → 26).
LOS NOMBRES LOS DIO EL CATÁLOGO, NO YO
Ninguno se inventó. Los gemelos cerrados el mismo día (dropdown-menu 96 %,
context-menu 87 %) dan content-{min-width,padding,radius,fg} y
chevron-{size,stroke}; accordion da trigger-font-family y disabled-trigger-fg.
Las cuatro recetas que declaran un chevron lo dimensionan con un LITERAL
(0.42em · 0.5em · 0.625rem): un glifo no es un paso de la escala de espacio.
Al revés con el subrayado del indicador: su 2px ES --border-width-medium, y
dropdown-menu.separator-thickness dice que un grosor decorativo se nombra por
esa familia.
LA §4 GENERADA FALLA DE CINCO MANERAS, UNA NUEVA
Llamaba trigger-width / trigger-height al CHEVRON — nombrando por el primer
selector de la regla ([…-trigger]::after) y colisionando con trigger-height,
que YA EXISTE y es la altura del control. Fundía en content-radius dos nodos
con dos valores. Cruzaba los nombres: content-padding-* para lo que es de la
FILA, y el padding real del panel a «sin nombre mecánico». Llamaba
content-width a un min-inline-size. Y la nueva: abría exigiendo «consume la
capa compartida menu-indicator» — HOMONIMIA, esa capa es el check de los
CheckboxItem de dropdown/context-menu y no toca el subrayado de esta barra
(LAYER_VOCABULARY sólo registra calendar-surface).
LAS FILAS DEL PANEL NO SON item- NI link-
En los gemelos, `item` ES la fila. Aquí `item` es el <li> de la barra (parte
del morfo) y el Link de barra viste el cromo del trigger: --…-item-radius o
--…-link-radius prometerían a un tema un nodo que no alcanzan. Se acuñan
content-link-*, que nombra el nodo por donde vive. Su tinta sigue leyendo
trigger-fg, como venía.
EL PRIVADO ERA UNA ESCALA, NO UN CONMUTADOR
--_navigation-menu-list-gap tenía tres fuentes (0 / --space-1 / --space-2) por
data-size: sube al TSC como list-gap-{sm,md,lg} + el resuelto, el molde que el
bundle del trigger ya tenía al lado. Los dos bloques [data-size] del CSS se
retiran — los emite el TSC, y dejarlos vivos da un diff de 0 por la ruta vieja.
indicator-x ERA DECLARACIÓN MUERTA
Soma escribe las cuatro coordenadas del rect INLINE sobre el indicador
(-x, -y, -w, -h) y la receta lee sólo -w y -h; su propio comentario registra
que sumar translateX(var(--…-x)) DUPLICABA el desplazamiento. No la leía
nadie. Retirada, 0 diffs — la prueba de que estaba muerta (precedente: el
re-point de --command-radius, el trío item-gap* de carousel). -w se queda:
vive, y es canal de valor, no superficie de tema.
EL PANEL NO ACUÑA TIPOGRAFÍA, A PROPÓSITO
Lleva [data-depth='overlay'], y ese plano declara font-family / line-height a
la misma especificidad (0,1,0) y más tarde en la cascada: un token ahí leería
muerto. Es §12.9, pendiente de firma — la misma que hizo retirar claves en
menubar, dropdown-menu, context-menu y tooltip. Sí se acuña la familia de la
BARRA (trigger-font-family), que no es overlay y alcanza.
TRES COSAS QUE APRENDIÓ EL INSTRUMENTO (registradas en next-features §13)
1. Sonda y centinela eran CIEGOS A LA MITAD DEL COMPONENTE. Las filas del
mega-menú son los <a> DESNUDOS del consumidor, estilados por descendencia:
el filtro data-{c}-* veía 10 nodos y NINGUNO era el panel ni una fila — 9
de los 19 knobs fuera del diff. Con EXTRA_NODES / extraNodes la sonda pasa
a 16 nodos abiertos. Y el paso `open` de la sonda no honraba EXTRA_NODES.
2. EL GUARD CERRABA EL PANEL QUE MEDÍA. Este mega-menú abre con pointerenter
y pointerleave programa el cierre; el guard abría con clic y luego aparcaba
el puntero en (0,0) — justo el gesto que lo cierra. Dos corridas del mismo
código discrepaban. Y daba las DOS caras del error: falso negativo en los
tokens de la fila, falso positivo en content-link-padding-inline, que leía
«vivo» por ensanchar el panel que sí se medía. Con openBy: 'hover', dos
corridas idénticas.
3. `color` está EN TRANSICIÓN en esta parte: forzar data-disabled y leer en el
acto devuelve el valor VIEJO — tres diagnósticos falsos («ni el inline lo
mueve») hasta congelar. La trampa del cuentagotas del color-picker, en otro
componente. Congelado: reposo oklch(0.2435 0 0) → disabled oklch(0.61 0 0).
Y una de contabilidad, del instrumento y no del componente: el censo decide el
eje `size` grepeando data-size en el CSS, y el protocolo §7.4 ORDENA retirar
esos bloques. Todo componente que lo aplique bien aparece «sin eje». Ya le
pasa a sidebar y nav-tree. En §13.
VERIFICACIÓN
Sonda antes/después 0 diffs (2.752 valores · 8 estados · 16 nodos con el panel
abierto) · R-5.4 43/44 en dos corridas idénticas, adjudicación única
(disabled-trigger-fg: la demo no monta el estado; forzado alcanza en trigger Y
link, rgb(1,2,3)) · component:audit PASS · censo --only 100 % · eidos-lint 30
morfo-backed / 3 eidos-only / 0 invalid / 0 class-hooks · rtl:check 0 ·
docs:check 0 · suite eidos 439/440 (el rojo es skin-media-player, el conocido)
· check sin error nuevo en los ficheros tocados · Chrome con el panel abierto:
panel 8/10px/224px, fila 8/12/6px, trigger 36px/6px/gap 4px, indicador 2px.
Tras tocar el guard, command, table y tooltip re-verificados sin regresión ni
STALE.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
83a897b925 |
uix(audio-player): temable — 37 % → 56 %, y el instrumento medía CERO nodos
3 claves nuevas (5 → 8) y un literal anotado. El 44 % restante es PRÉSTAMO CON DUEÑO, no deuda: ocho filas que leen `--media-player-*` porque este componente ES la piel de audio de ese chasis, y una ficha nunca propone duplicar un token prestado con prefijo propio (§5.3-3). Acuñado lo único que ESTE reproductor añade sobre el chasis: el peso del título y la insignia LIVE en versalitas espaciadas (peso + tracking). Los dos `600` pasan a `var(--font-weight-semibold)`: el mismo valor, con nombre. Y EL INSTRUMENTO ESTABA CIEGO DEL TODO: MEDÍA 0 NODOS. Tres razones encadenadas: 1. `/uix/components/audio-player` es un 404 — el CUARTO canónico sin ruta propia (picker-shell, mockup, surface y éste). Se mide en la demo del chasis. 2. Sus partes no llevan `data-audio-player-*`: son `data-media-player-*`. Con `extraNodes`, la sonda pasa de 0 a 48 NODOS. 3. Sólo existe tras el chip `media: audio` de esa demo: sonda (`DEMO_VARIANTS`) y guard (`openWith` + `openMarker`) lo pulsan ahora, y el guard barre además las cuatro variantes (card/row/bar/inline). Con eso, 0/8 → 6/8. Las dos que quedan son la insignia LIVE, que sólo existe mientras el proveedor refleja un directo: montada con `[data-live]`, alcanza (600 → 333 y 1,28px → 7px). Gates: sonda con 48 nodos (antes 0) · centinela 6/8 con las dos adjudicadas · censo 56 % · suite eidos con el rojo conocido ajeno · `rtl:check` 0 · `docs:check` 0 · equivalencia comprobada en el computado (título 600, tracking 1,28px). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
c08e88598d |
uix(image): temable — 60 % → 96 %, y dos instrumentos que morían en esta página
Una clave nueva y 12 literales anotados. La fila que queda fuera es el puente de paleta del placeholder de color, que no es deuda. Su «deuda» era casi toda IDENTIDAD: once `100 %` —la imagen, el fallback, el error y sus dos `<img>` llenan su caja— y un `opacity: 1`, porque una imagen CARGADA es opaca. Ninguno es una perilla, y ahora los doce llevan su razón. Lo acuñado es la barra del esqueleto (`skeleton-bg`): leía `--gradient-shimmer` a pelo, y es lo único de ese placeholder que un tema querría retocar sin tocar todas las recetas que brillan. LA PÁGINA NO ALCANZA `networkidle` NUNCA: monta un `src` roto a propósito para su estado de error y el navegador reintenta, así que sonda y guard morían con un `TimeoutError` antes de medir un solo nodo. Las dos caen ahora al evento `load` si la espera de red se agota — la ociosidad era una comodidad, no una garantía. UN TOKEN BAJO `@media (prefers-reduced-motion: reduce)` LEE MUERTO: `placeholder-base` sólo existe ahí, donde el brillo se sustituye por un plano. Emulando la preferencia alcanza, y vale para cualquier token detrás de una media query de preferencia. Y una lectura fantasma cazada por repetición: el primer diff acusó `opacity: 0.925793 → 1` en la imagen cargada. Era el fade EN VUELO —el mismo falso positivo que el rim animado de `onion-menu` esta mañana—. Repetida la medida: 0 diffs. Un comentario CSS no puede mover un computado. Gates: sonda 0 diffs en 544 valores × 7 estados (medida repetida) · comprobación aparte del esqueleto y del base de movimiento reducido · centinela 20/23 con las tres adjudicadas · censo 96 % · `component:audit` PASS · suite eidos con el rojo conocido ajeno · `rtl:check` 0 · `docs:check` 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
c679b1b449 |
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 |
|
|
df3c8d760e |
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 |
|
|
ab96ae783b |
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 |
|
|
001b1cdbd0 |
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 |
|
|
97a6f03d76 |
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 |
|
|
679dfdb291 |
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 |
|
|
a9b9711657 |
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 |