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 }
2025 Commits (3b325c4394e5500c5e1d385b59c82b86e7008979)
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
3b325c4394 |
feat(listbox): temable hasta donde le toca — 0 % → 68 %
Sexto del bloque F2-A, y el primero cuyo techo es DELIBERADO. censo 0 % → 68 % · global 42 % computed 2.813 valores en 7 estados: 0 diffs **Este componente no puede acuñar su ritmo de fila, y esa es la regla de oro de las capas compartidas.** Altura, padding inline y block, gap y tamaño de letra los posee `lib/list-surface.css`, por talla, para TODAS las superficies de lista —select, combobox, command y los menús—. Acuñar `--listbox-item-height` sería el vocabulario paralelo que las reglas de esa capa prohíben, y encima ganaría a la capa para todas las demás. Los cinco privados ADOPTAN la capa y se quedan privados; un tema mueve ese ritmo por `--list-*`, que es donde vive. Consecuencia honesta: el censo cuenta esos cinco como privados y el alcance se queda en 68 % en vez de ~86 %. **La métrica penaliza hacer lo correcto** — el mismo defecto de medición que D-TH.2-b arregló para los primitivos tipográficos (`--style-*` pasó a `system`). Registrado en `next-features.md` §13; extenderlo a las capas compartidas es decisión, no corrección al paso. **Dos cosas medidas que NO se tocaron:** - El `highlighted` pinta DOS veces: la receta pone un `--color-surface-overlay` plano y encima el arquetipo `item` añade su velo del 8 %. El plano es el duplicado que §38 deprecó, así que se dejó SIN token — bendecir con un nombre público algo condenado a morir es el error que este eje ya evitó en el codemod. Retirarlo mueve píxel. - La tinta de la fila seleccionada era código MUERTO y se retiró: el arquetipo la fija a (0,4,0) contra los (0,3,0) de la receta. Mismo hallazgo que en `table`, verificado igual, 0 diffs al quitarlo. Lo que sí es suyo y ahora es público: la superficie que sostiene las filas (fondo, borde, radio, padding, gap, tipografía, alto máximo), la esquina y la tinta de la fila, el glifo de selección y el grupo con su etiqueta. audit PASS · eidos-lint 0 invalid · rtl 0 · docs 0 · check 0 en tocados · suite: sólo el rojo conocido Demo con tab Tokens: 21 filas, 0 sin computar. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
d177ee331c |
feat(gradient-picker): temable — y su trigger resultó estar muerto entero
Quinto del bloque F2-A. censo 0 % → 88 % · global 42 % computed 609 valores en 8 estados: 0 diffs **El hallazgo llegó por el centinela: 36 de 45 tokens no movían nada.** Con esa cifra no se sigue adelante. El trigger es un POPOVER TRIGGER, y `popover.css` le pinta el cromo entero —altura, padding inline, tipografía, color, fondo, borde, radio, hover y foco— desde `[data-popover-trigger]:not([data-archetype='field-trigger'])`, que gana a `[data-gradient-picker-trigger]` en base (0,2,0 contra 0,1,0) y en los estados (0,3,0 contra 0,2,0). Esta receta re-declaraba TODO eso y nada pintaba: la altura, el padding, el tamaño de letra y el radio venían de popover, y con valores distintos —12px contra el `space-2-5` de la receta, 14px contra su `font-size-md`—. Llevaba así desde que el trigger se volvió trigger de popover. Así que no se tokeniza: **se retira**. Un token sobre una declaración muerta es un token que miente, y este eje ya retiró uno por lo mismo en `table`. Retirarlo dio **0 diffs**, que es la prueba de que estaba muerto. Un tema viste este trigger por el contrato de POPOVER, que es composición funcionando. Lo que sí es del picker se queda y alcanza (verificado a mano, porque el chip usa un hook de CLASE que el centinela no ve): el `gap` de la fila, el chip del degradado (tamaño por talla, radio, borde), las tres anchuras del panel, la lista de paradas y la rejilla de presets. **Correcciones al veredicto**, ambas medidas: `--gp-current-gradient` NO lo estampa el wrapper sino SOMA (`gradient-picker-provider.svelte.ts:197`), así que renombrarlo toca otra capa y no es de este commit; y no es un knob de tema sino un canal de valor —el degradado que eligió el usuario—, sobre el que un tema no tiene nada que decir. Las tres anchuras del panel se quedan como tokens planos leídos por tres reglas, no como cascada del TSC: el content va por PORTAL y dimensiona con `data-picker-size` (el atributo de picker-shell), y el vocabulario de scopes no tiene palabra para el atributo de otro componente. Al registro (`next-features.md` §13) van cuatro incidencias nuevas: que un componente compuesto pueda tener el cromo entero muerto sin que nada avise —merece guard, y el instrumento ya existe—, el nombre abreviado en soma, los dos hooks por CLASE que `eidos-lint` cuenta, y el foco opaco del trigger contra la mezcla suave del sistema. audit PASS · eidos-lint 0 invalid · rtl 0 · docs 0 · check 0 en tocados · suite: sólo el rojo conocido Demo con tab Tokens: 25 filas, 0 sin computar. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
5596fc8707 |
docs(next-features): el registro de incidencias del eje theme-reach
Encargo del autor: anotar todas las incidencias de esta clase para resolverlas después. Van al registro canónico (`next-features.md`), no a un sitio nuevo, y con su MEDICIÓN — no como impresión. **§12 — El contrato de cascada del velo de estado.** Seis incidencias que resultaron ser la misma: nadie fijó nunca cómo compone el velo de `archetypes.css` con las reglas de una receta. Y la raíz explica por qué se manifiestan de formas tan distintas: el velo se pinta con DOS pesos según el arquetipo — `:where()` (0,0,0) para `trigger`, especificidad plena (0,5,0) para `item`/`option`. 131 declaraciones-atajo en 45 componentes cancelan el velo (66 BASE · 32 ACENTO · 23 HOVER · 10 OTRA; cero valores con gradiente, así que el paso a longhand no tiene riesgo técnico; 32 nodos velados que hoy no reaccionan al ratón pasarían a hacerlo) · la prop `hoverable` de table no suprime nada · en tree-grid el empate (0,5,0) lo decide el ORDEN DE CARGA y varía entre recargas · la banda de tree-grid mata el hover en las filas pares · un token de tinta no puede ganar al arquetipo (`selected-row-fg` se retiró por eso) · el thumb de scroll-area no tiene velo al que migrar. Esto explica de paso la adopción 21/135 que midió la auditoría del 2026-07-01: el velo estaba escrito y estructuralmente derrotado. **§13 — Huecos de instrumento y de demo.** Lo que hizo que una medición mintiera o no existiera, que importa porque el eje entero se apoya en ellas: falsos negativos del centinela sobre propiedades transicionadas, pseudo-elementos y componentes compuestos que no ve, la sonda que no pasa el ratón por un `<tr>`, y la lección general —**una sonda sobre una parte que la demo no monta compara CERO valores y pasa**—, con la lista de partes condicionales afectadas. El handoff gana la regla para las sesiones del eje: si una incidencia mueve píxel o toca morfo, se mide, se anota ahí y se sigue; no se arregla dentro del commit del componente. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
9748733adc |
feat(tree-grid): el árbol se vuelve temable — 0 % → 95 %
Cuarto del bloque F2-A. 42 knobs con 29 privados que no derivaban de nada; ahora 47 claves públicas y tres privados, los tres legítimos. censo 0 % → 95 % · global 41 % → 42 % computed 9.744 valores en 7 estados: 0 diffs **Las guías de indentación eran UN color, no un fondo.** Lo que la propuesta llamaba `root-bg-image` son las líneas verticales por `aria-level`: cinco gradientes apilados que dibujan una línea por ancestro. El knob es `guide-fg` (+ `guide-width`); la construcción se queda en la receta — el mismo reparto que el damero de gradient-builder, donde el color es el token y el patrón es de la receta. **Tres privados sobreviven, y por razones distintas**: `--_tree-grid-columns` no es un knob de tema sino un CANAL de layout que el wrapper escribe inline desde la definición de columnas del consumidor; `--_tree-grid-palette-element` es el forward THM-2; `--_tree-grid-stable-rows` es dato del consumidor. **El hover de fila es no determinista, y viene de antes de este eje.** Dos hallazgos medidos: 1. En filas CON BANDA el hover está muerto: la regla de `striped` pesa (0,7,0) contra los (0,5,0) del hover y usa el atajo, así que en las pares no ocurre nada al pasar el ratón. 2. En las demás, quién gana depende del ORDEN DE CARGA: el velo del arquetipo `item` y el hover de la receta empatan EXACTAMENTE a (0,5,0). Seis corridas de la misma configuración dieron cuatro veces «sin velo» y dos «con velo». Eso segundo obligó a parar y caracterizarlo: al ver 3 diffs tras un cambio trivial (un `border-width` a token) lo primero que hice fue sospechar de mi cambio; comparar dos corridas del MISMO código dio los mismos 3 diffs, así que no era el código sino un empate de especificidad resuelto por orden — la misma clase de fragilidad que el canon documenta para las bandas de z-index. No se toca aquí: decidir quién manda mueve píxel y es la decisión pendiente del velo. El guard de huérfanos cazó tres tokens que acuñé suponiendo (`font-family`, `fg`, `surface-bg`): existían en la receta pero leyendo el valor crudo. Ahora los leen, que es justo lo que el eje persigue. audit PASS · eidos-lint 0 invalid · rtl 0 · docs 0 · check 0 en tocados · suite: sólo el rojo conocido `skin-media-player` Demo con tab Tokens: 47 filas, 0 sin computar. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
8324e2cedb |
docs(theming): partir recipes/base.ts, al terminar el eje entero
Pregunta del autor: por qué los tokens de 128 componentes viven en un fichero
de 6.223 líneas y no junto a su componente.
La respuesta medida: NO es un fichero de tokens, es el valor del campo
`recipes` de `EidosConfig` —serializable, que un tema hidrata—, y por eso los
valores no pueden mudarse a `components/{x}/`: ya se intentó y se retiró («The
old `tokens/components/*` were retired»). Lo que sí puede repartirse es la
AUTORÍA, con un fichero por componente que `base.ts` compone.
Verificado antes de proponerlo: el muro de tipos de `defineRecipes` sigue
disparando cuando las claves llegan por spread desde un fichero suelto —
probado con `trigger-color` y `padding-x`, ambas siguen sin compilar. Y el
único consumidor sensible a la forma (`eidos-purge`, que hace `Object.keys()`)
es indiferente.
Va al final de TODO el themeable, no al cerrar el bloque: mientras quede un
componente por tokenizar, ese fichero se toca en cada commit, y mover 6.000
líneas en medio garantiza conflicto con cualquier sesión que esté trabajando.
Los tres costes que justifican hacerlo se midieron hoy: casi entra un SEGUNDO
bloque `table` que el catálogo habría descartado en silencio, una coma perdida
rompió el catálogo entero dos veces, y es el punto de conflicto de todas las
sesiones concurrentes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
00cc9d76cf |
fix(media-player): el skin de audio SÍ se puede medir — y sale a cero
Corrección de una nota que escribí mal hace un commit. Dije que `audio-player` no tenía demo porque `/uix/components/audio-player` da 404. El autor lo corrigió: **la demo es la de `media-player`**, y el chip `media` conmuta a audio — ahí entra `<AudioPlayer variant>` sobre las mismas partes. El 404 es correcto; lo que estaba mal era mi lectura de él. Que la sonda encontrara 0 nodos no significaba «sin demo»: significaba que cargaba en modo VÍDEO. Y una comparación de 0 valores pasa siempre, así que ese «0 diffs» no valía nada — la clase de guard que este eje ya tiene anotada. `scripts/__probe-audio-skin.ts` pulsa el chip y recorre las cuatro variantes (card · row · bar · inline). Medido contra el commit anterior, revirtiendo los cuatro ficheros a HEAD~1 y restaurándolos: **1.008 valores, 0 diffs, 0 nodos ausentes**. El skin lee los públicos nuevos y pinta exactamente igual. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
b7a4cef08f |
feat(media-player): el proyector se vuelve temable — 0 % → 88 %
Tercero del bloque F2-A. 45 knobs sin contrato salvo el acento; ahora 61 claves públicas, **cero privados y cero globales**. censo 0 % → 88 % · global 40 % → 41 % computed 3.277 valores en 7 estados: **0 diffs** **El veredicto pedía renombrar `--_mp-*` → `--_media-player-*`; hacen falta menos pasos que eso.** El prefijo abreviado viola theming §6 r5, cierto, pero al leerlos uno a uno los dieciséis privados eran ALIAS PUROS de su fuente (`--_mp-fg: var(--color-content-primary)`, `--_mp-accent: var(--media-player-accent)`…). Un alias puro no se renombra: se borra. La capa privada entera desaparece y cada knob pasa a público. **El acento firmado NO se tocó**: `accent`/`accent-strong` siguen siendo `--scale-amber-9/10` — acento de proyector theme-stable, que debe leerse sobre cualquier vídeo en claro Y oscuro, con su doctrina escrita en el propio bloque. Verificado en vivo que sigue re-tintando el rango del Slider compuesto. **El scrim: UN color, dos gradientes.** La tira inferior de controles y el degradado del título salen del mismo `--media-player-scrim`; dos tokens dejarían que un tema rompiera la pareja sin querer. **`audio-player` viajaba en el mismo barco y no era obvio**: consume siete de esos tokens sin declarar ninguno. No es fuga entre componentes — es un SKIN sobre las mismas partes (`[data-media-player][data-variant]`), así que están en ámbito. Su receta se migró en el mismo pass. **Lo que no se pudo medir, y se dice**: `audio-player` no tiene página de demo (la ruta da 404), así que la sonda encontró 0 nodos — y una comparación de 0 valores pasa siempre, que es justo la clase de guard que no vale. Se verificó a mano estampando `data-variant` sobre el player vivo: el padding pasó a `6px 12px`, exactamente los tokens nuevos. Hueco de demo anotado en el README. Centinela 36/61. Los 25 restantes, adjudicados por clase: partes no montadas en la demo (captions, buffering, título de audio, paneles portalados), un pseudo-elemento (`::after` del scrim) y los que pintan DENTRO del Slider compuesto — verificado a mano que `--media-player-track` sí llega hasta `--slider-track-bg`. Los 6 knobs que quedan son literales de relleno (`100%`, `max-content`). audit PASS · eidos-lint 0 invalid · rtl 0 · docs 0 · check 0 en tocados · suite: sólo el rojo conocido `skin-media-player` Demo con tab Tokens: 61 filas, 0 sin computar. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
564133b485 |
feat(table): la rejilla se vuelve temable — 0 % → 86 %
Segundo del bloque F2-A. 46 knobs sin una sola entrada de contrato; ahora 45
claves públicas.
censo 0 % → 86 % · global 39 % → 40 %
computed 13.050 valores en 7 estados: **0 diffs**
**Una trampa esquivada de milagro**: `table` YA tenía bloque en `base.ts` — los
forwards de paleta THM-2. Insertar uno nuevo habría creado un segundo bloque
`table:` y el catálogo lo habría descartado EN SILENCIO (gana el último). Las
claves se fusionan en el existente; el guard de duplicadas lo confirma.
**Dos nombres corregidos, como decía el veredicto**: lo que el privado llamaba
`root-height-{k}` es la altura de la FILA (`row-height-{k}`), y el prefijo
`root-` desaparece del resto — el envoltorio ES el componente y
recipe-contract §1 no repite la parte en ese caso.
**Qué casa con el bundle y qué no**, medido eje a eje: la altura de fila sí
(1:1); el padding NO (la rejilla empaqueta más denso en xs y más suelto de md
en adelante — forzar el bundle cambiaría el default); y la tipografía casa
DESPLAZADA: de md en adelante la tabla tipa un paso por debajo
(`font-size-md` = `var(--size-sm-font-size)`), que es deliberado y ahora se lee
en el propio nombre de la coordenada en vez de esconderse en un primitivo.
**Dos hallazgos del arquetipo `item`, y ninguno es mío** — la fila lo lleva, y
ese velo de `archetypes.css` está a especificidad PLENA (0,5,0), a propósito:
1. **La prop `hoverable` no suprime nada.** Una fila se ilumina al pasar el
ratón aunque el atributo no esté — medido con hover real, y ocurría ANTES de
tocar esta receta. El arquetipo y la prop no se ponen de acuerdo sobre quién
decide: es decisión de morfo, se presenta, no se toca de oficio.
2. **`selected-row-fg` era un token que mentía.** La misma regla fija el
`color` de la fila seleccionada y gana a la receta (0,5,0 contra 0,3,0), así
que el token no podía mover nada nunca. Lo cazó el centinela; retirado con su
declaración muerta. La tinta de la fila seleccionada es del arquetipo.
Y una corrección de rumbo a mitad de camino: al pasar todo a `background-color`
el velo apareció en 3 sort-triggers (su velo sí va en `:where()`, 0,0,0). Eso
es el barrido sistemático del velo, que NO está firmado — así que en los nodos
velados se mantiene el atajo a propósito, con el porqué escrito en la línea.
Primero lo diagnostiqué como regresión mía; la medición lo corrigió.
Los 6 knobs que quedan: 3 privados de paleta (patrón THM-2, capa compartida) y
3 literales de layout/identidad (`inline-size: 100%` ×2, `opacity: 1`).
El test `horizontal-escape` afirmaba el nombre del privado; ahora afirma el
público, que es justo lo que este eje persigue.
audit PASS · eidos-lint 0 invalid · rtl 0 · docs 0 · check 0 en tocados ·
suite: sólo el rojo conocido `skin-media-player`
Demo con tab Tokens: 46 filas, 0 sin computar. README con «Talla y tema» y los
dos hallazgos del arquetipo.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
b43e7e2ec0 |
docs(theming): el tab Tokens entra en el protocolo del bloque
Cada componente que se tokenice engancha su tab en el mismo commit — una línea, porque el panel lee el contrato vivo y no hay tabla que mantener. `command` queda marcado como cerrado en la tabla del bloque. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
90aa6dc5f0 |
feat(demos): la demo enseña el contrato de tokens — `TokensPanel`
Hallazgo del autor mirando `command`: la demo no enseñaba su superficie de tema. Al ir a comprobarlo salió que **ninguna** demo lo hacía — ni las que este eje ya llevó de 0 % a 98 %. El tab `recipe` lista SELECTORES; los tokens no aparecían en ningún sitio, y lo más cerca era el tab `system` de 23 demos, que en `sidebar` enseña 3 de sus 40 claves. Un componente temable cuya demo esconde sus tokens está a medio entregar: la demo es el producto. `TokensPanel` es un panel COMPARTIDO (junto a SemaPanel / MotionPanel / SystemAxes), no una tabla a mano por demo: pregunta `eidos.getRecipeTokens(component)` — la misma entrada de `recipes/base.ts` que alimenta al generador — así que un token añadido, renombrado o retirado aparece aquí sin que nadie se acuerde de tocar la demo. Una lista escrita a mano sería una segunda fuente de verdad, y en este repo ya está medido lo que cuestan. Tres columnas: el nombre, el valor DECLARADO, y lo que COMPUTA sobre el escenario vivo. Esa tercera es la que convierte «es temable» en algo que se ve en vez de algo que el doc afirma. Un knob por talla o por ámbito se muestra como tal en vez de fingir un valor único. Verificado en el navegador con `command`: **57 filas, 0 sin computar** (`--command-input-padding-inline-sm` → `var(--size-sm-padding-inline)` → `calc(8px * 1 * 1)`). Entra en el protocolo del bloque F2-A: cada componente que se tokenice engancha su tab en el mismo commit. Para los 160 que aún no tienen contrato el panel dice justo eso — que todavía no son alcanzables por un tema. check 0 errores en ficheros tocados Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
4ddea7fe29 |
feat(command): la paleta se vuelve temable — 0 % → 98 %
Primer componente del bloque F2-A, y el peor del catálogo con veredicto
verificado: 52 knobs y NINGUNA entrada de contrato. Ahora tiene 57 claves y no
le queda un solo privado.
censo 0 % → 98 % · global 38 % → 39 %
computed 8.091 valores en 8 estados (reposo · 5 tallas · abierto · hover):
**0 diffs**. El default no se movió.
**El eje `size`** sigue el patrón firmado en Sidebar: coordenadas por talla en
`root` + nombre RESUELTO con `declarations` (default `host` = md, overrides
`size:sm`/`size:lg`), que es lo único que la receta lee. Los bloques
`[data-size]` del CSS se RETIRAN: dejarlos vivos pisaría los tokens nuevos con
los valores viejos y el diff daría 0 porque la ruta vieja seguiría mandando —
la trampa que este eje ya pagó una vez. Las cuatro coordenadas casan 1:1 con el
bundle, medido (`--size-{k}-padding-inline` / `--size-{k}-font-size`), así que
leen la coordenada y no el primitivo suelto — que es justo lo que
`recipe-css-contract` exige, y lo cazó: `group-heading-font-size` entró como
`var(--font-size-xs)` y salió como `var(--size-xs-font-size)`.
**El radio y el modo Dialog**, que el veredicto marcaba como ⚠ colisión y no lo
era: dentro de `<Command.Dialog>` el panel que el usuario ve ES el del dialog,
así que la regla de contexto reapunta el propio público a
`var(--dialog-content-radius)`. Leer el público de un hermano donde ese hermano
ES la superficie es composición correcta, no un segundo token — y de paso el
componente se queda sin privados (antes el radio vivía en `--_command-radius`).
**El hueco del scrollbar** se resuelve con el patrón de combobox:
`viewport-padding-inline` + `scrollbar-inset`, y la regla del overflow los
compone.
**Centinela: 50/57 tokens mueven un computed.** Los 7 restantes, adjudicados
uno a uno en el navegador, no por conjetura:
5 `loading-*`, `viewport-padding`, `empty-*` — sus partes NO están montadas
en la demo (el estado vacío, la barra de carga y el scroller interno son
condicionales). No se puede medir lo que no existe.
1 `input-placeholder-fg` — `::placeholder` es pseudo-elemento y
`getComputedStyle` sobre el nodo no lo ve. Límite del instrumento.
1 `input-border` — FALSO NEGATIVO del centinela: comprobado a mano tres
veces (con la misma congelación de transición que él usa) el token SÍ
alcanza, `oklch(0.3485)` → `rgb(1, 2, 3)`. No he encontrado por qué el
centinela lo pierde; queda anotado como defecto del instrumento.
`R-5.1 exception: inline-size: 100%` — el panel rellena su contenedor; es
mecánica de layout, no apariencia. Es el 2 % que falta.
audit PASS (R-5.3 incluida) · eidos-lint 0 invalid · rtl 0 · docs 0 ·
suite: el único rojo es el conocido `skin-media-player`
README con la sección «Talla y tema» (molde navigation-menu).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
bd032534be |
feat(census): los primitivos tipográficos se miden contra su capa — pieza 0 de F2-A
D-TH.2-b, la firma que va ANTES del bloque porque cambia el suelo del censo: medirla a mitad de camino falsearía todos los antes/después. `heading` marcaba «36 knobs, 0 % alcanzable» y eso no era deuda: era una lectura equivocada. Su receta resuelve cada eje como `var(--_heading-font-size, var(--style-h2-font-size))` — el privado es el escape POR INSTANCIA que el wrapper escribe desde una prop, y el named style es la superficie de tema, ya pública y viva. Acuñar `--heading-*` para espejarla sería un alias por eje × nivel: la clase que mató la purga del changelog §39. El criterio de qué ES un primitivo tipográfico se midió, no se supuso: o la receta SELECCIONA por `data-style` (`heading`, `text`, `s-text` — el named style es su API) o está atada entera a UN style (`code` → `--style-code-*`, `display` → `--style-hero-*`, `label` → `--style-label-*`). `code-block` queda FUERA a propósito: lee un par de tokens de style para su texto pero posee cromo de caja real, y eso sí es suyo. Detalle que costó una vuelta: la comprobación va ANTES de la de privado. Leer el privado primero puntúa el primitivo entero como inalcanzable cuando es completamente temable — por la capa que lo posee. antes 5.199 knobs · 1.837 públicos (37 %) · 894 privados · 232 sistema después 5.199 knobs · 1.837 públicos (38 %) · 788 privados · 338 sistema **Gate**: 106 knobs pasan de `private` a `system` en los SEIS primitivos y **cero** de los otros 156 se mueve — verificado componente a componente contra la versión en HEAD, no a ojo. Alcance `—` (nada que poseer) para heading, text y display; `s-text` al 100 %; `code` y `label` bajan a 7 y 2 knobs de deuda real. Informe regenerado (171 veredictos a mano intactos), veredicto de heading anotado como ejecutado, y recipe-contract §1 gana el párrafo que fija el criterio — incluida la frontera: un menú que lee `--style-label-font-family` para una etiqueta NO es un primitivo y no entra en el conjunto. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
5ee796fa98 |
docs(theming): el bloque F2-A preparado — ocho componentes, sus defectos y sus gates
La cola revisada se eleva a plan de bloque: pieza 0 de instrumento (D-TH.2-b: la familia tipográfica mide por --style-* — va primero porque cambia el suelo del censo), los ocho componentes con sus defectos MEDIDOS (censo) y la corrección que su veredicto §5 ya verificó contra el CSS, los gates de §7 por componente, y la revisión adversarial al cierre. 330 knobs hoy a 0 %; el bloque debe llevar el global de 37 % a ~42 %. Y al cerrarlo, el orden de lo que las firmas desbloquearon: el diseño de calendar-surface (220 knobs más), el mandato Field, y el resto de B1-B8. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
80b22fb60c |
docs(eidos): los README de componente hablan el idioma nuevo — claves sueltas
El codemod reescribe tokens COMPLETOS (`--{c}-clave`); un README que cita la
clave a pelo (`input-color`, `dot-color`) se le escapa. Barrido de los 180
README/CONTINUE contra los dos planes del codemod: 19 apariciones entre
backticks en 14 ficheros, de las que sólo 10 en 6 ficheros eran CLAVES — el
resto era la PROP `color` (los 9 roles), que está viva y no se toca. La
adjudicación fue con el contexto delante, no con un replace a ciegas: la línea
«Eidos añade `size`, `variant`, `color`» habla de la prop; la línea
«`gap`, `font-size`, `color`, `min-block-size`» lista claves.
Corregidos: chat-typing (2) · chat-composer (4) · chat-list (1) ·
password-field (1) · waveform (2, incluida la lista de contrato en su bloque
de código, anotada con el nombre viejo como crónica).
Re-barrido tras corregir: 0 restantes. `docs:check` 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
e20e8b8095 |
docs(theming): el informe habla el idioma nuevo — y el fleco del README, cerrado
Al rendir cuentas del repaso de los 7 salió un fleco: el README de gradient-builder mencionaba `checker-color` por su clave SUELTA, sin el prefijo `--gradient-builder-`, y el codemod busca tokens completos — quedó con el nombre muerto. Dos líneas corregidas, más las dos del §5 de su ficha. Y la causa de fondo: las 170 fichas del informe llevaban desde el codemod citando el contrato con los nombres viejos en sus secciones generadas. Regenerado entero (`--report`): 69 fichas cambian, los 171 veredictos §5 escritos a mano sobreviven (verificado), `docs:check` 0. El censo del informe registra 5.199 knobs · 1.837 públicos — cuatro menos que el suelo del codemod, que son EXACTAMENTE las cuatro declaraciones de hover bespoke que la firma 3 retiró (checkbox, splitter, switch, drp ×2 −1 nueva del checked). El alcance global sigue en 37 %. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
d27e2ac9fc |
docs(theming): la firma 3 al día — y el atajo que llevaba dos meses ganando
Cuatro componentes migrados con su medición, y el diagnóstico que cambió al ejecutar: el velo del sistema no llegaba a ninguno, y la culpa no era del hover propio sino de la regla base — el atajo `background:` pesa (0,1,0) contra el `:where()` (0,0,0) de archetypes.css y fija `background-image: none` para siempre. Queda escrito lo que NO he hecho y por qué: scroll-area pide decisión de morfo (su thumb no lleva arquetipo con velo), el barrido de las otras 141 declaraciones no entra en una firma que se dio sobre knobs concretos, y la demo del date-range-picker no monta `kind`, así que su overview no se puede medir. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
0c80a8efb8 |
fix(date-range-picker): el overview hereda el velo, y su demo no lo enseña
Firma 3, cuarto componente. Los cuatro nav del overview llevan `archetype: 'trigger'` y sus celdas `archetype: 'item'`: los dos reciben velo de `archetypes.css`. Y los dos lo tenían cancelado por la misma causa que el resto — `background: transparent` es el ATAJO y también fija `background-image: none`, con (0,1,0) contra el `:where()` (0,0,0). reglas base `background: transparent` → `background-color: transparent` reglas hover fuera las dos; el velo las sustituye contrato `hover-overview-nav-bg` y `hover-overview-cell-bg`, retiradas **La verificación aquí es más floja que en los tres anteriores, y conviene decirlo**: la demo monta UN picker sin `kind`, así que el overview de mes/año no se renderiza nunca y la sonda no puede medir el pintado (2 nodos vistos). El artefacto es la forma del CSS y el arquetipo leído del morfo, no un computed antes/después. El mecanismo sí está medido tres veces (checkbox, splitter, switch). Queda anotado un hueco de demo: `kind='month'` y `kind='year'` son la mitad de la API del picker (norma N-6) y la superficie de demo no los expone, así que esas partes no se pueden ver ni medir. La demo es el producto. audit PASS · censo 40 % · suite 434 (1 rojo conocido) · rtl 0 · docs 0 Nota aparte: las celdas conservan `background:` (atajo) en `[data-in-range]` y `[data-range-start/end]`. Son estado de rango con valencia, no hover neutro — fuera del alcance de esta firma, pero cancelan el velo igual. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
3219a19c3c |
fix(switch): el velo para el off, el swap de paleta para el checked
Firma 3, tercer componente, y el primero donde la regla servía DOS hovers a la
vez: `--_switch-track-bg-hover` resolvía al color neutro cuando el switch está
off y al `palette-solid-hover` cuando está checked. recipe-contract §2 los
separa —el neutro es la capa del sistema, el con valencia es el swap de la
receta— así que la regla se parte en dos:
`[data-switch]:hover` sólo el borde; el fondo lo pone
el velo de archetypes.css
`[data-switch][data-state=checked]:hover` `background-color:
var(--_switch-palette-solid-hover)`
Y la regla base pasa a `background-color:` por lo mismo de siempre: el atajo
fijaba `background-image: none` y ganaba al `:where()` del sistema.
Medido (377 valores, 7 estados): 2 diffs, los dos en el hover del track off —
el velo aparece y el fondo vuelve a su base. Verificado además en el navegador
que las DOS reglas viven y que el swap del checked sigue en pie.
`track-bg-off-hover` sale del contrato, y con él el privado que lo reenviaba.
censo switch 57 % · audit PASS · suite 434 (1 rojo conocido)
Nota de método: borrar la clave por línea partió una declaración multilínea
(clave y valor en líneas distintas) y dejó el valor huérfano — esbuild lo cazó
al regenerar. Un rename en sitio es seguro; un borrado necesita mirar si el
valor va debajo.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
c66b738101 |
fix(splitter): el asa recupera el velo del sistema
Firma 3, segundo componente, y la misma pareja de causas que el checkbox: el asa lleva `archetype: 'trigger'`, así que `archetypes.css` le pinta el velo, pero la regla base declaraba el ATAJO `background:` (0,1,0) y lo cancelaba contra el `:where()` (0,0,0) del sistema. Encima había un hover propio con su propio color. regla base `background:` → `background-color:` regla hover fuera entera; el velo la sustituye Medido (812 valores, 7 estados): 2 diffs, ambos en el hover del asa — `bg oklch(0.8853) → oklch(0.931)` y `img none → linear-gradient(rgb(0 0 0 / 0.08))`. Reposo, tallas, dragging, disabled y foco: idénticos. El estado `dragging` conserva su acento (`--splitter-active-handle-bg`), que es un hover CON VALENCIA y no le toca la capa neutra. `handle-bg-hover` sale del contrato: sin consumidores. censo splitter 100 % · audit PASS · suite 434 (1 rojo conocido) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
b35855c20d |
fix(checkbox): el velo del sistema vuelve a pintar — y el atajo era el culpable
Firma 3, primer componente. El checkbox tenía un hover propio
(`bg-off-hover`, un `color-mix` del surface al 82 %) de los que §38 deprecó, y
al quitarlo apareció que el velo del sistema no volvía. La causa no era el
hover: era la regla BASE.
`[data-checkbox] { background: var(--checkbox-bg-off) }` usa el ATAJO, que fija
`background-image: none`, y esa regla pesa (0,1,0) contra el `:where()` de
`archetypes.css`, que pesa (0,0,0). El velo perdía la cascada SIEMPRE — no en
hover, nunca. El hover bespoke no era un capricho: era la cicatriz de que el
velo del sistema no llegaba, y con él delante nadie lo notó.
Dos cambios, uno por causa:
regla base `background:` → `background-color:` (longhand), que deja libre
`background-image` para el velo
regla hover fuera el `background` propio; queda el `border-color`, que es
otro eje y otra decisión
Medido en el navegador (sonda de computed, 2.436 valores en 7 estados):
antes hover bg `srgb 0.974` · img `none` ← el velo, ausente
después hover bg `oklch(0.9911)` · img `linear-gradient(rgb(0 0 0 / 0.08))`
reposo intacto · tallas intactas · disabled intacto · foco intacto
8 diffs, todos en el hover y sólo en esas dos propiedades. El hover queda algo
MÁS visible que antes (0.911 efectivo contra 0.974): es la calibración del
sistema, que es justamente lo que la firma perseguía.
`bg-off-hover` sale del contrato: sin consumidores, y un alias público que
nadie consume rompe `recipe-css-contract`.
censo checkbox 47 % · audit PASS · eidos-lint 0 invalid · suite 434 (1 rojo
conocido) · rtl 0 · docs 0
No adjunto captura: el panel del navegador no está visible en esta sesión y el
screenshot expira. La evidencia es la medición de computed sobre Chromium real.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
476da2dd23 |
feat(eidos): el tercer muro — `color` como slot deja de compilar
Cierra el triple muro de D-TH.6, con el mismo molde que la normalización px/py del 2026-07-06: el codemod vacía la deuda, R-5.3 la vigila al correr el audit, y el tipo la impide mientras escribes. El muro de tipos es el único que no necesita que nadie ejecute nada. Ayer no podía entrar: disparaba sobre 17 claves de hover neutro que todavía decían `color`. El commit anterior las renombró al descubrir que no eran capa de estado, así que ahora el catálogo compila limpio y el muro sube. Las tres exenciones firmadas van DELETREADAS, no encajadas en un patrón laxo —una exención que no se puede leer es una que nadie puede auditar—: `*-focus-ring-color` (espeja la familia del sistema), `orb-color-*` (color como sustantivo) y `stop-color-*` (una parte de gradient-builder). Muta-prueba: `mutant-trigger-color` → NO COMPILA, con el mensaje que enseña la regla las tres exenciones → compilan catálogo actual → 0 errores en recipes Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
93017975ca |
fix(eidos): la capa de estado es un VELO — 38 hovers que no lo eran, al idioma
El codemod de ayer apartó 47 knobs «en cola de migración a la capa de estado».
Al empezar esa migración se ve que sólo SEIS lo estaban: el velo de §38 es
`background-image: linear-gradient(var(--state-hover), var(--state-hover))` y
no alcanza nada más. De los otros 41, veintidós mueven un BORDE y diecinueve
mueven la TINTA — ninguno es la capa de estado por muy neutro que sea su valor.
Eran deuda de nombre, y yo los había excluido del renombrado.
El clasificador tenía media prueba: miraba el VALOR (¿neutro o con valencia?) y
no la PROPIEDAD. Ahora exige las dos, siguiendo un salto por privado
(`--_switch-track-bg-hover`) y entre componentes (la familia calendar presta
`--calendar-control-*` a month-grid, range-calendar y year-grid, y por eso
esos cuatro no aparecían consumidos en su propia receta).
renombres 38 en 22 componentes · 113 referencias en 30 ficheros
cola real 6 (checkbox · date-range-picker ×2 · scroll-area · splitter ·
switch) — los únicos que pintan fondo
censo 162 · 5.203 · 1.841 (37 %) · 56 — IDÉNTICO
--names 38 → 0 desviadas
diff generated 38 renombres 1:1 · 0 cambios de valor
computed field · tabs contra la línea base ORIGINAL (anterior a las dos
pasadas): 4.031 valores, 16 estados, 0 diffs
huérfanos 0 · formato sin regresión · audit 163 PASS · rtl 0 · docs 0 ·
suite 434 pasan (1 rojo conocido) · check 0 errores en ficheros tocados
La muta-prueba se reescribió contra el catálogo vivo — asertaba nombres que el
codemod ya había renombrado — y gana los casos que faltaban: un knob de borde
neutro es `modifier`, uno de tinta neutro es `both`, y sólo los de fondo son
`state-layer`.
Queda una pregunta que §38 no contesta y que NO he decidido: si un hover de
borde o de tinta POR COMPONENTE debe existir siquiera. R-4.3 guarda
`background*` en `:hover`, nada más. Ahora al menos se llaman como deben
mientras se decide.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
e154b23fb9 |
docs(theming): el handoff al día — el bloque del vocabulario, cerrado
Los cuatro pasos hechos, con sus commits, y lo único que queda del bloque: el tercer muro (el tipo de `defineRecipes`) espera a que la migración a la capa de estado vacíe las 17 claves de hover neutro que todavía dicen `color`. Y las cuatro cosas que el bloque enseñó a base de costar vueltas: el plan se escribe antes de tocar nada o el verificador mide el vacío; un valor puede cambiar de texto sin cambiar de significado; prettier en masa reformatea deriva ajena (88 de 101 ficheros ya salían sucios por el CRLF del checkout); y un hover se clasifica por su VALOR, nunca por su nombre — cosa que este mismo handoff tenía mal. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
7781d6e4c8 |
feat(audit): R-5.3 — la gramática de los nombres deja de ser prosa
El canon de nombres llevaba meses documentado y sin guard, y la medición del
2026-07-01 ya decía qué le pasa a un canon así: deriva entre el 30 y el 85 %.
Había derivado. El codemod de ayer lo normalizó; esto es lo que impide que
vuelva.
R-5.3 valida una FORMA, no una lista — ahí se separa del guard de eventos, que
comprueba pertenencia a un vocabulario cerrado. La forma es: tinta = `fg`,
modificador interactivo DELANTE, y detrás lo dimensional y contextual.
No reimplementa la gramática: la consume de `theming-census --names`, que es
la misma fuente sobre la que corrió el codemod. Dos implementaciones de una
gramática son dos gramáticas que acaban discrepando — y este repo ya pagó esa
factura con `commit-resize`, un hook muerto tres meses en una receta con todos
los tests en verde.
Entra en `error` directo, sin rampa `warn`, porque su deuda murió en el mismo
pass (precedente R-4.4). Las claves en cola de migración a la capa de estado se
reportan APARTE: no son deuda de nombre, son knobs que van a desaparecer, y
renombrar lo condenado es churn.
Muta-prueba de tres caras, que es lo único que distingue un guard de un guard
que pasa sobre el vacío:
`-bg-hover` con valor de acento → ROJO
`trigger-color` → ROJO
`primary-solid-hover` → VERDE (canónica: COLOR_ROLE_SLOTS pone
el modificador detrás por construcción)
La tercera es la que importa: es el fallo que un codemod ingenuo habría
cometido sobre las 47 claves de rol, `button` entero incluido.
Doctrina en el mismo pass: recipe-contract §1 gana las dos filas que le
faltaban (tinta y estado) más la frase que las gobierna y las dos familias con
gramática propia; §4 gana la fila R-5.3; theming §6.7 una nota fechada que
acota el principio de plataforma del px/py a los ejes dimensionales. El
checklist de cierre declara la regla — lo cazó `docs:check` con su propio
guard I5, que exige que toda regla del audit esté declarada allí.
Lo que NO entra, y por qué: el tercer muro (el tipo en `defineRecipes`, molde
`PhysicalAxisKey`) está escrito y probado, y dispara sobre 17 claves — los
hovers neutros que la firma 3 manda migrar. Meterlo hoy rompería `npm run
check` a todo el mundo por una deuda que ya tiene dueño y fecha. Entra cuando
la migración a la capa de estado las vacíe; son dos líneas entonces.
component:audit 163 PASS · 3 NEEDS-WORK (badge, mockup, motion — los tres
sin tocar por esto, R-5.3 pasa en los 166)
docs:check 0
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
bd916ef3fb |
refactor(eidos): el catálogo habla un idioma — 269 claves al vocabulario firmado
D-TH.6, ejecutada. El slot de tinta pasa a `fg` y el modificador interactivo
se pone delante, que es lo que theming §6.7 r7 documentaba sin guard desde
que se escribió. Value-preserving: renombra la clave en `recipes/base.ts` y
sus 793 referencias en 101 ficheros; no toca un solo valor.
claves renombradas 269 en 66 componentes
referencias 793 en 101 ficheros
censo antes/después 162 recetas · 5.203 knobs · 1.841 públicos (37 %) · 56
sin contrato — IDÉNTICO, como debe ser un rename
--names antes/después 269 desviadas → 0
Qué NO entra, y por qué:
47 `{rol}-{slot-de-rol}` canónicas: COLOR_ROLE_SLOTS pone el modificador
detrás POR CONSTRUCCIÓN (`primary-solid-hover`)
13 exentas firmadas `--focus-ring-*` es familia del sistema; el color
de `aura` es un sustantivo; `stop-color` ES una
parte de gradient-builder
47 hovers neutros por VALOR: migran a la capa de estado
(§38 + R-4.3), no se renombran — firma 3
6 ocurrencias en historia changelog, errores-toxico, PLAN-affix/background:
reescribir un registro fechado lo vuelve mentira
El clasificador vive en el censo (`--names`), no en un script suelto, para que
el guard R-5.3 consuma la MISMA gramática que el codemod. Su muta-prueba
(`__names-mutatest.ts`, 27 casos) es lo que hizo el trabajo: cazó que yo
promovía al frente CUALQUIER valor declarado por el morfo, y así
`--sidebar-width-icon` (la anchura del raíl colapsado) se convertía en
`--sidebar-icon-width` (la anchura de un icono), que es otra cosa. La firma
dice «delante lo interactivo, detrás lo dimensional y contextual»: ahora sólo
promociona el vocabulario interactivo cerrado, y lo contextual —`below`,
`loaded`, `vertical`, `icon`— se queda donde estaba. Los cinco casos de
regresión están en la muta-prueba.
También cazó que `dropdown-menu.item-bg-hover` lee `var(--color-primary-element)`:
es un hover CON VALENCIA (el palette swap de recipe-contract §2), no el hover
bespoke que §38 deprecó. Clasificar por el nombre lo habría metido en una
migración que no le toca; se clasifica por el VALOR.
Verificación (§7.4, artefacto por paso):
diff de generated/ 269 renombres 1:1 · 0 cambios de valor · 22 privados
reapuntados a los nombres nuevos (`__names-verify-diff`)
computed tag-group · field · tabs: 6.467 valores en 23 estados,
0 diffs — y comprobado en el navegador que sirve el CSS
nuevo, para que ese 0 no sea el de una copia cacheada
huérfanos 0 en código
suite eidos 434 pasan · 1 rojo, el conocido (`skin-media-player`).
`recipe-css-contract` verde: es el guard que caza el
`var()` sin fallback a un nombre que ya no se emite,
o sea el fallo exacto de un rename a medias
component:audit 163 PASS · 3 NEEDS-WORK, los tres SIN TOCAR por esto
check 0 errores en ficheros tocados (los 73 globales son de
otras sesiones de la rama; se atribuye por fichero)
rtl:check 0 · docs:check 0
formato el renombrado no alarga ninguna línea: ningún fichero
tiene más líneas de 100 chars que antes, así que no se
pasa prettier — hacerlo reformateaba 300 ficheros de
deriva ajena
Entra aquí la corrección del repaso de los 7 ya hechos:
`gradient-builder.checker-color` → `checker-fg` (el damero de transparencia;
ahí `color` era slot). Sus `stop-color-*` no se tocan.
Queda para el paso siguiente: R-5.3 no puede graduar a `error` directo
mientras los 47 hovers neutros sigan hablando el idioma viejo — o migran
antes (firma 3, ya firmada), o el guard necesita una exención greppable.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
f09e04fab1 |
docs(theming): el acta — las catorce decisiones que faltaban, firmadas
El eje llevaba desde el 19 ejecutando sólo lo que no dependía de una firma.
Hoy se presentaron las catorce con recomendación fundada y el autor las firmó
en bloque. Quedan escritas donde se ejecutan: §4 del plan fila a fila, y una
sección de acta en el handoff con el QUÉ ejecutable de cada una.
La que cambió de sentido al presentarla fue D-TH.6. La pregunta no era
«normalizar hacia lo documentado» sino una CONTRADICCIÓN entre dos doctrinas
firmadas: theming §6.7 r7 dice que el slot de tinta es `fg`; el principio de
plataforma del codemod px/py («el token se llama como la propiedad») dice
`color`. Se adjudicó `fg`, y con una razón que acota el principio en vez de
romperlo: gobierna los ejes DIMENSIONALES, no la pareja `bg`/`fg` — si la
gobernara, `bg` tendría que llamarse `background-color`, y nadie lo propone.
De la misma lectura salió que el inventario heredado estaba inflado: de las
370 claves «desviadas», 47 son `{rol}-{slot-de-rol}` (`primary-solid-hover`,
`palette-hover`) donde COLOR_ROLE_SLOTS pone el modificador detrás POR
CONSTRUCCIÓN. Un codemod sobre las 370 las habría roto — `button` entero.
El inventario real es 301, y la gramática es POR FAMILIA, no única.
Los 22 «falsos amigos» resultaron ser tres cosas distintas: los que se quedan
(la familia del sistema `--focus-ring-*`, el color como sustantivo de `aura`,
y `stop-color` que ES una parte de gradient-builder), los que el morfo ya
resuelve (`partial`, `read`, `failed` son valores declarados de `data-state` y
`data-delivery`), y los dos `scrim-color-on-*`, que chocaban con el prefijo
canónico `on-` del acento y pasan a `scrim-fg-over-*`.
Y una que no era limpieza sino decisión: los hovers neutros NO se renombran,
MIGRAN a la capa de estado (§38 + R-4.3). Renombrarlos habría presumido que
sobreviven.
Con las firmas, la revisión de los 7 componentes ya corregidos contra la
gramática nueva: 6 limpios y una corrección (`gradient-builder.checker-color`
→ `checker-fg`), que entra en el codemod. Que salgan limpios no es suerte —
el backfill de este eje ya venía escribiendo `fg`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
51122efa32 |
docs(theming): mañana lo primero — el vocabulario de tokens: D-TH.6 (codemod) y R-5.3 (guard de gramática)
La conversación de hoy destapó que el backfill está escribiendo canon sobre un catálogo que habla al revés: el vocabulario de nombres existe (theming §6.7 + recipe-contract §1) pero no tiene guard, y la medición fundacional del 2026-07-01 ya dijo lo que le pasa a un canon defendido por prosa. Medido ahora con `scripts/__names-inventory.ts` (semilla del `--names` del censo): sobre 3.333 claves públicas hay 370 desviadas en 68 componentes — 125 con el modificador detrás, 215 con `color` como slot o segmento, 30 con ambas. Y dos falsos amigos que NO se renombran de oficio: `*-focus-ring-color` espeja la familia del sistema, y los `orb-color-*` de aura usan color como sustantivo. El handoff pone el bloque como primera tarea, con el orden que evita el error clásico: primero la FIRMA (D-TH.6, una decisión con el inventario delante), luego el codemod value-preserving con el triple muro del precedente px/py (renombres 1:1 en generated, censo idéntico, computed intacto en tres sondas), y sólo entonces el guard — R-5.3 como gramática y no como lista, porque el vocabulario de tokens es compositivo donde el de eventos era cerrado: la talla contra las siete canónicas, el modificador cerrado y delante, el slot contra sus tres fuentes, y la parte contra el morfo compilado como ya hace eidos-lint. En `error` directo: tras el codemod no hay deuda que tolerar, y la rampa warn es como estos guards mueren. Con muta-prueba antes de darlo por bueno. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
6e501f4fdf |
docs(blocks): handoff del día — cinco filas cerradas, y el eje que cambió el método
Deja escrito por dónde entrar mañana, en este orden: §0 EL EJE VIVO — el chasis de sección, que es lo que hay que retomar. Con la frase del autor que lo abrió («lo estás convirtiendo en una mierda a base de parches, ninguna es profesional ni de alcance») y su pregunta, que es la que tenía razón: si no debería haber un componente del que se basen todos los bloques. El censo que la confirmó (16 · 15 · 9 · 8 copias del Header), la firma (que CREZCA Section, no que nazca un hermano), por qué el nombre Block se descartó (ya significa «a todo el ancho» en el canon), los tres commits, las dos trampas que costaron —el Background que se capa dentro de la medida, y la raíz que extiende SectionProps y no HTMLAttributes— y las tres cosas que quedan: los 19 README describen el nido viejo, el contrato del tier no dice todavía que la raíz de un block ES un Section, y nada más. §1 LA COLA, REENCUADRADA — con el aviso de que más de la mitad de lo que sigue archivado bajo un block tiene la causa en el canon (A-48, A-53, A-60, A-62, A-65), así que bajarlas dentro de la frontera del tier fabrica parches. Y el trabajo honesto que sí queda dentro: A-74, A-96 y A-71. A-11 replanteada en su ficha por la misma razón: su arreglo profesional es que el canon publique el CATÁLOGO en runtime de sus escalas —como ya hace EIDOS_VARIANTS— y no siete líneas de exhaustividad repartidas por las demos. Queda medido lo que hoy está derivado: ContainerSize sin xxl, Breakpoint con 3 de 6, Decor con 5 de 8. docs:check 0/0 (813). Los avisos de prettier de los dos ficheros son preexistentes (CRLF), comprobado contra HEAD. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
cc98444343 |
uix(blocks): las diecisiete raíces que quedaban dejan de reescribir el chasis de sección
Cada block abría con el mismo nido escrito a mano: un <section {...rest}> sólo
para poder nombrarse, dentro Section para el aire, dentro Container para la
medida, y a veces un Background para la banda. Con Section ya crecido, la raíz
es una línea y el <section> duplicado desaparece: medido en las previews, el
documento pasa de 3 elementos <section> a 2 en cada block migrado.
Trece van al patrón entero (size + containerSize + decor):
article-grid, contact, faq, pricing, team, testimonials, feature-split,
logo-cloud, newsletter, más los dos de la primera tanda. Cuatro conservan algo
propio y sólo pierden el envoltorio:
- cta mantiene su Container, porque su medida lleva un paddingX condicional
(`full` significa EDGE TO EDGE y el gutter hay que quitarlo) que no es del
chasis.
- content-section no usa Container: su medida es una rejilla de cinco pistas.
- site-footer usa `as="footer"` — la sección de cierre de la página ES una
sección, con el mismo aire, la misma medida y la misma banda; lo que cambia
es el elemento, que es lo que la hace landmark `contentinfo`.
- hero conserva su Container en la variante `background`, y esto lo destapó la
MEDIDA, no la lectura: al pasarle `containerSize` su backdrop quedó atrapado
dentro de la medida y el hero salió con 128px de blanco a cada lado en vez de
a sangre. El `decor` del chasis se pinta FUERA del container justo por eso,
pero el de hero no es un patrón de tokens sino la media de la app bajo un
scrim, así que llega como hijo — y un hijo se capa con la copia. Queda escrito
en el sitio donde volvería a morder. Verificado después: fondo 1280 a sangre,
hijo directo de la sección, medida 1024 dentro, en hero y en stats-band.
Los tipos siguen la regla que ya estaba escrita en el types.ts de stats-band y
que la primera tanda confirmó: la raíz extiende los props del componente del
canon que envuelve —SectionProps— y nunca HTMLAttributes, cuya superficie
laxa choca con el style/class refinado del canon al esparcirse.
Gates: svelte-check 72/62 antes y después · vitest eidos+blocks 470/471 (el rojo
es skin-media-player, ajeno) · blocks:check 0/19 · docs:check 0/0. Medido en
navegador block a block: tag SECTION, aire, medida y patrón en su sitio, y el
hero en sus tres layouts.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
35216b591c |
fix(natural-time-picker): la anotación del literal volvió a su línea — prettier la había separado del valor
Regresión mía de ayer, cazada por el guard al preguntar si los nombres pasan por el canon. Al renombrar los privados abreviados a su nombre completo, tres declaraciones cruzaron el ancho de línea y prettier las partió en varias; la anotación que las justifica quedó en otra línea, y R-2.1 la exige en LA MISMA. Resultado: un componente que estaba en PASS pasó a NEEDS-WORK con tres colores crudos donde en realidad hay tres excepciones anotadas de tono fijo. Se arregla con `prettier-ignore` sobre cada declaración, que es lo único que no toca lo que importa: ni se acorta el comentario que documenta por qué el color es fijo, ni se cambia el valor. El diff de computed sigue vacío (6.438 valores, ocho estados) y el componente vuelve a PASS. La lección va al handoff, porque el error no fue el rename sino el ORDEN de la verificación: correr `component:audit` antes del formateo final deja pasar exactamente esta clase de regresión. 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 |
|
|
3bfe0d4130 |
docs(theming): revisadas las fichas 11-20 — veredicto verificado y corregido en cada una
Segunda tanda de revisión del eje theme-reach: command, table, media-player, tree-grid, field-langs, gradient-picker, listbox, carousel, heading y feed. Cifras reproducidas en las diez; la corrección va escrita en el bloque de veredicto de cada ficha, que sobrevive a la regeneración. Lo que la verificación cambió del plan mecánico: - Tres «colisiones» no eran colisiones: el radio de command es ADAPTACIÓN al Dialog que lo hospeda (lee un público de dialog, legítimo); el ancho del panel de gradient-picker es POR TALLA (20/18/22rem); y el título de feed son las redeclaraciones por talla de su propio privado. - Dos nombres mecánicos estaban mal: el `root-height` de table es la FILA (row-height, al bundle), y el `root-bg-image` de tree-grid son las GUÍAS de indentación — el knob es un color (`guide-color`), los cinco gradientes son de la receta. - Dos prefijos más de la misma plaga: media-player declara `--_mp-*` (el censo lo cuenta global) y field-langs `--_fls-*` — que además lee un privado DE FIELD con la fórmula del label un paso por debajo, sólo resoluble anidado: eso va al mandato field-composition, no se copia congelado. Y gradient-picker estampa `--gp-current-gradient` inline: mismo renombrado que gb. - Una regla de capa a punto de romperse: la propuesta de listbox acuñaba `font-size = var(--list-font-size)`, que es un público DE LA CAPA list-surface — duplicarlo es el vocabulario paralelo que la propia nota prohíbe. Retirada. - Y una lectura equivocada del 0 %: heading NO tiene deuda — es el primitivo tipográfico consumiendo la capa de named styles, que ES su superficie de tema (applyTypeScale la retunea en vivo). Acuñar --heading-* sería el alias por eje×nivel que la purga del §39 mató. Disposición propuesta: medir --style-* como sistema transversal para los primitivos tipográficos — decisión D-TH.2, y aplica a toda la familia B5. media-player trae además contexto que la propuesta ignoraba: su acento ya está firmado en el propio bloque de recetas como theme-stable (una escala cruda a propósito, para leerse sobre cualquier vídeo) — no se «corrige» a un rol. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
07778b597a |
uix(date-range-picker): la vista de década es suya y pasa a tokens; el calendario y el campo esperan su firma
Séptimo del eje theme-reach. Este componente es un híbrido de tres familias y el contrato lo dice ahora explícitamente: la vista de década/mes es invención suya y pasa a contrato público — separación, ancho, cabecera, navegación, rejilla y celdas, con los estados del rango separados por nombre en vez de amontonados en uno solo. Más el acento. Lo que NO se toca tiene dueño y esperando firma: la mitad de calendario lee `--calendar-*` y su corrección es la decisión de familia calendar que la auditoría del sistema dejó registrada; la entrada segmentada lee `--field-*` y le toca el mandato de composición de Field. Duplicar cualquiera de las dos bajo prefijo propio habría sido justo el alias que la purga de 2026-07 mató. Y el mismo defecto de prefijo que sus hermanos, aquí por partida doble: 44 referencias `--_date-field-*` y 9 `--_calendar-font-size` declaradas en ESTE CSS con el nombre de otro componente. Con un DateField o un Calendar anidados se habrían pisado. Renombradas, con el diff de computed en cero. Verificación: diff de computed vacío sobre 493 valores en ocho estados · component:audit PASS · suite eidos sin rojos nuevos · rtl:check 0 · docs:check 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
ecb23d6a48 |
uix(blocks): los ocho encabezados de sección eran el mismo, y uno ya había derivado
feature-grid, feature-split, pricing, testimonials, faq, article-grid, team y contact declaraban cada uno su `.Header` con las mismas tres líneas: un Box con maxWidth 48rem, un Motion trigger="viewport" y un Stack gap 3. Ocho ficheros, una sola idea. Y la copia se pagó donde siempre: article-grid había perdido su Motion. Su encabezado aparecía sin el reveal que tenían los siete hermanos, y nadie lo notó porque no hay dos secciones iguales delante a la vez. Componer el canon lo restaura — medido: su primer hijo estampa ya `data-animation-trigger`, igual que el de pricing. Los ocho pasan a componer `Section.Header`, que gana `align` para la única variación real: feature-grid hace llegar el eje de la sección al contenido del encabezado por contexto (A-84) y article-grid alinea a start. faq conserva su `marginX="auto"` en el call site. Ninguna parte pública desaparece: `Pricing.Header` sigue siendo `Pricing.Header`, sólo que ahora hay una implementación en vez de ocho. Medido en las ocho previews: 768px de medida (48rem) donde tocaba, faq en sus 736 por su propio centrado, y el alignItems de cada Stack en lo que su block pide — center en feature-grid, start en article-grid, stretch en el resto. Gates: svelte-check 72/62 antes y después · vitest src/uix/blocks 36/36 · blocks:check 0/19. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
68eb8af260 |
uix(time-range-picker): el reloj de dos columnas y la franja AM/PM pasan a tokens
Sexto del eje theme-reach, y gemelo de time-picker: mismo reparto, mismas razones. A contrato lo que el componente inventa — el reloj de dos columnas (su separación, ancho, tinta y tipografía), los colores de las tres manecillas y la franja AM/PM entera — y sin tocar el lado con forma de campo, que lee `--field-*` porque compone Field. Y el mismo defecto de prefijo que su gemelo: 56 referencias declaraban `--_time-field-*` dentro de este CSS (más una mención en el wrapper). Renombradas a `--_time-range-picker-*`, con el diff de computed en cero. Dos cosas que la revisión había marcado como acuñables y NO lo son, confirmadas al ejecutar: el realce del reloj al puntero lee `--toggle-palette-solid`, que es la paleta de otro componente — si procede, es un bloque de composición cruzada, y eso es decisión, no corrección; y el `padding-inline` del raíl consume el tamaño del pulgar del Slider, un eje que publica su dueño o no se publica. Verificación: diff de computed vacío sobre 725 valores en ocho estados · component:audit PASS · suite eidos sin rojos nuevos · rtl:check 0 · docs:check 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
e4fcdd62ee |
uix(time-picker): el reloj es suyo y pasa a tokens; el campo sigue siendo del Field
Quinto del eje theme-reach, y el primero donde la mitad del componente NO debe tokenizarse: time-picker compone Field, así que su parte con forma de campo lee `--field-*` por diseño y duplicar esos ejes bajo prefijo propio sería el vocabulario paralelo que el contrato prohíbe. Lo que sí es invención suya —la cara del reloj— pasa a contrato: separación, ancho, tinta y tipografía del reloj, los colores de las tres manecillas, el refuerzo del pulgar sobre la superficie del popover y el radio del trigger en línea. Más el acento, que ahora es público en vez de esconderse tras un privado. Y un defecto que el censo no podía ver: la receta declaraba sus privados con el prefijo de OTRO componente (`--_time-field-*`) dentro de su propio CSS. No era sólo cosmético — un TimeField anidado y este picker se habrían pisado el nombre. Son 44 referencias renombradas, con el diff de computed en cero. El 82 % que el censo sigue contando fuera son esos préstamos de Field: no es deriva, es un préstamo con dueño, y su corrección es el mandato de composición de Field que la auditoría del sistema dejó apuntado (§5.3-3), no este eje. El guard de huérfanos me corrigió por el camino: había declarado doce tokens de más, copiados del range-picker (la franja AM/PM y los ticks), que este componente no tiene. Retirados; sólo `trigger-radius` tenía consumidor real y se cableó. Verificación: diff de computed vacío sobre 580 valores en ocho estados · component:audit PASS · suite eidos sin rojos nuevos · rtl:check 0 · docs:check 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
4a27715170 |
uix(section): la sección de una página es un componente, no un esqueleto que cada block reescribe
Los 19 blocks del tier repetían el mismo chasis: un <section {...rest}> escrito a
mano, dentro Section (el aire), dentro Container (la medida) y a veces
Background (la decoración). Medido antes de tocar: containerSize recableado en
16 blocks, sectionSize en 15, decor en 9, y OCHO copias del mismo Header —
Box maxWidth 48rem + Motion viewport + Stack gap 3—, una de las cuales ya había
derivado en silencio: la de article-grid perdió su Motion.
La regla del propio tier dice que una parte se gana el compound cuando SE
REPITE. Aquí lo que se repite son los blocks, así que la repetición se gana un
componente. Y no uno nuevo: ese componente ya existía a medias y se llama
Section. Crece en vez de nacer un hermano — un segundo componente obligaría a
explicar en cada README en qué se diferencia de Section, y la explicación sería
«Section con tres props más».
Section gana, todo opcional: containerSize (compone Container DENTRO, y sin él
los hijos se renderizan igual que siempre), decor (pinta Background.Pattern
como HIJO, para que la banda sangre a todo el ancho por detrás de la medida sin
ganar caja) y la parte Section.Header.
Y renderiza un <section> real. Renderizaba un <div> a propósito, con la nota
«si un consumidor necesita un <section> de verdad para el landmark, que lo
envuelva» — y nueve blocks hacían exactamente eso, pagando un nodo extra para
poder nombrarse. Un <section> sin nombre NO es landmark (se expone como region
sólo al llevar nombre accesible, que es lo que A-109 dejó medido esta misma
semana), así que el tag no ensucia el árbol de nadie y la raíz duplicada
desaparece.
Para eso Box gana `as`, que cierra F21 («los primitivos de layout no pueden
cambiar de elemento»): lista CERRADA de elementos contenedores, nunca void —
es una escotilla semántica, no un prop de tag libre—, default 'div', y el
modelo de caja, las vars y la receta idénticos rinda lo que rinda. Los ≥2 casos
reales que la decisión original pedía para reconsiderarlo llegaron hace tiempo:
la columna de enlaces del pie que debía ser ul/li, y estos dieciséis blocks.
Migrados DOS como prueba, uno por clase: feature-grid (medida) y stats-band
(medida + decoración). Cada uno pierde dos niveles de anidamiento y su
<section> duplicado — medido en navegador: [data-section] ES el <section>, el
patrón 'grid' sigue siendo su hijo, 1280 de ancho, 64px de aire, 1024 de
medida, y el documento pasa de 3 elementos <section> a 2. Los otros 17 van en
tandas aparte.
⚠️ El paso de tipos que costó los únicos 5 errores de esta rama, y que lo
explica una regla ya escrita en el propio types.ts de stats-band: «sub-parts
extend the props of the canon component they wrap, never HTMLAttributes: the
raw attribute surface clashes with the canon's refined style/class when spread
through (the feature-grid lesson)». Ahora la RAÍZ también envuelve un
componente del canon, así que sigue la misma regla: FeatureGridProps y
StatsBandProps extienden SectionProps.
Gates: svelte-check 72/62 antes y después · vitest eidos+blocks 470/471 (el
rojo es skin-media-player, ajeno y documentado) · blocks:check 0/19 ·
docs:check 0/0. Prosa al día: README de Box y de Section, la demo de Section
(que anunciaba la limitación en cuatro sitios) y F21 cerrado en
PLAN-blocks-quality §6.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
ae91573c32 |
uix(proof-of-human): el cromo del reto se puede temar; la paloma y el cielo no, y eso ya estaba firmado
Cuarto del eje theme-reach. El reto ataba su escenario a primitivos globales: alto mínimo, radio, fondo, borde y los bordes de veredicto no los alcanzaba ningún tema. Ahora son 20 claves públicas, con el alto por talla en el canon de dos piezas y `parts: ['root']`, más la guía del trazo (corredor, línea, estela, marcador de meta) y el token arrastrable. Lo que NO entra es la escena — la paloma, la carta, los cielos del reloj — y no por falta de tiempo: el guard de recetas ya lista este componente en la excepción de tono fijo, y sus literales ya llevan anotación línea a línea. Un sello no se vuelve azul porque el tema lo haga. La revisión había anotado esto como decisión pendiente del autor; era una firma que ya existía y no había leído. Dos fallos míos que la verificación cazó y que quedan escritos porque son reutilizables. El primero: añadí un bloque `'proof-of-human'` a las recetas sin ver que ya había uno (el forward de paleta), y en un object literal la segunda clave gana — mi bloque se descartaba EN SILENCIO. Lo delató el diff de computed con 28 diferencias, empezando por un `border-radius` que caía a cero. El segundo: al fusionar los bloques perdí la última clave, y el guard de tokens fantasma lo cazó al instante. Verificación: diff de computed vacío sobre 406 valores en siete estados · component:audit PASS · suite eidos sin rojos nuevos · rtl:check 0 · docs:check 0. El centinela no vale aquí y así queda dicho: la demo declara su propio `min-block-size` y un degradado sobre el escenario, de modo que tapa dos de los tokens; los que no pisa responden. El 86 % que el censo sigue contando fuera son los dos ficheros de la escena, que enganchan por CLASE en vez de por data-attr — migrarlos es otro eje, anotado. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
e774ca7e84 |
uix(natural-time-picker): el cromo de la banda se puede temar, y el cielo sigue siendo el cielo
Tercero del eje theme-reach. La receta ataba todo a primitivos globales y guardaba su geometría en privados con un prefijo abreviado, `--_ntp-*`, que ni el censo veía ni el vocabulario permite: alcance 0 %. Ahora son 60 claves públicas y el 61 % medido. Dos de los tres bloqueos que la revisión había anotado no existían: la doctrina ya estaba escrita y no la había leído. El guard de recetas ya lista este componente en la excepción de TONO FIJO — un cielo no cambia con el tema, y sus literales ya llevaban su válvula línea a línea, así que los cielos, la tinta del sol/luna y la línea de posición se quedan privados y anotados, sin decisión que pedir. Y el panel sigue leyendo `--popover-*` porque su propia cabecera dice que esa superficie ES el flotante canónico: duplicarla con prefijo propio habría sido el vocabulario paralelo que el contrato prohíbe. De ahí que el 39 % restante no sea deuda sino préstamo deliberado. Lo que sí es suyo pasa a contrato: banda y knob por talla con `parts: ['panel']` —conservando su derivación de la altura de control, así que la expresión sigue siendo el knob y mover la talla arrastra ambos—, la línea indicadora, los ticks, el readout (hora, meridiem, periodo), los steppers, los chips y el trigger. Más el `panel-gap`, que sí es del panel y no del popover. Las 37 referencias `--_ntp-*` mueren: el prefijo de un privado es el nombre completo del componente (theming §6 regla 5). El rename alcanza receta, wrapper, README y el propio guard que las citaba. Verificación: diff de computed vacío sobre 6.438 valores en ocho estados. Centinela 40/62 automático, y cada uno de los marcados muertos verificado a mano: banda, knob, gap del panel, línea, radio de la línea e inset de los ticks alcanzan; los `trigger-*` NO, porque `[data-popover-trigger]` gana la cascada y viste el trigger — ya eran inertes antes de tokenizarlos, y ahora está dicho en el README en vez de fingir que el token pinta. Guards: component:audit PASS · eidos-lint 0 invalid · suite eidos sin rojos nuevos · rtl:check 0 · docs:check 0 · el guard del bundle obligó a apuntar `tick-font-size` a la coordenada `--size-xxs-font-size`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
cef2d32f16 |
uix(combobox): el campo y su panel pasan a tokens públicos, con la talla resuelta donde manda
Segundo del eje theme-reach. El combobox tenía UN token propio (`content-z`) y
todo lo demás atado a primitivos globales o enterrado en privados: alcance 0 %.
Ahora son 106 claves públicas y el 76 % medido — lo que queda fuera son tres
hovers que esperan firma, seis literales de layout y doce knobs que pasan por
privados que ya derivan de un público.
La talla sigue el canon de dos piezas (coordenada + nombre resuelto), pero con
una diferencia que el DOM impone: los resueltos del control se emiten sobre
`control` e `input`, no sobre el root, porque el `data-size` que manda vive en el
control — y el input suelto lo necesita fuera de él. Los del panel se emiten
sobre `content`, que viaja por portal y nunca vería un token del root. Es el
precedente de `select` aplicado donde toca.
Altura, tipografía y separación del control consumen la coordenada del bundle
`--size-{k}-*`; el relleno inline SE DESVÍA del bundle en cuatro tallas y
conserva su valor de hoy — la desviación queda visible en el contrato en vez de
redondearse a la coordenada, que habría sido un cambio visual disfrazado de
limpieza. El chip conserva sus expresiones (`control-height − inset`,
`control-font-size − offset`), así que mover el control sigue arrastrándolo.
El centinela cazó un fallo real a mitad del trabajo: la primera pasada dejó
vivos los ocho bloques `[data-…][data-size='…']` de la receta, que pisaban los
tokens nuevos con los valores viejos. El diff de computed daba cero JUSTAMENTE
porque la ruta vieja seguía mandando; sin centinela habría pasado por bueno.
Verificación: diff de computed vacío sobre 1.566 valores en ocho estados —
reposo, las cinco tallas, el panel ABIERTO y hover. Centinela 67/106 automático
y el resto verificado a mano: `content-z` alcanza (80 → 4321) aunque viva en el
wrapper flotante; placeholder, disabled e ink del trigger forzando su estado;
los del panel abriéndolo por teclado. Los siete nombres resueltos no se mueven
por diseño — el tema mueve la coordenada. Los catorce `selected-tag-*`, los tres
`separator-*` y `scrollbar-inset` no son verificables en esta demo (no monta
modo múltiple, ni separadores, ni scroll): queda dicho, con su consumidor
comprobado en la receta, en vez de darlos por buenos.
Un token nace inerte y se anota como tal: `content-font-family` y
`content-line-height` los pisa `[data-depth='overlay']` con la misma
especificidad — es la tipografía de portal del Build contract, y tocarla sería
otro eje.
Guards: component:audit PASS · eidos-lint 0 invalid · suite eidos sin rojos
nuevos · rtl:check 0 · docs:check 0 · el guard del bundle obligó a apuntar
`indicator-size` e `item-indicator-size` a `--size-sm-icon-size`.
El informe `docs/audit/theming/` se regenera entero, así que todas las fichas
actualizan su fecha de medida; las que cambian de contenido son combobox y
gradient-builder.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
18165e74ed |
uix(blocks-demos): la vista previa nace con el estado vivo de ActiveUix, no con los defaults
El iframe de la vista previa es OTRO documento con OTRO runtime (BootUix corre
su propio createActiveUix), así que el estado de la sección no cruza el marco:
la rama de 375/768 se abría SIEMPRE en claro, y en 9 demos siempre en LTR, con
la topbar en oscuro y RTL delante (A-29). Dentro del documento los blocks
siempre atendieron a ActiveUix — el shell conduce el framework por
uix.prefs.setIntent y el modeSource de eidos —; lo que faltaba era la única
serialización que existe entre dos runtimes: la URL.
El arreglo va en el ARNÉS, no demo a demo. BlockDemo lee el estado VIVO por la
superficie sancionada del framework — readActiveUixPrefsSlot(uix.prefs,
'direction'|'language')?.get(), las lecturas por dimensión que son rune-backed
(contador $state por dimensión), y eidos.getThemeContext().mode, que lee el
modeSource del shell — y compone la URL final: los props del BLOCK los pone el
demo, los EJES los pone el arnés. El {#key} y el enlace «abrir ↗» pasan a la
URL resuelta, así que mover un toggle global re-monta el iframe con los params
nuevos y el enlace abre lo que estás viendo.
Retirados los 11 controles «dir (solo la vista previa)» de banner, contact,
cta, feature-grid, feature-split, hero, newsletter, site-footer, site-header,
stats-band y team: eran el eje de sección repetido por demo — la regla del
arnés dice que tema, idioma y dirección los da el shell — y no tocaban prefs,
sólo escribían la URL. El shell queda como único dueño de los ejes; una
precedencia demo-gana habría dejado el toggle global inerte en esas páginas.
Dos correcciones a la ficha: testimonials ya derivaba su URL (el literal
desnudo quedó atrás en F2b), y el alcance real era el sistémico que la propia
ficha apuntaba — mode= no viajaba en NINGUNO de los 19 demos y dir= faltaba en
9. La pata lang sigue siendo inerte salvo en contact (blocks.* registrado),
pero viaja igual: es un eje del shell.
Verificado con la topbar REAL de la sección, no con params a mano: en oscuro +
RTL, el iframe de pricing a 375 arranca base-dark + dir=rtl (tinta
oklch(0.9491 0 0)) y el de hero — que tenía control local — hereda igual; en el
estado por defecto los dos vuelven a base-light + ltr. La URL sale con los
props del block primero y mode/dir/lang detrás.
Gates: svelte-check 72/62 antes y después · blocks:check 0/19 · docs:check
0/0. Huella: BlockDemo +31, cada demo −18 (−192/+39 en total).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
2 months ago |
|
|
3cdb5b5f03 |
uix(gradient-builder): el editor entero pasa a tokens públicos — un tema lo mueve sin tocar el sistema
Primer componente del eje theme-reach, y su piloto: la receta declaraba CERO
tokens propios («self-contained», decía la cabecera) y ataba cada knob a un
primitivo global o a un privado. Alcance medido: 0 %. Un tema no podía cambiar
ni el alto del preview ni el radio de la tarjeta sin mover el ecosistema.
Ahora los 72 knobs son contrato público del componente. Los ejes dimensionales
siguen el canon de dos piezas que firmó el Sidebar: coordenadas por talla
(`preview-height-{sm,md,lg}`…) más UN nombre resuelto (`host` = md, `size:sm`,
`size:lg`) que es lo único que consume el CSS — así los bloques `[data-size]`
de la receta desaparecen y la cascada la emite el TSC. La geometría se queda
PROPIA (px × `--scaling`): un raíl de edición no tiene coordenada de
control-height, así que forzarlo al bundle habría sido rediseño. La tipografía
sí lo consume, que es lo que el guard `recipe-css-contract` exige.
De paso mueren dos abreviaturas que escondían tokens al censo y podían cruzarse
por anidamiento: `--gb-checker*` y `--gb-stop-fill` pasan a
`--_gradient-builder-*` (también en el wrapper). El damero de transparencia se
parametriza por `checker-color` + `checker-cell`, y el patrón de cuatro
gradientes se queda privado — un tema cambia el color de la celda, no reescribe
la rampa. El `padding` shorthand se parte en ejes lógicos.
El default no se mueve: sonda antes/después con 6.612 valores computados
comparados en 7 estados (reposo · cinco tallas · hover) sobre 34 nodos, diff
VACÍO. El único diff que apareció era la transición de hover de un Button
compuesto capturada a mitad — demostrado reproduciendo el valor interpolado en
el árbol ya modificado, bajando la espera de asentamiento.
Centinela: 72/72 tokens alcanzan. 49 desde el ámbito del componente en reposo;
las 12 coordenadas de talla y las 2 de preset seleccionado forzando su estado;
`hover-preset-border` con hover real; y los 4 del editor de color sólo desde
`:root`, porque el panel viaja por portal — anotado en el README para que nadie
lo lea como un token muerto.
Dos hallazgos que quedan anotados, no corregidos (mueven píxel, así que son
decisión): el wrapper pinta el preview con el shorthand `background` inline, que
resetea `background-image` y tapa el damero, de modo que la transparencia no
llega a verse; y el foco de la parada usa el patrón de dos anillos con
box-shadow cuando §32 canonizó `outline`.
Guards: component:audit PASS · eidos-lint 0 invalid · suite eidos sin rojos
nuevos (queda el conocido skin-media-player) · rtl:check 0 · docs:check 0 ·
check sin errores en los ficheros tocados · censo 0 % → 80 % (el resto son
privados que YA derivan de públicos, el anillo de foco y tres literales de
layout) · captura 2× revisada.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
2591ec2f03 |
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta
El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear. Ahora `theming-census.ts --report` escribe la auditoría entera bajo `docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162 recetas con CSS + 8 componentes sin receta, el árbol completo de `eidos/components/`. Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector (agrupados por clase), sistema transversal aparte, los privados con la columna que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado, un token prestado de otra receta, un shorthand o un eje físico. Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el componente que componen (icon-button → button), la capa que consumen (affix → viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button). Defectos del instrumento corregidos en el mismo pase, todos encontrados mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía como estado y producía `hover-disabled-*`; un knob que lee un privado se proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se reportaba como préstamo del componente `icon` (ahora el préstamo se verifica contra las claves del dueño en `base.ts`); y el default de talla salía sin nombrar en vez de `-md`. Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 · 1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0 diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs · prettier limpio (el árbol generado entra en .prettierignore junto a eidos/generated). Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco correcciones — dos de ellas evitaban romper el default), y 5 que no se tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado declarados en su propio CSS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
48bad6c726 |
uix(feature-split): una sección se nombra por su encabezado, y sus filas cuelgan de él
feature-split era el único block de sección del tier sin parte .Header. Sus
filas emitían h2 por defecto, así que una sección de tres filas aportaba tres
h2 hermanos al outline y la sección misma no tenía nombre (A-110). La página
compuesta lo rodeaba a mano: encabezado escrito como primer hijo y un level={3}
repetido fila a fila.
El h2 de las filas no era un descuido: era una decisión ESCRITA en el README
del block —«.Title emits h2 by default (each row is a section-level statement
of its own)… drop it to 3 when the page already put an h2 above the rows»—. Se
deroga en sitio, y no por gusto sino porque el resto del tier declara lo
contrario: FeatureGrid.ItemTitle, las preguntas de faq y Pricing.PlanName ponen
en level 3 lo que se repite, y el h2 lo lleva el Header.
Nace FeatureSplit.Header, calcado de Testimonials.Header (Box con medida +
Motion + Stack) y sin contexto, porque este block no tiene eje align que
heredar. Y .Title baja su default de 2 a 3. La forma queda coherente en los dos
casos, que es lo que la hace preferible a las alternativas: con .Header el h2 es
suyo y las filas cuelgan; sin .Header la app ya puso el h2 encima y las filas
cuelgan igual. La tercera vía —un contexto donde el Header marcase su presencia
y el Title derivara su nivel— daba el mismo resultado a cambio de un hijo
escribiendo en el contexto del padre, que es el patrón que ya costó un bucle de
$effect en este repo. Por un solo bit, no compensa.
Verificado en navegador, outline leído del DOM:
/blocks/feature-split/preview h2 «Del evento crudo a la decisión»
h3 ×3 (las filas) ← antes: h2 ×3, sin nombre
/blocks/landing h2 + h3 ×2, dentro de un outline de página
que ya no salta de nivel
La demo compone su encabezado y LandingSite deja de hacerlo a mano: fuera el
Stack escrito a pelo y fuera los dos level={3}. Header medido en su sitio (x
144, 768 de ancho: la misma medida que la copia de las filas).
Gates: svelte-check 72/62 antes y después · vitest src/uix/blocks 36/36 ·
blocks:check verde (19 blocks, 147 ficheros) · docs:check 0/0.
Primera fila del ledger arreglada DENTRO del tier, por acotación del eje: las
tres anteriores de la sesión (A-111, A-112, A-109) eran todas de canon.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
1e3a67ad39 |
uix(banner): la tira de aviso no es la cabecera del sitio, así que deja de reclamar su landmark
Toda página con cabecera exponía DOS landmarks banner: el <header> del
site-header y la tira de aviso, que estampaba role="banner" a mano. ARIA
reserva ese rol para la cabecera DEL SITIO —identidad, búsqueda del sitio— y
dice que un documento debería marcar como mucho uno (A-109).
Las dos salidas que la ficha proponía estaban mal planteadas, y lo destapó una
revisión adversarial de tres refutadores independientes:
- «Estampar el rol ANTES de los rest props, que es la clase naming donde gana
el consumidor» contradice la doctrina que la propia ficha citaba: role es
clase CONTRATO por nombre de atributo (morfo/types.ts:811-832;
ARIA_NAMING_ATTRS contiene sólo aria-label). El runtime siempre gana ahí, y
con razón: un data-state o un role pisables son un componente que miente.
- Y el orden de estampado NUNCA fue el mecanismo. Medido: el segundo landmark
es el <header> del site-header, que no lleva atributo role ninguno — recibe
banner IMPLÍCITO por ser una <header> fuera de un elemento de sección. Un rol
pisable habría dejado quitarlo caso por caso, empujando el arreglo a cada
app, en vez de arreglarlo por defecto.
- El morfo, además, nunca declaró el rol (aria: []): era un literal del wrapper
de eidos que el README daba por contrato de la parte. Drift morfo↔eidos que
se queda sin objeto.
Lo firmado: la tira deja de reclamar el landmark. defaultElement header→section
en el morfo, fuera el role literal. Un <section> es region SÓLO cuando tiene
nombre, así que el aria-label del consumidor la hace encontrable y su ausencia
la deja como contenido plano — que es exactamente lo que el censo de landmarks
de A-111 ya sancionaba por escrito.
⚠️ SIN nombre por defecto, y esta es la corrección que la revisión me obligó a
hacer sobre mi propia propuesta: yo había planteado un default traducido
(«Aviso»), copiando el patrón de skeleton/spinner. Habría convertido TODA tira
en landmark sin salida — /temas/grafito apila cuatro y habrían salido cuatro
region con el mismo nombre, que es la clase de defecto de A-111 con otro traje.
El precedente de <nav> no transfiere: un <nav> es SIEMPRE landmark y por eso
pide nombre; un <section> lo es PORQUE lo tiene. Quien quiera una tira
encontrable, la nombra.
Derogadas por escrito las dos afirmaciones que decían lo contrario, porque
existían y estaban firmadas: el README del canon («Banner is a landmark for
page-level announcements — site header, persistent notice, system status bar»)
y el «✓ ejemplar» que docs/audit/components/banner.md le puso a ese contrato el
2026-07-07. La otra mitad de aquella decisión —no role="alert", el feedback
transitorio es de Toast— sigue en pie y se dice.
Verificado con el árbol AX de Chrome por CDP, servidor limpio:
/blocks/landing banner ['Aviso del producto','Acme'] → banner ['Acme']
+ region ['Aviso del producto']
/blocks/banner/preview banner ['Principal'] · region ['Aviso del producto']
/temas/grafito banner [] · region [] — las cuatro tiras sin nombre
dejan de ser landmarks, que es lo correcto
Píxel idéntico: la receta selecciona [data-banner], no el elemento — tira a
sangre 1280×54 con el mismo fondo, capturada y mirada. Ningún test, guard, CSS
ni consulta del repo seleccionaba por role=banner ni por el tag.
Gates: svelte-check 72/62 antes y después · vitest eidos+blocks+morfo 692/693
(el rojo es skin-media-player, ajeno y documentado) · blocks:check 0/19 ·
docs:check 0/0 (644) · morfo:check PASS en banner. Prosa re-sincronizada en 11
sitios: los README de canon, block y app-shell, la ficha de auditoría, las tres
demos, landing y su README.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
07a99679d7 |
docs(theming): el plan del eje theme-reach — medir qué alcanza un tema, y corregir componente a componente
El autor, al ver que el radio del trigger del nav vivía en un privado clavado a
--radius-default: «¿los componentes son themables? si no lo son, es un error
como framework». Lo es. La doctrina ya exige que cada receta declare sus knobs
en recipes/base.ts (recipe-contract §1, theming §6), pero R-1…R-4 sólo
comprueban «sin literales», no «alcanzable por un tema»: una receta con todo en
var(--radius-md) y privados pasa component:audit en PASS y sólo se puede temar
moviendo el sistema entero.
Medido, no opinado — scripts/theming-census.ts (instrumento, NO guard): de 5.205
knobs de apariencia en 162 recetas, 1.621 (33 %) pasan por un token público del
componente; 1.880 atan directo a un primitivo global, 856 a privados, 614 son
literales; 62 componentes no tienen una sola entrada en el contrato; 59 tienen
alcance < 20 %; 6 al 100 %. navigation-menu, tras todo el día de hoy, 44 %.
El plan (PLAN-theming.md): §1 el censo por familia y por componente (tabla
regenerable, no editable) · §2 los cuatro ejes de «personalizable» (tokens ·
talla · color/variante · estados) · §3 la regla R-5 «alcance de tema» para
component-audit, con su válvula de excepción · §4 ocho decisiones D-TH para el
autor (R-5 dura · perímetro de knob · WIP · orden · DEFAULT IDÉNTICO · normalizar
nombres de slot, que hoy incumplen tabs y el propio nav · cuándo gradúa a error ·
panel de temas) · §5 fases F0–F4 con gates · §6 deuda transversal · §7 el
PROTOCOLO DE VERIFICACIÓN por componente, porque «lo ejecutará Opus, y Opus falla
mucho»: cinco preguntas, medir ANTES (computed por parte/estado/talla, sonda
headless desde la raíz, fuera de callbacks de MutationObserver), editar sólo la
forma firmada, medir DESPUÉS (diff de computed = 0, prueba de CENTINELA por cada
token nuevo, desde el píxel hacia arriba, registro de animationstart/end),
guards por fichero, un componente = un commit con sus artefactos, y revisión
adversarial por bloque. Cada paso tiene un artefacto; sin artefacto no está hecho.
El instrumento se probó por mutación antes de creerle, y falló la primera vez:
no contaba una regla de una sola línea (`[x] { padding-inline: 8px; }`), así que
meter un literal no movía la cifra. Regex endurecido; ahora 6→7→8 con una y dos
mutaciones. Las cifras del plan son las del instrumento final, y el handoff
(CONTINUE-theming.md) obliga a reproducirlas antes de seguir.
Nada firmado, nada construido. Gates del commit: docs:check 0/642 · prettier del
script limpio · tsc del script sin errores · el plan cita ficheros que existen
(los once, comprobados).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
a827948459 |
uix(navigation-menu): el hover es el del sistema — la misma capa, sobre la forma de cada elemento
«El estilo tiene que ser el mismo para todos los elementos. ¿De dónde sacas esos estilos?» De ningún sitio que valiera: en |
2 months ago |
|
|
12796ac428 |
uix(navigation-menu): los enlaces del panel hablan el mismo hover que la barra
«¿Por qué los otros ítems no tienen el mismo hover? ¿Qué sentido tiene?»
Ninguno. En
|
2 months ago |
|
|
822b78cf2c |
uix(navigation-menu): el panel sube como lo que es, y la caja gris del hover era el <li>
Dos correcciones del autor sobre |
2 months ago |
|
|
ae9e277fad |
uix(navigation-menu): el botón «aparecía» en vez del panel, y la forma del trigger no la podía tocar ningún tema
Dos defectos que el autor señaló en el megamenú, y los dos anteriores a T-1.
1 · EL TRANSLATE. `emerge-open` / `emerge-close` se estampaban en el TRIGGER,
así que la firma genérica `[data-event^='emerge-open'] { animation: present-rise }`
corría sobre el BOTÓN: opacidad 0→1 y 8px de translate en el control, mientras
el panel —la superficie que entra en el campo— no hacía nada. Medido: a +8 ms el
trigger con `present-rise` y `translateY(5.5px)`, a +120 todavía vivo.
La premisa del target era real («el content se monta al abrir, un sello dirigido
a él muere») y la receta A-36 ya la respondía, por eso dropdown-menu, popover,
context-menu y dialog declaran `content` y ninguno declara `trigger`:
`open` es `post` —el flip monta el panel y el runtime difiere el emit pasado
`tick()`— y `close` es `pre` —el panel sigue ahí para anunciarlo—, con
`targetFallback: [trigger]` para la carrera en que ya no está. Resolución por
estado de montaje, DECLARADA, no un fallback a mano en el provider.
navigation-menu era el último que la arrastraba. Ahora el trigger queda sin
evento, sin animación y sin transform en todos los instantes; el panel se lleva
la aparición. La suite lo fija en las dos ramas (content montado · fallback).
Al mover el sello salió un doble que habría enviado: el panel tiene entrada
propia (slide+scale+fade, 120 ms) y la firma se la pisaba durante el hold; al
retirarse el sello, la de la receta arrancaba DE CERO. El panel entraba dos
veces, 320 + 120 ms. Precedente exacto en tooltip («the preset entrance wins
over the global present-rise signature», medido allí el 13-08): la entrada
propia gana por especificidad. Registro de animaciones tras el arreglo: una sola
entrada de 116 ms, nada en el botón.
2 · LA FORMA. El radio del trigger vivía en un PRIVADO (`--_navigation-menu-
radius`) clavado a `--radius-default` —«decoupled from size (Radix model)», una
línea que leyó a Radix como regla cuando Radix no pinta nada—, la altura salía
de `padding-block: var(--space-2)` (un token de ESPACIO como altura de CONTROL:
medido 33,5 / 36 / 41 px en sm/md/lg, sólo `md` en la escala por aritmética
casual) y los rellenos de hover/abierto estaban escritos a mano. El contrato del
componente publicaba tres knobs (content-z, indicator-w/x): ningún tema podía
decir «píldora» sin mover el radio del sistema entero. Por el TSC y por lo que
hacen Button (`--button-radius-*`), dropdown-menu (`item-*`) y el Sidebar de
ayer (`--sidebar-row-*` por talla), eso es un error, y el autor lo firmó así:
themable, default el actual.
Ahora el trigger (y el Link, que comparte su cromo) toma el bundle canónico
`--size-{k}-*` a través de tokens públicos por talla —`--navigation-menu-
trigger-{height,font-size,line-height,padding-inline,radius}` con sus pasos
`-{sm,md,lg}` y el nombre resuelto por `data-size`— más `-bg-active` /
`-fg-active` para el acento de abierto / aria-current. A `md` el default es
exactamente lo que traía: 36 px, 6 px de radio. Medido antes → después: altura
33,5/36/41 → 30/36/44; radio 6/6/6 → 6/6/10. Y un tema decide la forma en una
línea: `--navigation-menu-trigger-radius: var(--radius-full)` en el root → 9999px
en el trigger, medido.
Y el hover: la receta pintaba `background: var(--color-surface-overlay)` con el
SHORTHAND, que resetea `background-image` —donde vive la capa de estado canónica
que `archetypes.css` ya da a todo `trigger` (R-4.3)—. Medido: `background-image:
none` en hover, la capa muerta y una superficie a mano en su lugar; y el trigger
ABIERTO sin ninguna respuesta al puntero (el mismo bug que la fila activa del
Sidebar, ayer). La regla de hover desaparece (la capa ya está) y el acento usa
`background-color`, así que compone encima: medido, hover cerrado = sólo la
capa; hover abierto = acento + capa.
Fuera del alcance y sin tocar: los enlaces DENTRO del panel
(`[data-navigation-menu-content] :is(a, …)`) conservan su `background`
shorthand — otra superficie, otro selector, otro día.
Gates: check 72 con CERO en los ficheros tocados · soma nav 9/9 · sema+morfo
550/550 · eidos 434/435 (el rojo es `skin-media-player`, ajeno, el mismo que
documenta el handoff del sidebar) · eidos-lint 31 morfo-backed / 0 invalid ·
component:audit PASS · rtl:check 0 · docs:check 0/640 · generated/base.css +41
líneas, sólo las del nav.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |