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

165 lines
9.7 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.

# listbox — 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-25 · **Alcance**: **92%** — 23 de 25 knobs por token público
- **Knobs de apariencia**: 31 — público 23 · privado 0 · global 1 · literal 1 · sistema 3 · excepción 6 · estructural 0 · puente 3 · canal 0 _(los cinco últimos, fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 21 pública(s) — `gap`, `padding`, `radius`, `border-width`, `border`, `bg`, `fg`, `font-family`, `line-height`, `max-block-size`, `invalid-border`, `item-radius`, `item-fg`, `disabled-item-fg`, `item-indicator-size`, `group-gap`, `group-label-padding-block`, `group-label-fg`, `group-label-font-size`, `group-label-font-weight`, `group-label-tracking` · 2 privada(s) forward — `_palette-solid`, `_palette-element`
- **Eje `size`**: no · **ficheros**: `listbox.css`
## 1. Knobs fuera de alcance
### 1.1 Directo a primitivo global (1)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `listbox.css:143` | `[data-listbox-item][data-highlighted]:not([data-disabled])` | `background` | `var(--color-surface-overlay)` |
### 1.2 A través de un privado (0)
Sólo el **residuo**: el puente de paleta (§2-ter) y el canal de valor
(§2-quater) salen aparte, porque no son deuda ni tienen nombre que acuñar.
_Ninguno._
### 1.3 Literales (1)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `listbox.css:77` | `[data-listbox][data-block]` | `inline-size` | `100%` |
### 1.4 Excepciones firmadas (6) — 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 | `listbox.css:61` | `[data-listbox]` | `font-size` | `var(--_listbox-item-font-size)` |
| 2 | `listbox.css:129` | `[data-listbox-item]` | `gap` | `var(--_listbox-item-gap)` |
| 3 | `listbox.css:130` | `[data-listbox-item]` | `min-block-size` | `var(--_listbox-item-height)` |
| 4 | `listbox.css:131` | `[data-listbox-item]` | `padding-block` | `var(--_listbox-item-padding-block)` |
| 5 | `listbox.css:132` | `[data-listbox-item]` | `padding-inline` | `var(--_listbox-item-padding-inline)` |
| 6 | `listbox.css:194` | `[data-listbox-group-label]` | `padding-inline` | `var(--_listbox-item-padding-inline)` |
## 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 | `listbox.css:111` | `[data-listbox][data-disabled]` | `opacity` | `var(--opacity-disabled)` |
| 2 | `listbox.css:121` | `[data-listbox]:focus-visible, [data-listbox][data-focused]` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
| 3 | `listbox.css:161` | `[data-listbox-item][data-disabled]` | `opacity` | `var(--opacity-disabled)` |
## 2-ter. Puente de paleta THM-2 (3) — fuera del ratio
La receta lee `var(--_listbox-palette-{slot})`, que **lo escribe el forward** de la
cascada de paleta bajo `[data-listbox]:where([data-color], [data-color-custom])`
(`renderRecipePaletteForward`, firma B′). Es el MECANISMO de la paleta por
instancia, y se alcanza **dos veces**: por la capa compartida `--palette-*` y
por los tonos públicos del propio componente. **No se acuña**: un público
encima dejaría que un tema lo fijara y matara en silencio el `color=` de cada
instancia (veredictos §5 de `card`, `tags-input`, `avatar`).
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `listbox.css:151` | `[data-listbox-item][data-selected]:not([data-disabled])` | `background` | `var(--_listbox-palette-element)` |
| 2 | `listbox.css:155` | `[data-listbox-item][data-selected][data-highlighted]:not([data-disabled])` | `background` | `var(--_listbox-palette-element)` |
| 3 | `listbox.css:176` | `[data-listbox-item-indicator]` | `color` | `var(--_listbox-palette-solid)` |
## 3. Privados de la receta — ¿de dónde sale su valor?
| privado | declaraciones | valor(es) | origen | ¿deriva de un público? |
| --- | ---: | --- | --- | :-: |
| `--_listbox-item-height` | 1 | `var(--list-item-height)` | global | no |
| `--_listbox-item-padding-inline` | 1 | `var(--list-item-padding-inline)` | global | no |
| `--_listbox-item-padding-block` | 1 | `var(--list-item-padding-block)` | global | no |
| `--_listbox-item-gap` | 1 | `var(--list-item-gap)` | global | no |
| `--_listbox-item-font-size` | 1 | `var(--list-font-size)` | global | no |
| `--_listbox-max-height` | 1 | `var(--listbox-max-block-size)` | public | **sí** |
Consumidos y **no declarados en el CSS** (vienen de `base.ts` o de un estilo inline del wrapper): `--_listbox-palette-element`, `--_listbox-palette-solid`.
## 4. Propuesta de corrección
- **Consume la capa compartida `list-surface`.** Un eje que la capa posee se consume como `var(--_x, var(--x))`; el consumidor **no acuña** `--listbox-{eje}` para él — sería un vocabulario paralelo (README de `eidos/components`, «Capas compartidas» regla 2).
- **Consume la capa compartida `menu-indicator`.** Un eje que la capa posee se consume como `var(--_x, var(--x))`; el consumidor **no acuña** `--listbox-{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` (7)
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 (`--listbox-…`) | scope TSC | valor propuesto | usos |
| --- | --- | --- | ---: |
| `font-size` | `root` | `var(--_listbox-item-font-size)` | 1 |
| `item-gap` | `root` | `var(--_listbox-item-gap)` | 1 |
| `item-height` | `root` | `var(--_listbox-item-height)` | 1 |
| `item-padding-block` | `root` | `var(--_listbox-item-padding-block)` | 1 |
| `item-padding-inline` | `root` | `var(--_listbox-item-padding-inline)` | 1 |
| `item-bg` | `root` | `var(--color-surface-overlay)` | 1 |
| `group-label-padding-inline` | `root` | `var(--_listbox-item-padding-inline)` | 1 |
### 4.2 Sin nombre mecánico (1)
- **⚠ 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`.
### 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 -->
**ADJUDICADO 2026-08-22. Su 68 % es el TECHO de este diseño**, y lo que queda
fuera no se arregla en esta ficha.
**Cinco de sus nueve privados ADOPTAN la capa `list-surface`** — el ritmo de
fila (alto, padding, gap, tamaño de fuente) que `lib/list-surface.css` resuelve
por talla sobre el gancho `data-list-surface`, compartido con select, combobox,
command y los tres menús. Dos más son el **puente de paleta THM-2**
(`--_listbox-palette-*`, misma razón que `tag-group`), y el noveno sí deriva de
un público (`--_listbox-max-height`).
**Coser la capa aquí NO es la respuesta, y está medido.** Sus dos hermanos
(`dropdown-menu`, `context-menu`) sí acuñan el puente
(`--dropdown-menu-item-height: var(--list-item-height)`), así que la tentación
era igualar. Medido en el menú abierto, con la fila a 36 px:
| lo que escribe un tema en `:root` | resultado |
| --- | --- |
| `--dropdown-menu-item-height: 1234px` | **36 px — sin efecto** |
| `--list-item-height: 1234px` | **36 px — sin efecto** |
Es cascada: la capa declara `--list-item-*` sobre `[data-list-surface][data-size]`
y el puente del hermano sobre `[data-{c}-content]`; las dos ganan a `:root`
siempre. (El centinela los da por vivos porque escribe INLINE en el nodo, que sí
gana — falso positivo del instrumento para esta forma.) Copiar el puente aquí
subiría la cifra sin dar alcance a nadie: sería un token que miente, la clase
que R-5.4 existe para cazar.
**Dónde se arregla**: en la capa, dando a `list-surface` una entrada de receta
como se hizo con `calendar-surface` (que resuelve por talla en su gancho pero
cuyo vocabulario vive en la receta de la familia). Registrado en
`next-features` §13; toca diez componentes de golpe, así que es su propio eje.
**Lo demás**: el `background: var(--color-surface-overlay)` del item resaltado
es la incidencia ya escrita del velo de estado (§12: el `highlighted` pinta dos
veces, plano + velo, y no se bendice el duplicado), y el `100%` de
`[data-block]` es identidad.
<!-- veredicto:end -->

Powered by TurnKey Linux.