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

157 lines
12 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.

# toggle — 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**: **100%** — 24 de 24 knobs por token público
- **Knobs de apariencia**: 25 — público 24 · privado 0 · global 0 · literal 0 · sistema 1 · excepción 3 _(los dos últimos, fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 126 pública(s) — `height-xs`, `height-sm`, `height-md`, `height-lg`, `height-xl`, `padding-inline-xs`, `padding-inline-sm`, `padding-inline-md`, `padding-inline-lg`, `padding-inline-xl`, `gap-xs`, `gap-sm`, `gap-md`, `gap-lg`, `gap-xl`, `font-family`, `line-height`, `letter-spacing`, `font-size-xs`, `font-size-sm`, `font-size-md`, `font-size-lg`, `font-size-xl`, `font-weight-xs`, `font-weight-sm`, `font-weight-md`, `font-weight-lg`, `font-weight-xl`, `radius`, `radius-sm`, `radius-md`, `radius-lg`, `radius-xl`, `radius-full`, `border-width`, `shadow`, `transition-duration`, `transition-ease`, `primary-track`, `primary-element`, `primary-hover`, `primary-border`, `primary-solid`, `primary-solid-hover`, `primary-text`, `primary-contrast`, `neutral-track`, `neutral-element`, `neutral-hover`, `neutral-border`, `neutral-solid`, `neutral-solid-hover`, `neutral-text`, `neutral-contrast`, `risk-track`, `risk-element`, `risk-hover`, `risk-border`, `risk-solid`, `risk-solid-hover`, `risk-text`, `risk-contrast`, `threat-track`, `threat-element`, `threat-hover`, `threat-border`, `threat-solid`, `threat-solid-hover`, `threat-text`, `threat-contrast`, `secondary-track`, `secondary-element`, `secondary-hover`, `secondary-border`, `secondary-solid`, `secondary-solid-hover`, `secondary-text`, `secondary-contrast`, `affirm-track`, `affirm-element`, `affirm-hover`, `affirm-border`, `affirm-solid`, `affirm-solid-hover`, `affirm-text`, `affirm-contrast`, `palette-track`, `palette-element`, `palette-hover`, `palette-border`, `palette-solid`, `palette-solid-hover`, `palette-text`, `palette-contrast`, `solid-bg`, `solid-fg`, `solid-border`, `solid-hover-bg`, `solid-hover-border`, `solid-on-bg`, `solid-on-fg`, `solid-on-border`, `solid-on-hover-bg`, `solid-on-hover-border`, `outline-bg`, `outline-fg`, `outline-border`, `outline-hover-bg`, `outline-hover-border`, `outline-on-bg`, `outline-on-fg`, `outline-on-border`, `outline-on-hover-bg`, `outline-on-hover-border`, `ghost-bg`, `ghost-fg`, `ghost-border`, `ghost-hover-bg`, `ghost-hover-border`, `ghost-on-bg`, `ghost-on-fg`, `ghost-on-border`, `ghost-on-hover-bg`, `ghost-on-hover-border`, `disabled-opacity`, `invalid-border`
- **Eje `size`**: sí · **ficheros**: `toggle.css`
## 1. Knobs fuera de alcance
### 1.1 Directo a primitivo global (0)
_Ninguno._
### 1.2 A través de un privado (0)
_Ninguno._
### 1.3 Literales (0)
_Ninguno._
### 1.4 Excepciones firmadas (3) — 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 | `toggle.css:162` | `[data-toggle][data-block]` | `inline-size` | `100%` |
| 2 | `toggle.css:175` | `[data-toggle][data-icon-only] [data-toggle-body]` | `width` | `1px` |
| 3 | `toggle.css:176` | `[data-toggle][data-icon-only] [data-toggle-body]` | `height` | `1px` |
## 2. Sistema transversal (1) — 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 | `toggle.css:157` | `[data-toggle]: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? |
| --- | ---: | --- | --- | :-: |
| `--_toggle-height` | 5 | `var(--toggle-height-md)`, `var(--toggle-height-xs)`, `var(--toggle-height-sm)`, `var(--toggle-height-lg)`, `var(--toggle-height-xl)` | public | **sí** |
| `--_toggle-padding-inline` | 5 | `var(--toggle-padding-inline-md)`, `var(--toggle-padding-inline-xs)`, `var(--toggle-padding-inline-sm)`, `var(--toggle-padding-inline-lg)`, `var(--toggle-padding-inline-xl)` | public | **sí** |
| `--_toggle-gap` | 5 | `var(--toggle-gap-md)`, `var(--toggle-gap-xs)`, `var(--toggle-gap-sm)`, `var(--toggle-gap-lg)`, `var(--toggle-gap-xl)` | public | **sí** |
| `--_toggle-font-size` | 5 | `var(--toggle-font-size-md)`, `var(--toggle-font-size-xs)`, `var(--toggle-font-size-sm)`, `var(--toggle-font-size-lg)`, `var(--toggle-font-size-xl)` | public | **sí** |
| `--_toggle-font-weight` | 5 | `var(--toggle-font-weight-md)`, `var(--toggle-font-weight-xs)`, `var(--toggle-font-weight-sm)`, `var(--toggle-font-weight-lg)`, `var(--toggle-font-weight-xl)` | public | **sí** |
| `--_toggle-radius` | 6 | `var(--toggle-radius)`, `var(--toggle-radius-sm)`, `var(--toggle-radius-md)`, `var(--toggle-radius-lg)`, `var(--toggle-radius-xl)`, `var(--toggle-radius-full)` | public | **sí** |
| `--_toggle-bg` | 3 | `var(--toggle-solid-bg)`, `var(--toggle-outline-bg)`, `var(--toggle-ghost-bg)` | public | **sí** |
| `--_toggle-fg` | 3 | `var(--toggle-solid-fg)`, `var(--toggle-outline-fg)`, `var(--toggle-ghost-fg)` | public | **sí** |
| `--_toggle-border` | 3 | `var(--toggle-solid-border)`, `var(--toggle-outline-border)`, `var(--toggle-ghost-border)` | public | **sí** |
| `--_toggle-hover-bg` | 3 | `var(--toggle-solid-hover-bg)`, `var(--toggle-outline-hover-bg)`, `var(--toggle-ghost-hover-bg)` | public | **sí** |
| `--_toggle-hover-border` | 3 | `var(--toggle-solid-hover-border)`, `var(--toggle-outline-hover-border)`, `var(--toggle-ghost-hover-border)` | public | **sí** |
| `--_toggle-on-bg` | 3 | `var(--toggle-solid-on-bg)`, `var(--toggle-outline-on-bg)`, `var(--toggle-ghost-on-bg)` | public | **sí** |
| `--_toggle-on-fg` | 3 | `var(--toggle-solid-on-fg)`, `var(--toggle-outline-on-fg)`, `var(--toggle-ghost-on-fg)` | public | **sí** |
| `--_toggle-on-border` | 3 | `var(--toggle-solid-on-border)`, `var(--toggle-outline-on-border)`, `var(--toggle-ghost-on-border)` | public | **sí** |
| `--_toggle-on-hover-bg` | 3 | `var(--toggle-solid-on-hover-bg)`, `var(--toggle-outline-on-hover-bg)`, `var(--toggle-ghost-on-hover-bg)` | public | **sí** |
| `--_toggle-on-hover-border` | 3 | `var(--toggle-solid-on-hover-border)`, `var(--toggle-outline-on-hover-border)`, `var(--toggle-ghost-on-hover-border)` | public | **sí** |
## 4. Propuesta de corrección
- **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` (0)
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 (`--toggle-…`) | scope TSC | valor propuesto | usos |
| --- | --- | --- | ---: |
### 4.2 Sin nombre mecánico (3)
- **⚠ 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`.
- **⚠ decisión: `width` es shorthand o eje físico — hay que partirlo en ejes lógicos antes de nombrarlo (recipe-contract §1, R-4.4)** — 1: `width`.
- **⚠ decisión: `height` es shorthand o eje físico — hay que partirlo en ejes lógicos antes de nombrarlo (recipe-contract §1, R-4.4)** — 1: `height`.
### 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 -->
**EJECUTADO 2026-08-23 — 81 % → 100 %, contrato 119 → 126 claves.**
Tres knobs fuera de alcance y una firma. Ninguno era doctrina: los tres eran
sistema leído a pelo o literales sin anotar.
- **El radio (§1.2, 1 knob · 6 fuentes)**. El bloque de `base.ts` decía «No
`--toggle-radius-*` aliases» y la receta leía `--radius-default` y la escala
global `--radius-*` directamente. Es exactamente el agujero que
`navigation-menu` tenía —«pinned to `--radius-default` ("decoupled from
size", a line that read Radix as a rule when Radix paints nothing) … not
reachable by a theme short of moving `--radius-default` for the whole
system»— y el catálogo ya lo había resuelto en los CUATRO componentes con
escala `rounded`: `button`, `badge`, `image` y `color-swatch` declaran sus
`radius-{paso}` y el reposo lee UNO de ellos. Toggle es el único que no.
Entran `radius` (el reposo, default `var(--radius-default)`, que así SIGUE
siguiendo el default del arquetipo) y `radius-{sm,md,lg,xl,full}`. El
desacople de `size` se conserva intacto: son pasos de `rounded`.
- **El tinte inválido (§1.1, 1 knob)**: `var(--color-threat-element)` a pelo →
`--toggle-invalid-border` con ese valor. La costura de PLAN §2-A.
- **Los tres literales (§1.3)** se FIRMAN con `/* literal: */` y salen del
ratio: el `100%` de `data-block` es identidad, y el par `1px` del cuerpo
`sr-only` ES la técnica (precedente `text-blur` / `text-scramble`).
**Diff de computed: VACÍO.** 416 valores en 7 estados (sonda estándar) más
1.150 valores en 25 celdas de variante / `rounded` / `on` / `invalid` /
`disabled` / `block` / `icon-only` / tono (barrido dirigido: el escenario monta
UN toggle `solid`, apagado y válido). Capturas 2× antes y después, idénticas.
**Centinela R-5.4: 70/126, 56 adjudicadas.** Empezó en **36/126** y la
diferencia NO es adjudicación, es instrumento: el guard sólo sabía barrer UN
eje, y aquí las claves `{variante}-*` y `{variante}-on-*` son las mismas tres
variantes en dos ESTADOS distintos. Forzar el estado con `prepareWith` cambiaba
una mitad por la otra (36 → 46, y las de reposo murieron) — «montar más puede
medir menos», esta vez dentro de un componente. El guard aprende a barrer el
PRODUCTO CARTESIANO de varios ejes (`variant` × `state` × `invalid`), y a
quitar un atributo con `null`, porque sellar `data-invalid` toda la corrida
hacía que el borde inválido ganase a los otros seis bordes. Con eso, 58/126. Y
el barrido llegó también al PASE DE HOVER, que no barría nada: 70/126, con las
doce claves de hover vivas (es literalmente la razón que
`radio-group.hover-segmented-segment-fg` tenía escrita en el ledger).
De las 56 restantes, 48 son la cascada de paleta por PATRÓN — medida aquí, con
un matiz que conviene escribir: **las ocho `neutral-*` caen con las demás
porque el propio guard estampa `data-color=neutral` para probarlas**, que es
justo lo que enciende el bloque genérico. Las otras 8 están medidas una a una
(los cinco pasos de `rounded`, que la demo no expone; `disabled-opacity`; y el
par de transición, que ES lo que el guard congela).
**Anotado, fuera por D-TH.5**: `[data-toggle]:hover` y
`[data-toggle][data-invalid]` pesan lo mismo (0,2,0) y el hover se declara
antes, así que **un toggle inválido bajo el puntero pierde su tinte**.
Arreglarlo mueve píxel y es una decisión de escala del sistema — los cuatro
controles de formulario (checkbox, switch, pin-input, toggle) comparten el
patrón. No se toca aquí.
<!-- veredicto:end -->

Powered by TurnKey Linux.