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

155 lines
11 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.

# switch — 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**: **81%** — 17 de 21 knobs por token público
- **Knobs de apariencia**: 22 — público 17 · privado 4 · global 0 · literal 0 · sistema 1 · excepción 0 _(los dos últimos, fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 52 pública(s) — `size-xs-track-width`, `size-xs-track-height`, `size-xs-thumb-size`, `size-xs-thumb-translate`, `size-sm-track-width`, `size-sm-track-height`, `size-sm-thumb-size`, `size-sm-thumb-translate`, `size-md-track-width`, `size-md-track-height`, `size-md-thumb-size`, `size-md-thumb-translate`, `size-lg-track-width`, `size-lg-track-height`, `size-lg-thumb-size`, `size-lg-thumb-translate`, `size-xl-track-width`, `size-xl-track-height`, `size-xl-thumb-size`, `size-xl-thumb-translate`, `track-radius`, `track-border-width`, `thumb-inset`, `thumb-radius`, `thumb-border-width`, `thumb-shadow`, `transition-duration`, `transition-ease`, `track-bg-off`, `track-border-off`, `hover-track-border-off`, `invalid-track-border`, `disabled-track-bg`, `disabled-track-border`, `disabled-thumb-bg`, `disabled-thumb-border`, `disabled-opacity`, `thumb-bg`, `thumb-border`, `thumb-icon-fg`, `primary-solid`, `primary-solid-hover`, `neutral-solid`, `neutral-solid-hover`, `risk-solid`, `risk-solid-hover`, `threat-solid`, `threat-solid-hover`, `secondary-solid`, `secondary-solid-hover`, `affirm-solid`, `affirm-solid-hover` · 2 privada(s) forward — `_palette-solid`, `_palette-solid-hover`
- **Eje `size`**: sí · **ficheros**: `switch.css`
## 1. Knobs fuera de alcance
### 1.1 Directo a primitivo global (0)
_Ninguno._
### 1.2 A través de un privado (4)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `switch.css:26` | `[data-switch]` | `background-color` | `var(--_switch-track-bg)` |
| 2 | `switch.css:70` | `[data-switch]:hover:not([data-disabled]):not([data-readonly])` | `border-color` | `var(--_switch-track-border-hover)` |
| 3 | `switch.css:74` | `[data-switch][data-state='checked']:hover:not([data-disabled]):not([data-readonly])` | `background-color` | `var(--_switch-palette-solid-hover)` |
| 4 | `switch.css:144` | `[data-switch][data-state='checked'] [data-switch-thumb]` | `color` | `var(--_switch-palette-solid)` |
### 1.3 Literales (0)
_Ninguno._
### 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 (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 | `switch.css:99` | `[data-switch]: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? |
| --- | ---: | --- | --- | :-: |
| `--_switch-track-width` | 5 | `var(--switch-size-md-track-width)`, `var(--switch-size-xs-track-width)`, `var(--switch-size-sm-track-width)`, `var(--switch-size-lg-track-width)`, `var(--switch-size-xl-track-width)` | public | **sí** |
| `--_switch-track-height` | 5 | `var(--switch-size-md-track-height)`, `var(--switch-size-xs-track-height)`, `var(--switch-size-sm-track-height)`, `var(--switch-size-lg-track-height)`, `var(--switch-size-xl-track-height)` | public | **sí** |
| `--_switch-thumb-size` | 5 | `var(--switch-size-md-thumb-size)`, `var(--switch-size-xs-thumb-size)`, `var(--switch-size-sm-thumb-size)`, `var(--switch-size-lg-thumb-size)`, `var(--switch-size-xl-thumb-size)` | public | **sí** |
| `--_switch-thumb-translate` | 5 | `var(--switch-size-md-thumb-translate)`, `var(--switch-size-xs-thumb-translate)`, `var(--switch-size-sm-thumb-translate)`, `var(--switch-size-lg-thumb-translate)`, `var(--switch-size-xl-thumb-translate)` | public | **sí** |
| `--_switch-track-bg` | 2 | `var(--switch-track-bg-off)`, `var(--_switch-palette-solid)` | private, public | no |
| `--_switch-track-border` | 2 | `var(--switch-track-border-off)`, `var(--_switch-palette-solid)` | private, public | no |
| `--_switch-track-border-hover` | 2 | `var(--switch-hover-track-border-off)`, `var(--_switch-palette-solid-hover)` | private, public | no |
| `--_switch-thumb-offset` | 3 | `0px`, `var(--_switch-thumb-translate)`, `calc(-1 * var(--_switch-thumb-translate))` | literal, private | no |
Consumidos y **no declarados en el CSS** (vienen de `base.ts` o de un estilo inline del wrapper): `--_switch-palette-solid`, `--_switch-palette-solid-hover`.
## 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` (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 (`--switch-…`) | scope TSC | valor propuesto | usos |
| --- | --- | --- | ---: |
| `bg` | `root` | ⚠ `var(--switch-track-bg-off)` / `var(--_switch-palette-solid)` | 2 |
| `hover-border` | `root` | ⚠ `var(--switch-hover-track-border-off)` / `var(--_switch-palette-solid-hover)` | 2 |
### 4.2 Sin nombre mecánico (2)
- **⚠ 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** — 2: `background-color`, `color`.
### 4.3 Avisos sobre los tokens propuestos (2)
- **el privado `--_switch-track-bg` debe pasar a leer este público (o desaparecer)** — `--switch-bg`
- **el privado `--_switch-track-border-hover` debe pasar a leer este público (o desaparecer)** — `--switch-hover-border`
### 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 — 76 % → 81 %.** UNA clave nueva (51 → 52), veinticinco
adjudicaciones medidas (13 propias + 12 por patrón) y un hallazgo que no se
arregla aquí. Diff de computed **0 sobre 1.344 valores en 36 estados** (la sonda
estándar, 416 en 7, más una sonda de estados propia —checked, invalid,
readonly, disabled, RTL, los cinco pasos de talla, cada uno con hover y foco—,
928 en 29). Capturas 2× antes/después idénticas.
**Sólo había un knob fuera de alcance de verdad**: el borde inválido, que iba a
`--color-threat-element` a pelo. Sube como **`invalid-track-border`**, y el
nombre no es el `invalid-border` que el catálogo repite seis veces (checkbox,
listbox, grid-list, radio-cards…): esta receta tiene **DOS partes con borde** y
las nombra —su clave hermana de estado es `disabled-track-border`, y el pulgar
lleva `thumb-border`—, así que un nombre desnudo no diría cuál de las dos tiñe.
La forma modificador + parte + ranura ya existe en el catálogo
(`invalid-input-border`, `invalid-control-border`).
⚠⚠ **Las DOCE claves de tono no pintan, y aquí cae también `neutral`.** Es la
cascada de paleta ya medida en button / badge / callout —el bloque genérico
`[data-switch][data-color]` se emite el ÚLTIMO y gana por orden a igual
especificidad (0,2,0)—, pero con un agravante propio: **soma estampa
`data-color` SIEMPRE** (es el color resuelto, `neutral` por defecto), así que el
fallback que en otras recetas mantiene vivo el tono por defecto tampoco se
alcanza nunca. Medido en checked: `--switch-neutral-solid` no mueve nada,
`--palette-solid` sobre el nodo repinta (`oklch(0.5556 …)` → `rgb(4,5,6)`).
Adjudicadas **por patrón**; la incidencia de fondo (419 claves en 49 recetas) es
la de `next-features.md` §13 y no se arregla en el commit de un componente.
**Las otras trece adjudicaciones son estado y no deuda**: los cinco
`size-*-thumb-translate` pintan sobre `transform` —que el guard no lee— y valen
0px hasta que el interruptor está encendido (forzado: 12/14/16/18/20px →
1234px); el par de transición ES la transición que el guard congela (medido sin
congelar: 0,12s → 11,5s); y los cinco `disabled-*` sólo existen bajo
`[data-disabled]`, que ningún trigger alcanza. El propio `invalid-track-border`
entra en esa clase (forzado: `oklch(0.9555 0.0207 13.86)` → `rgb(1,2,3)`).
**Los cuatro privados de §1.2 se quedan, y por dos razones distintas.**
`--_switch-track-bg` y `--_switch-track-border-hover` son **CONMUTADORES**: dos
fuentes cada uno —la clave pública del estado apagado y el forward de paleta del
encendido—, y aplanarlos obligaría a duplicar cada regla por color (clase 1 de
F2-B). Las dos lecturas directas de `--_switch-palette-solid*` —el fondo del
track en hover, la tinta del check dentro del pulgar— son el **puente THM-2**,
que no se acuña: un público encima dejaría que un tema fijara el tono y matara
el `color=` de cada instancia. **El techo de este componente no es el 100 %.**
⚠ **Defecto real medido, NO arreglado** (mueve píxel): el tinte inválido es
`--color-threat-element`, el paso 3 de la escala —`oklch(0.9555 0.0207 13.86)`,
casi blanco—, así que el borde de un switch inválido es prácticamente invisible
sobre superficie clara; y en cuanto el puntero entra, la regla de hover (0,4,0)
lo tapa con el borde neutro fuerte. Es el mismo valor que usan `checkbox`,
`radio-group` y `toggle` para el mismo estado, así que es una decisión de escala
del sistema —`element` contra `border`—, no de esta receta: cambiarlo es un
rediseño de default en cuatro componentes, y D-TH.5 lo deja fuera del eje.
<!-- veredicto:end -->

Powered by TurnKey Linux.