8.7 KiB
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 · método y protocolo:PLAN-theming.md§1, §2, §7.
- Medido: 2026-08-20 · Alcance: 68% — 23 de 34 knobs por token público
- Knobs de apariencia: 37 — público 23 · privado 9 · global 1 · literal 1 · sistema 3 (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:131 |
[data-listbox-item][data-highlighted]:not([data-disabled]) |
background |
var(--color-surface-overlay) |
1.2 A través de un privado (9)
| # | fichero:línea | selector | propiedad | valor |
|---|---|---|---|---|
| 1 | listbox.css:49 |
[data-listbox] |
font-size |
var(--_listbox-item-font-size) |
| 2 | listbox.css:117 |
[data-listbox-item] |
gap |
var(--_listbox-item-gap) |
| 3 | listbox.css:118 |
[data-listbox-item] |
min-block-size |
var(--_listbox-item-height) |
| 4 | listbox.css:119 |
[data-listbox-item] |
padding-block |
var(--_listbox-item-padding-block) |
| 5 | listbox.css:120 |
[data-listbox-item] |
padding-inline |
var(--_listbox-item-padding-inline) |
| 6 | listbox.css:139 |
[data-listbox-item][data-selected]:not([data-disabled]) |
background |
var(--_listbox-palette-element) |
| 7 | listbox.css:143 |
[data-listbox-item][data-selected][data-highlighted]:not([data-disabled]) |
background |
var(--_listbox-palette-element) |
| 8 | listbox.css:164 |
[data-listbox-item-indicator] |
color |
var(--_listbox-palette-solid) |
| 9 | listbox.css:182 |
[data-listbox-group-label] |
padding-inline |
var(--_listbox-item-padding-inline) |
1.3 Literales (1)
| # | fichero:línea | selector | propiedad | valor |
|---|---|---|---|---|
| 1 | listbox.css:65 |
[data-listbox][data-block] |
inline-size |
100% |
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:99 |
[data-listbox][data-disabled] |
opacity |
var(--opacity-disabled) |
| 2 | listbox.css:109 |
[data-listbox]:focus-visible, [data-listbox][data-focused] |
outline |
var(--focus-ring-width) solid var(--focus-ring-color) |
| 3 | listbox.css:149 |
[data-listbox-item][data-disabled] |
opacity |
var(--opacity-disabled) |
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 comovar(--_x, var(--x)); el consumidor no acuña--listbox-{eje}para él — sería un vocabulario paralelo (README deeidos/components, «Capas compartidas» regla 2). - Consume la capa compartida
menu-indicator. Un eje que la capa posee se consume comovar(--_x, var(--x)); el consumidor no acuña--listbox-{eje}para él — sería un vocabulario paralelo (README deeidos/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(--list-font-size) |
1 |
item-gap |
root |
var(--list-item-gap) |
1 |
item-height |
root |
var(--list-item-height) |
1 |
item-padding-block |
root |
var(--list-item-padding-block) |
1 |
item-padding-inline |
root |
var(--list-item-padding-inline) |
1 |
item-bg |
root |
var(--color-surface-overlay) |
1 |
group-label-padding-inline |
root |
var(--list-item-padding-inline) |
1 |
4.2 Sin nombre mecánico (4)
- ⚠ 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: el privado que alimenta este knob no se declara en el CSS (viene de
base.tso de un estilo inline) — hay que resolverlo antes de nombrarlo — 3:background,color.
4.3 Avisos sobre los tokens propuestos (5)
- el privado
--_listbox-item-font-sizedebe pasar a leer este público (o desaparecer) —--listbox-font-size - el privado
--_listbox-item-gapdebe pasar a leer este público (o desaparecer) —--listbox-item-gap - el privado
--_listbox-item-heightdebe pasar a leer este público (o desaparecer) —--listbox-item-height - el privado
--_listbox-item-padding-blockdebe pasar a leer este público (o desaparecer) —--listbox-item-padding-block - el privado
--_listbox-item-padding-inlinedebe pasar a leer este público (o desaparecer) —--listbox-item-padding-inline,--listbox-group-label-padding-inline
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, unbackgrounden 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
Revisión 2026-08-20 — verificación previa a implementación (Opus).
Análisis: CORRECTO (38 · 0 · 9 · 23 · 3 · 3). Consume DOS capas
compartidas: list-surface y menu-indicator.
Propuesta: APTA CON UNA CORRECCIÓN QUE ES LA REGLA DE ORO DE LAS CAPAS.
- La fila
font-size = var(--list-font-size)NO se acuña: ese token es PÚBLICO DE LA CAPAlist-surface(lib/list-surface.css:24-44, por talla). Duplicarlo como--listbox-font-sizees exactamente el «vocabulario paralelo» que la nota de arriba prohíbe — la capa posee el eje, el consumidor escribe la RANURA privada si diverge (var(--_x, var(--x))). Revisar el resto de la tabla contra los tokens de la capa (--list-item-height/-padding-*/-gapy--list-font-size): toda fila cuyo valor sea uno de ellos se RETIRA de la propuesta. - El resto (bg, radius, borde de la superficie, checkmark del indicador si
no es de
menu-indicator): verbatim válido, scoperoot.
Sin eje size (nada estampa data-size — la talla del listbox la lleva
la capa list-surface por contexto): no inventar coordenadas por talla aquí.
Estados de opción (highlighted/checked): el hover/highlight viene del
arquetipo — verificar con el píxel-arriba que ningún hover-* propuesto
duplique la capa de estado antes de acuñarlo.
Bloqueos de firma: ninguno duro.