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>
Consumidos y **no declarados en el CSS** (vienen de `base.ts` o de un estilo inline del wrapper): `--_gradient-builder-stop-fill`.
## 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-builder-{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` (51)
### 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
@ -123,72 +79,22 @@ no distingue lo que debería — se marca `⚠`.
- **⚠ 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** — 2: `inline-size`.
- **⚠ decisión: `padding` es shorthand o eje físico — hay que partirlo en ejes lógicos antes de nombrarlo (recipe-contract §1, R-4.4)** — 4: `padding`.
- **⚠ 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)** — 6: `border`.
- **ya alcanzable: el privado `--_gradient-builder-preview-height` deriva de un público (`var(--gradient-builder-preview-height)`) — sin acción; el censo lo cuenta como no alcanzable por el límite del regex** — 1: `block-size`.
- **ya alcanzable: el privado `--_gradient-builder-radius` deriva de un público (`var(--gradient-builder-preview-radius)`) — sin acción; el censo lo cuenta como no alcanzable por el límite del regex** — 1: `border-radius`.
- **ya alcanzable: el privado `--_gradient-builder-checker` deriva de un público (`linear-gradient(45deg, var(--gradient-builder-checker-color) 25%, transparent 25%), linear-gradient(-45deg, var(--gradient-builder-checker-color) 25%, transparent 25%), linear-gradient(45deg, transparent 75%, var(--gradient-builder-checker-color) 75%), linear-gradient(-45deg, transparent 75%, var(--gradient-builder-checker-color) 75%)`) — sin acción; el censo lo cuenta como no alcanzable por el límite del regex** — 1: `background-image`.
- **ya alcanzable: el privado `--_gradient-builder-track-height` deriva de un público (`var(--gradient-builder-track-height)`) — sin acción; el censo lo cuenta como no alcanzable por el límite del regex** — 1: `block-size`.
- **ya alcanzable: el privado `--_gradient-builder-stop-size` deriva de un público (`var(--gradient-builder-stop-size)`) — sin acción; el censo lo cuenta como no alcanzable por el límite del regex** — 2: `inline-size`, `block-size`.
- **ya alcanzable: el privado `--_gradient-builder-label` deriva de un público (`var(--gradient-builder-label-fg)`) — sin acción; el censo lo cuenta como no alcanzable por el límite del regex** — 1: `color`.
### 4.3 Avisos sobre los tokens propuestos (6)
### 4.3 Avisos sobre los tokens propuestos (1)
- **⚠ 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-builder-preview-height` debe pasar a leer este público (o desaparecer)** — `--gradient-builder-preview-height-md`, `--gradient-builder-preview-height-sm`, `--gradient-builder-preview-height-lg`
- **el privado `--_gradient-builder-radius` debe pasar a leer este público (o desaparecer)** — `--gradient-builder-preview-radius`, `--gradient-builder-track-radius`
- **⚠ 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-builder-track-height` debe pasar a leer este público (o desaparecer)** — `--gradient-builder-track-height-md`, `--gradient-builder-track-height-sm`, `--gradient-builder-track-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 · el privado `--_gradient-builder-stop-size` debe pasar a leer este público (o desaparecer)** — `--gradient-builder-stop-width-md`, `--gradient-builder-stop-width-sm`, `--gradient-builder-stop-width-lg`, `--gradient-builder-stop-height-md`, `--gradient-builder-stop-height-sm`, `--gradient-builder-stop-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-builder-selected-stop-shadow`, `--gradient-builder-stop-shadow`, `--gradient-builder-preset-height`
- **el privado `--_gradient-builder-label` debe pasar a leer este público (o desaparecer)** — `--gradient-builder-angle-dial-label-fg`
- **⚠ decisión: el valor es una expresión — el token puede llevar la expresión entera o sólo su término variable** — `--gradient-builder-stop-shadow`
### 4.4 Lo que hay que comprobar a mano (PLAN-theming §1.3 · §7.4)
@ -243,4 +149,54 @@ fuerzan al bundle ni se marca desviación: el bundle no tiene esas coordenadas.
bloque picker (D-TH.4): todo su contrato sale con valores verbatim y cero
decisiones estructurales pendientes (sólo el damero, menor).
| Mesh editor (puntos 2D) | **diferir** (fast-follow declarado) | El eje ya publica `aurora()`; el editor de mesh reutilizará el patrón stop=slider. Diamond descartado en el research. |
| Midpoints (color hints) + `parseGradient` (paste-to-edit) | **diferir** (Phase-2.5 declarada) | Con el research ya hecho en CONTINUE.md. |
| Gizmo on-canvas (superficie dual barra + lienzo) | **diferir** | v1 = la barra precisa; el gizmo espera el caso mesh. |
| API pública de tokens (`--gradient-builder-*` en recipes/base.ts) | **diferir** | Los privados pasan las R-rules; registrar cuando haya demanda real de theming del editor. |
| Demo v2 (D-1.2/D-3.1 de la máquina) | **implementar** | Pase de demos de esta iniciativa (ver sesión S9). |
| Mesh editor (puntos 2D) | **diferir** (fast-follow declarado) | El eje ya publica `aurora()`; el editor de mesh reutilizará el patrón stop=slider. Diamond descartado en el research. |
| Midpoints (color hints) + `parseGradient` (paste-to-edit) | **diferir** (Phase-2.5 declarada) | Con el research ya hecho en CONTINUE.md. |
| Gizmo on-canvas (superficie dual barra + lienzo) | **diferir**| v1 = la barra precisa; el gizmo espera el caso mesh. |
| API pública de tokens (`--gradient-builder-*` en recipes/base.ts) | **diferir**| Los privados pasan las R-rules; registrar cuando haya demanda real de theming del editor. |
| Demo v2 (D-1.2/D-3.1 de la máquina) | **implementar**| Pase de demos de esta iniciativa (ver sesión S9). |