You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/docs/audit/theming/gradient-builder.md

197 lines
13 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# gradient-builder — alcance de tema: análisis y propuesta
> Generado por `node --import tsx/esm scripts/theming-census.ts --report`.
> Lo **medido** y la **propuesta** se regeneran; el **Veredicto** (§5) se conserva.
> Vista de conjunto: [README](./README.md) · método y protocolo:
> [`PLAN-theming.md`](../../process/PLAN-theming.md) §1, §2, §7.
- **Medido**: 2026-08-23 · **Alcance**: **93%** — 52 de 56 knobs por token público
- **Knobs de apariencia**: 60 — público 52 · privado 0 · global 1 · literal 3 · sistema 4 · excepción 0 _(los dos últimos, fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 72 pública(s) — `preview-height-sm`, `preview-height-md`, `preview-height-lg`, `track-height-sm`, `track-height-md`, `track-height-lg`, `stop-size-sm`, `stop-size-md`, `stop-size-lg`, `gap-sm`, `gap-md`, `gap-lg`, `padding-block-sm`, `padding-block-md`, `padding-block-lg`, `padding-inline-sm`, `padding-inline-md`, `padding-inline-lg`, `preview-height`, `track-height`, `stop-size`, `gap`, `padding-block`, `padding-inline`, `radius`, `bg`, `border`, `border-width`, `preview-radius`, `preview-bg`, `preview-border`, `track-radius`, `track-border`, `checker-fg`, `checker-cell`, `stop-radius`, `stop-bg`, `stop-ring`, `stop-ring-width`, `stop-shadow`, `selected-stop-shadow`, `stop-actions-gap`, `angle-dial-gap`, `angle-dial-head-gap`, `label-font-size`, `label-fg`, `value-font-size`, `value-fg`, `stop-color-title-font-size`, `stop-color-title-font-weight`, `stop-color-title-fg`, `stop-color-sliders-gap`, `stop-list-gap`, `stop-list-row-gap`, `stop-list-value-gap`, `stop-list-value-padding-block`, `stop-list-value-padding-inline`, `stop-list-value-radius`, `stop-list-value-bg`, `stop-list-value-fg`, `stop-list-value-border`, `stop-list-value-font-size`, `selected-stop-list-value-border`, `stop-list-pos-fg`, `presets-gap`, `preset-grid-gap`, `preset-height`, `preset-radius`, `preset-border`, `hover-preset-border`, `selected-preset-border`, `selected-preset-outline`
- **Eje `size`**: no · **ficheros**: `gradient-builder.css`
## 1. Knobs fuera de alcance
### 1.1 Directo a primitivo global (1)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `gradient-builder.css:129` | `[data-gradient-builder-stop]:focus-visible` | `box-shadow` | `0 0 0 calc(2px * var(--scaling, 1)) var(--color-surface-default), 0 0 0 calc(4px * var(--scaling, 1)) var(--color-primary-solid)` |
### 1.2 A través de un privado (0)
_Ninguno._
### 1.3 Literales (3)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `gradient-builder.css:37` | `[data-gradient-builder]` | `inline-size` | `100%` |
| 2 | `gradient-builder.css:188` | `[data-gradient-builder-stop-color-sliders]` | `inline-size` | `90%` |
| 3 | `gradient-builder.css:260` | `[data-gradient-builder-preset]` | `inline-size` | `100%` |
### 1.4 Excepciones firmadas (0) — fuera del ratio
Literales que llevan su anotación `/* literal: <razón> */` en la propia
declaración: la válvula de recipe-contract §3, la misma que honra
`component-audit`. **Una desviación firmada no es deuda** — se listan para que la
razón se lea, no para acuñarlas.
_Ninguno._
## 2. Sistema transversal (4) — informativo, fuera del ratio
Un tema los alcanza **a nivel de sistema**, por diseño (recipe-contract §2).
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `gradient-builder.css:60` | `[data-gradient-builder][data-disabled]` | `opacity` | `var(--opacity-disabled)` |
| 2 | `gradient-builder.css:230` | `[data-gradient-builder-stop-list-value]:hover:not(:disabled)` | `background-image` | `linear-gradient(var(--state-hover), var(--state-hover))` |
| 3 | `gradient-builder.css:236` | `[data-gradient-builder-stop-list-value]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
| 4 | `gradient-builder.css:271` | `[data-gradient-builder-preset]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
## 3. Privados de la receta — ¿de dónde sale su valor?
| privado | declaraciones | valor(es) | origen | ¿deriva de un público? |
| --- | ---: | --- | --- | :-: |
| `--_gradient-builder-preview-height` | 1 | `var(--gradient-builder-preview-height)` | public | **sí** |
| `--_gradient-builder-track-height` | 1 | `var(--gradient-builder-track-height)` | public | **sí** |
| `--_gradient-builder-stop-size` | 1 | `var(--gradient-builder-stop-size)` | public | **sí** |
| `--_gradient-builder-radius` | 1 | `var(--gradient-builder-preview-radius)` | public | **sí** |
| `--_gradient-builder-border` | 1 | `var(--gradient-builder-border)` | public | **sí** |
| `--_gradient-builder-stop-ring` | 1 | `var(--gradient-builder-stop-ring)` | public | **sí** |
| `--_gradient-builder-label` | 1 | `var(--gradient-builder-label-fg)` | public | **sí** |
| `--_gradient-builder-checker-cell` | 1 | `var(--gradient-builder-checker-cell)` | public | **sí** |
| `--_gradient-builder-checker` | 1 | `linear-gradient(45deg, var(--gradient-builder-checker-fg) 25%, transparent 25%), linear-gradient(-45deg, var(--gradient-builder-checker-fg) 25%, transparent 25%), linear-gradient(45deg, transparent 75%, var(--gradient-builder-checker-fg) 75%), linear-gradient(-45deg, transparent 75%, var(--gradient-builder-checker-fg) 75%)` | public | **sí** |
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).
### 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
al final) y theming §6.7 (slots de color, modificador delante). Un token con
DOS valores distintos es una colisión de nombre: son dos knobs, o el nombre
no distingue lo que debería — se marca `⚠`.
| token (`--gradient-builder-…`) | scope TSC | valor propuesto | usos |
| --- | --- | --- | ---: |
| `stop-shadow` _(ya existe)_ | `root` | `0 0 0 calc(2px * var(--scaling, 1)) var(--color-surface-default), 0 0 0 calc(4px * var(--scaling, 1)) var(--color-primary-solid)` | 1 |
| `stop-color-sliders-width` | `root` | `90%` | 1 |
### 4.2 Sin nombre mecánico (2)
- **⚠ 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`.
### 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** — `--gradient-builder-stop-shadow`
### 4.4 Lo que hay que comprobar a mano (PLAN-theming §1.3 · §7.4)
- [ ] **Privado que no deriva de un público** — §3 lo marca; el privado debe leer el público o desaparecer.
- [ ] **Velo o acento en el nodo equivocado** (`archetype: 'item'` en un envoltorio, un `background` en shorthand que mata la capa de estado) — se mide desde el píxel hacia arriba.
- [ ] **Doble animación** al mover un sello a una superficie con animación propia — registro de `animationstart`/`animationend`.
- [ ] **Diff de computed = 0** en reposo · hover · abierto · disabled · foco, por talla, antes y después.
- [ ] **Centinela por token nuevo**: valor imposible en el root → el nodo lo sigue. Si no, el token miente.
## 5. Veredicto
<!-- veredicto:start -->
**Revisión 2026-08-20 — verificación previa a implementación (Opus).**
**Análisis (§1–§3): CORRECTO** (62 · 0 · 8 · 46 · 3 · 5, reproducido). Partes
limpias y bien estampadas; `data-size` vive en el ROOT ✓ (sin `parts`).
Hallazgo de prefijo, tercero de la familia picker: la receta usa `--gb-*`
(`--gb-checker`, `--gb-stop-fill`) — abreviatura que viola theming §6 regla 5
y esconde privados al censo. **Renombrar `--gb-*` →
`--_gradient-builder-*`** (el checker es privado; `--gb-stop-fill` lo escribe
el wrapper inline por parada — mismo tratamiento que `match-anchor-width`,
privado bien prefijado, runtime).
**Propuesta (§4.1): APTA — la mejor del lote — con tres correcciones:**
1. **Forma por talla**: como en todos — coordenadas `{part}-{eje}-{k}` en
`root` (la tabla ya las trae con valores verbatim CORRECTOS, incluidos los
`calc(Npx * var(--scaling,1))` que conservan la participación en el zoom)
**+ nombre resuelto** con `declarations` default `host` (md) y
`size:sm/lg` — la receta consume sólo el resuelto (patrón Sidebar).
`gap-sm`/`gap-lg` son coordenadas de ese mismo eje (`gap`), no tokens
sueltos.
2. **`stop-shadow` ⚠**: los dos valores son reposo vs SELECCIONADA — la
segunda ya existe como `selected-stop-shadow`; resolver por selector y
dejar `stop-shadow` (reposo) + `selected-stop-shadow`, sin colisión. Ojo:
ambos valores llevan `color-mix(… var(--color-neutral-contrast) …)` — es
sombra, no hover, así que no toca R-4.3; se acuñan tal cual.
3. **`preview-bg-image` / `stop-bg`**: sus valores referencian los `--gb-*`
de arriba — acuñar DESPUÉS del renombrado, leyendo el privado bien
prefijado (`var(--_gradient-builder-checker)`), o promoviendo el checker a
público si el autor quiere el damero temable (decisión menor, propongo
privado).
Resto de la tabla (angle-dial, presets, kind-switch, stop-list, título):
**válida tal cual** — nombres en vocabulario, `root`, verbatim. Los 3
literales: anotar o tokenizar. Nota de talla: las alturas de preview/track y
los tamaños de stop son geometría PROPIA (px·scaling), no del bundle — ni se
fuerzan al bundle ni se marca desviación: el bundle no tiene esas coordenadas.
**Bloqueos de firma**: ninguno duro — este componente puede ser el PILOTO del
bloque picker (D-TH.4): todo su contrato sale con valores verbatim y cero
decisiones estructurales pendientes (sólo el damero, menor).
---
**EJECUTADO 2026-08-20 (piloto del eje).** Alcance **0 % → 80 %** · contrato
**0 → 72 claves públicas**. El 80 % es el suelo del regex: los 7 knobs que
quedan como `private` pasan por privados que **ahora derivan todos de un
público** (§3 lo confirma), el único `global` es el anillo de foco (sistema) y
los 3 literales son geometría de layout (`100%`, `90%`).
Qué se hizo, exactamente lo propuesto arriba:
1. Bloque `'gradient-builder'` en `recipes/base.ts` con el canon de dos piezas —
coordenadas por talla (`*-{sm,md,lg}`) + nombre resuelto (`host` = md,
`size:sm` / `size:lg`); la receta consume SÓLO el resuelto y los bloques
`[data-size]` del CSS desaparecen (los emite el TSC).
2. Renombrados los prefijos abreviados: `--gb-checker*` → `--_gradient-builder-checker*`,
`--gb-stop-fill` → `--_gradient-builder-stop-fill` (también en el wrapper,
`gradient-builder-stop.svelte`). El damero se parametriza por
`checker-fg` + `checker-cell` (nombre post-codemod D-TH.6); el patrón de cuatro gradientes queda
privado.
3. `padding` shorthand partido en `padding-block` / `padding-inline` (ejes
lógicos, recipe-contract §1).
4. Los 4 `*-font-size` consumen la **coordenada del bundle**
(`--size-sm-font-size`), no el primitivo — lo exigió el guard
`recipe-css-contract` («recipes consume the size bundle»), y es
value-preserving.
Verificación (artefactos):
- **Diff de computed = 0**: sonda Playwright antes/después, **6.612 valores**
comparados en 7 estados (reposo · 5 tallas · hover) sobre 34 nodos. El único
diff que apareció era la transición de hover de un `Button` compuesto
capturada a mitad — demostrado bajando la espera de asentamiento en el árbol
YA modificado y reproduciendo el valor interpolado (`oklab(… / 0.646795)`).
- **Centinela: 72/72 tokens alcanzan.** 49 desde el ámbito del componente en
reposo; 12 coordenadas de talla + 2 de preset seleccionado verificadas
forzando su estado; `hover-preset-border` con hover real; los 4 del editor de
color **sólo desde `:root`** (el panel viaja por portal — anotado en el README).
- **`preview-bg` + `checker-fg`/`-cell` no mueven píxel hoy**: el wrapper
pinta el degradado con el shorthand `background` inline, que resetea
`background-image` y tapa el damero. El token es real; el damero no se ve.
Deuda anotada (corregirlo mueve píxel ⇒ decisión).
- 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 · captura 2×
revisada.
⚠ **La ficha dice «Eje `size`: no»** porque el detector busca `data-size` en el
CSS de la receta y ahora la cascada por talla vive en el generado. El componente
SÍ tiene eje `size` — límite del instrumento, no del componente.
<!-- veredicto:end -->

Powered by TurnKey Linux.