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>
## 3. Privados de la receta — ¿de dónde sale su valor?
| privado | declaraciones | valor(es) | origen | ¿deriva de un público? |
| --- | ---: | --- | --- | :-: |
| `--_gradient-picker-trigger-height` | 3 | `var(--control-height-md)`, `var(--control-height-sm)`, `var(--control-height-lg)` | global | no |
| `--_gradient-picker-trigger-padding-inline` | 3 | `var(--space-2-5)`, `var(--space-2)`, `var(--space-3)` | global | no |
| `--_gradient-picker-trigger-font-size` | 3 | `var(--font-size-md)`, `var(--font-size-sm)`, `var(--font-size-lg)` | global | no |
| `--_gradient-picker-chip-size` | 3 | `calc(var(--control-height-md) - var(--space-2))`, `calc(var(--control-height-sm) - var(--space-2))`, `calc(var(--control-height-lg) - var(--space-2))` | global | no |
_La receta no declara privados propios en su CSS._
## 4. Propuesta de corrección
- **Consume la capa compartida `picker-shell`.** Un eje que la capa posee se consume como `var(--_x, var(--x))`; el consumidor **no acuña**`--gradient-picker-{eje}` para él — sería un vocabulario paralelo (README de `eidos/components`, «Capas compartidas» regla 2).
- **Tiene eje `size`**: los tokens dimensionales van por talla (`{part}-{eje}-{k}`) apuntando al bundle `--size-{k}-*`, nunca al primitivo crudo (theming §5; el guard `recipe-css-contract` prohíbe el primitivo).
### 4.1 Tokens a declarar en `lib/recipes/base.ts` (41)
### 4.1 Tokens a declarar en `lib/recipes/base.ts` (2)
Valor **verbatim** del CSS de hoy: el default no se mueve, sólo cambia quién
puede moverlo. Nombres derivados de recipe-contract §1 (ejes lógicos, talla
@ -96,61 +55,12 @@ no distingue lo que debería — se marca `⚠`.
- **⚠ decisión: `border` es shorthand o eje físico — hay que partirlo en ejes lógicos antes de nombrarlo (recipe-contract §1, R-4.4)** — 4: `border`.
- **foco: lo posee el sistema (`--focus-ring-*`, theming §32) — no acuñar token propio** — 1: `outline`.
- **⚠ decisión: `100%` es un valor identidad o geometría de layout, no un knob de tema — el perímetro de «knob» es D-TH.2, sin firmar** — 1: `inline-size`.
### 4.3 Avisos sobre los tokens propuestos (5)
### 4.2 Sin nombre mecánico (1)
- **el privado `--_gradient-picker-trigger-height` debe pasar a leer este público (o desaparecer)** — `--gradient-picker-trigger-height-md`, `--gradient-picker-trigger-height-sm`, `--gradient-picker-trigger-height-lg`
- **el privado `--_gradient-picker-trigger-padding-inline` debe pasar a leer este público (o desaparecer)** — `--gradient-picker-trigger-padding-inline-md`, `--gradient-picker-trigger-padding-inline-sm`, `--gradient-picker-trigger-padding-inline-lg`
- **el privado `--_gradient-picker-trigger-font-size` debe pasar a leer este público (o desaparecer)** — `--gradient-picker-trigger-font-size-md`, `--gradient-picker-trigger-font-size-sm`, `--gradient-picker-trigger-font-size-lg`
- **⚠ decisión: el valor es una expresión — el token puede llevar la expresión entera o sólo su término variable · el privado `--_gradient-picker-chip-size` debe pasar a leer este público (o desaparecer)** — `--gradient-picker-width-md`, `--gradient-picker-width-sm`, `--gradient-picker-width-lg`, `--gradient-picker-height-md`, `--gradient-picker-height-sm`, `--gradient-picker-height-lg`, `--gradient-picker-value-swatch-width-md`, `--gradient-picker-value-swatch-width-sm`, `--gradient-picker-value-swatch-width-lg`, `--gradient-picker-value-swatch-height-md`, `--gradient-picker-value-swatch-height-sm`, `--gradient-picker-value-swatch-height-lg`
- **⚠ decisión: el valor es una expresión — el token puede llevar la expresión entera o sólo su término variable** — `--gradient-picker-preset-height`
- **⚠ decisión: `100%` es un valor identidad o geometría de layout, no un knob de tema — el perímetro de «knob» es D-TH.2, sin firmar** — 1: `inline-size`.
### 4.4 Lo que hay que comprobar a mano (PLAN-theming §1.3 · §7.4)
resets externos bajan por `$effect` con guard — sin bucle.
- **Sin prop `color`** (como el builder).
## Talla y tema
Contrato en `lib/recipes/base.ts > gradient-picker` (25 claves). Alcance del
censo: **88 %** (era 0 % antes del 2026-08-21), sin privados.
### El trigger NO se tematiza desde aquí, y no es un olvido
Medido con centinela sobre cada knob: 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 pesa más
que cualquier cosa que este fichero diga sobre `[data-gradient-picker-trigger]`
(0,2,0 contra 0,1,0; 0,3,0 contra 0,2,0 en los estados).
Esta receta re-declaraba todo eso y **nada de ello 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`). Era código muerto, así que se retiró en vez de tokenizarlo —
un token sobre una declaración muerta es un token que miente, y retirarlo dio
**0 diffs** en 609 valores, que es la prueba de que estaba muerto.
**Un tema viste este trigger por el contrato de POPOVER** (`--popover-trigger-*`).
Es composición funcionando: el trigger ES un popover trigger. Lo que sí es
propio del picker —el `gap` de la fila y el chip del degradado— vive aquí y
alcanza (verificado a mano: el chip usa un hook de CLASE y el centinela no lo
ve).
### Lo demás
Las tres anchuras del panel (`content-width-{sm,md,lg}`) se quedan como tokens
planos leídos por tres reglas, no como cascada del TSC: el content viaja 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.
**El relleno del chip no es un knob**: soma estampa el degradado del usuario
inline (`--gp-current-gradient`, un canal de valor), y un tema no tiene nada
que decir sobre lo que el usuario eligió. Que ese nombre esté abreviado
—contra theming §6 r5— es deuda de SOMA, no de aquí; anotada en
`next-features.md` §13.
## Gaps
| Gap | Disposición | Detalle |
| --- | --- | --- |
| Persistencia de presets guardados | **descartar** (nivel componente) | Como ColorPicker: los presets/save son del CONSUMIDOR; el panel de presets ya compone. |
| Migración a `PickerProvider<TValue>` (deferValue genérico) | **diferir** | Task #31 de la memoria — pasada conjunta de los pickers POST audit-fix. |
| Demo v2 (D-1.5/D-3.1 de la máquina) | **implementar** | Pase de demos de esta iniciativa (ver sesión S9). |
| Persistencia de presets guardados | **descartar** (nivel componente) | Como ColorPicker: los presets/save son del CONSUMIDOR; el panel de presets ya compone. |
| Migración a `PickerProvider<TValue>` (deferValue genérico) | **diferir**| Task #31 de la memoria — pasada conjunta de los pickers POST audit-fix. |
| Demo v2 (D-1.5/D-3.1 de la máquina) | **implementar**| Pase de demos de esta iniciativa (ver sesión S9). |
<tr><tdclass="name">open</td><tdclass="type">boolean</td><tdclass="default">false</td><td>Popover open state. Bindable.</td></tr>
<tr><tdclass="name">mode</td><tdclass="type">'inline' | 'modal'</td><tdclass="default">'inline'</td><td>Footer layout (Clear/Cancel vs Cancel/Save).</td></tr>
<tr><tdclass="name">deferValue</td><tdclass="type">boolean</td><tdclass="default">false</td><td>Buffer edits in a draft until Listo; other closes discard.</td></tr>
<tr><tdclass="name">onValueChange</td><tdclass="type">(g: Gradient) => void</td><tdclass="default empty">—</td><td>Fires on every edit (live; deferValue off).</td></tr>
<tr><tdclass="name">presets <spandata-uix-tagdata-kind="eidos">eidos</span></td><tdclass="type">Gradient[]</td><tdclass="default empty">—</td><td>Defined → the popover splits into Editor + Predefinidos tabs.</td></tr>
<tr><tdclass="name">maxPresets</td><tdclass="type">number</td><tdclass="default">10</td><td>Cap on user-saved presets — "Añadir degradado" disables at the cap.</td></tr>
<pdata-uix-section-desc>Source: <code>src/uix/morfo/components/gradient-picker.ts</code> · <code>expression: 'delegated'</code> (the Popover is the dialog).</p>