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/knob.md

126 lines
7.4 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.

# knob — 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-24 · **Alcance**: **78%** — 18 de 23 knobs por token público
- **Knobs de apariencia**: 26 — público 18 · privado 1 · global 1 · literal 3 · sistema 3 · excepción 1 _(los dos últimos, fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 33 pública(s) — `gap-sm`, `gap-md`, `gap-lg`, `gap-xl`, `diameter-sm`, `diameter-md`, `diameter-lg`, `diameter-xl`, `label-font-size-sm`, `label-font-size-md`, `label-font-size-lg`, `label-font-size-xl`, `value-font-size-sm`, `value-font-size-md`, `value-font-size-lg`, `value-font-size-xl`, `gap`, `diameter`, `label-font-size`, `value-font-size`, `radius`, `shadow`, `dragging-shadow`, `track-bg`, `face-bg`, `arc-width`, `pointer-inset`, `pointer-width`, `pointer-bg`, `indicator-radius`, `value-fg`, `label-fg`, `label-line-height`
- **Eje `size`**: no · **ficheros**: `knob.css`
## 1. Knobs fuera de alcance
### 1.1 Directo a primitivo global (1)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `knob.css:127` | `[data-knob-value-field] [data-spin-field-input]:focus-visible` | `border-radius` | `var(--radius-sm)` |
### 1.2 A través de un privado (1)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `knob.css:114` | `[data-knob-value-field] [data-spin-field-input]` | `inline-size` | `calc(var(--_knob-value-digits, 3) * 1ch)` |
### 1.3 Literales (3)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `knob.css:71` | `[data-knob-value-text]` | `line-height` | `1` |
| 2 | `knob.css:120` | `[data-knob-value-field] [data-spin-field-input]` | `line-height` | `1` |
| 3 | `knob.css:159` | `[data-knob-control]:hover` | `filter` | `brightness(1.08)` |
### 1.4 Excepciones firmadas (1) — 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.
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `knob.css:149` | `[data-knob-indicator]::before` | `block-size` | `28%` |
## 2. Sistema transversal (3) — 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 | `knob.css:125` | `[data-knob-value-field] [data-spin-field-input]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
| 2 | `knob.css:163` | `[data-knob-control]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
| 3 | `knob.css:174` | `[data-knob][data-disabled] [data-knob-control]` | `opacity` | `var(--opacity-disabled)` |
## 3. Privados de la receta — ¿de dónde sale su valor?
_La receta no declara privados propios en su CSS._
Consumidos y **no declarados en el CSS** (vienen de `base.ts` o de un estilo inline del wrapper): `--_knob-value-digits`.
## 4. Propuesta de corrección
- **Consume la capa compartida `spin-field`.** Un eje que la capa posee se consume como `var(--_x, var(--x))`; el consumidor **no acuña** `--knob-{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` (3)
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 (`--knob-…`) | scope TSC | valor propuesto | usos |
| --- | --- | --- | ---: |
| `value-field-radius` | `root` | `var(--radius-sm)` | 1 |
| `indicator-height` | `root` | `28%` | 1 |
| `hover-control-filter` | `root` | `brightness(1.08)` | 1 |
### 4.2 Sin nombre mecánico (3)
- **⚠ decisión: `1` 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: `line-height`.
- **⚠ decisión: el privado que alimenta este knob no se declara en el CSS (viene de `base.ts` o de un estilo inline) — hay que resolverlo antes de nombrarlo** — 1: `inline-size`.
### 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 -->
**Medido 2026-08-22** (sonda ×2 = 0 diffs en 754 valores · 7 estados). El knob
ya tenía siete públicos «de facto» —consumidos con la forma
`var(--knob-x, <default>)`— pero **ninguno declarado en el contrato**, así que
un tema no los ve en `getRecipeTokens()` y el censo los cuenta bien por
casualidad. La tokenización consiste sobre todo en DECLARAR lo que ya se
consume, y en separar dos cosas que compartían nombre.
1. **Cuatro `--knob-*` NO son de tema: son canal de valor.** El provider los
escribe INLINE desde el estado del control
(`knob-provider.svelte.ts:358`): `--knob-progress`, `--knob-angle`,
`--knob-start-angle` y `--knob-sweep`. Ningún contrato puede ganarles y
ninguno debe intentarlo — es la clase de `carousel.item-gap` y de la `z` del
preview de `drag-drop`.
2. **⚠ `--knob-arc-width` significa DOS cosas con DOS defaults distintos**: el
inset de la cara del dial (`--space-3`, líneas 80 y 113) y la separación
superior del puntero (`--space-2`, línea 165). Un tema que lo escriba mueve
las tres a la vez, pero sin tema cada una vale algo distinto — «un nombre
que no distingue lo que debería», que es la definición de colisión. Se
separan: `arc-width` (la cara) y `pointer-inset` (el puntero), cada uno con
su valor verbatim. **Cambia la superficie, no el píxel.**
3. **Las cuatro coordenadas por talla suben al TSC.** Hoy el default por talla
vive en un privado y el público es un OVERRIDE que va DELANTE
(`var(--knob-gap, var(--_knob-gap))`), así que declarar `--knob-gap` en el
contrato **mataría la escala**: ganaría siempre. La forma correcta es la del
resto del eje — coordenadas `{eje}-{k}` + nombre resuelto por `data-size`, y
la receta lee el resuelto a secas. El eje es **sm|md|lg|xl**, sin xs.
4. El diámetro es `control-height × 2` por talla: una PROPORCIÓN del bundle,
que se conserva verbatim en cada coordenada.
<!-- veredicto:end -->

Powered by TurnKey Linux.