19 KiB
Auditoría de alcance de tema — el catálogo entero
Generado, no escrito a mano:
node --import tsx/esm scripts/theming-census.ts --report. Este README es la vista de conjunto; una ficha por componente al lado, con su análisis y su propuesta de corrección. Método, clases, protocolo de verificación y fases:PLAN-theming.md. La auditoría del SISTEMA de theming (fase 1, cerrada 2026-07-07) estheming-audit.md; ésta es la deuda de adopción que aquélla dejó apuntada en su §5.3-3.
- Medido: 2026-08-22 · 162 recetas con CSS + 8 componentes sin receta = 170 fichas, el árbol entero de
eidos/components/ - La pregunta: ¿cuánto de la apariencia de cada componente puede cambiar un tema sin tocar el sistema ni la receta?
- Alcance global: 58% — 2727 de 4682 knobs pasan por un token público del componente
- Reparto: público 2727 · privado 338 · global 1064 · literal 553 · sistema transversal 509 (fuera del ratio)
- Sin token público propio: 24 · alcance < 20 %: 17 · alcance 100 %: 17 · con eje
size: 58
Cómo se lee
| clase | qué es | ¿lo alcanza un tema del componente? |
|---|---|---|
public |
var(--{c}-…), el contrato del componente |
sí |
private |
var(--_{c}-…), nombre interno |
sólo si el privado deriva de un público (cada ficha lo dice en §3) |
global |
primitivo del sistema (--space-*, --radius-*, --color-*, bundle --size-{k}-*…) |
sólo moviendo el sistema entero |
literal |
ni token: 8px, 1.25, #fff |
no — y viola R-2/R-4 |
system |
sistemas transversales que la receta CONSUME por contrato (capa de estado, anillo de foco, planos de depth, motion, bandas z, opacidad, shape, floating-gap) | sí, a nivel de sistema, por diseño (recipe-contract §2) — fuera del ratio |
Alcance = public / (public + private + global + literal).
Límites de la medida (regex sobre el CSS; sobre-reporta, nunca infra-reporta):
una declaración con varios tokens se clasifica por la primera clase que casa
(público → privado → sistema → global); el token público de OTRO componente
cuenta como global y la ficha lo marca «⤴ prestado»; los valores dentro de
@keyframes cuentan como cualquier otro. Tres defectos que ningún regex ve —
privado que no deriva, velo en el nodo equivocado, doble animación — van en la
checklist §4.4 de cada ficha.
Qué NO propone una ficha
La propuesta deriva nombres de la doctrina; no la contradice. Por eso una ficha nunca propone acuñar un token para:
- lo que posee una capa compartida (
list-surface,spin-field,picker-shell,viewport-placement,field-segment-state,menu-indicator,sliding-indicator): un eje = un token público de la capa- una ranura privada, consumido
var(--_x, var(--x))— el consumidor no acuña--{c}-{eje}(README deeidos/components, «Capas compartidas» regla 2);
- una ranura privada, consumido
- el foco (
--focus-ring-*, theming §32) ni la capa de estado (--state-*, §38): un knob del sistema, no del componente; - un token prestado de otro componente (la familia
calendaren los pickers): la corrección es la decisión de familia registrada entheming-audit.md§B, no un duplicado con prefijo propio; - un shorthand o un eje físico (
padding,border,height): primero se parte en ejes lógicos (recipe-contract §1, R-4.4), y eso es decisión.
Sin receta CSS (8)
Ni un .css en su directorio: no hay knob que medir, y eso es una
respuesta, no un hueco. Cada ficha dice —medido, no supuesto— dónde vive su
visual: el componente que compone, la capa compartida que consume, o la receta
ajena que pinta sus attrs (y cuyos knobs ya están contados allí).
affix · count-up · format-date · format-number · icon-button · relative-time · svg · trans
Y el vocabulario de nombres tiene una desviación medida: el catálogo habla
mayoritariamente con el modificador al final (-bg-hover, -bg-active) mientras
theming §6.7 lo pone delante (hover-bg). Las fichas proponen la forma
documentada y lo dejan anotado: la normalización es D-TH.6, sin firmar.
Los 20 con más knobs fuera del contrato
| # | componente | fuera de alcance | alcance | knobs | contrato |
|---|---|---|---|---|---|
| 1 | palabras | 363 | 22% | 471 | 0 |
| 2 | chronos | 235 | 1% | 245 | 9 |
| 3 | proof-of-human | 77 | 15% | 95 | 20 |
| 4 | prose | 73 | 1% | 74 | 2 |
| 5 | time-range-picker | 67 | 20% | 88 | 22 |
| 6 | time-picker | 47 | 18% | 59 | 16 |
| 7 | color-picker | 39 | 51% | 82 | 54 |
| 8 | field-langs | 39 | 0% | 41 | 0 |
| 9 | date-range-picker | 30 | 40% | 53 | 22 |
| 10 | radio-cards | 28 | 60% | 72 | 39 |
| 11 | drawer | 26 | 57% | 62 | 44 |
| 12 | popover | 26 | 46% | 51 | 36 |
| 13 | natural-time-picker | 25 | 67% | 77 | 62 |
| 14 | nav-tree | 23 | 4% | 25 | 1 |
| 15 | menubar | 22 | 15% | 27 | 5 |
| 16 | password-field | 21 | 40% | 36 | 23 |
| 17 | aura | 20 | 43% | 35 | 24 |
| 18 | float-panel | 20 | 59% | 53 | 28 |
| 19 | field | 19 | 72% | 73 | 60 |
| 20 | navigation-menu | 19 | 44% | 38 | 26 |
Sin ningún token público propio (24)
Ni una clave pública en lib/recipes/base.ts — alguno tiene privados forward
(_palette-*), que no son contrato: un tema no puede nombrarlos.
alert-dialog · aspect-ratio · auto-grid · avatar · cascade · css-field · date-picker · display · field-langs · heading · highlight · mask-field · mockup · month-grid · motion · number-field · palabras · range-calendar · stack · text · text-blur · text-scramble · wrap · year-grid
Tabla completa (162 recetas, por alcance ascendente)
La columna «contrato» cuenta las claves públicas del bloque del componente.
| componente | alcance | knobs | público | privado | global | literal | sistema | contrato | size |
|---|---|---|---|---|---|---|---|---|---|
| range-calendar | 0% | 98 | 0 | 7 | 2 | 4 | 85 | 0 | – |
| month-grid | 0% | 61 | 0 | 7 | 0 | 5 | 49 | 0 | – |
| year-grid | 0% | 61 | 0 | 7 | 0 | 5 | 49 | 0 | – |
| field-langs | 0% | 41 | 0 | 0 | 31 | 8 | 2 | 0 | – |
| aspect-ratio | 0% | 3 | 0 | 0 | 1 | 2 | 0 | 0 | – |
| date-picker | 0% | 2 | 0 | 0 | 0 | 2 | 0 | 0 | – |
| sticky | 0% | 2 | 0 | 1 | 0 | 1 | 0 | 1 | – |
| text-blur | 0% | 2 | 0 | 0 | 0 | 2 | 0 | 0 | – |
| cascade | 0% | 1 | 0 | 0 | 0 | 1 | 0 | 0 | – |
| motion | 0% | 1 | 0 | 0 | 0 | 1 | 0 | 0 | – |
| section | 0% | 1 | 0 | 0 | 0 | 1 | 0 | 4 | – |
| chronos | 1% | 245 | 3 | 21 | 202 | 12 | 7 | 9 | y |
| prose | 1% | 74 | 1 | 0 | 43 | 30 | 0 | 2 | y |
| nav-tree | 4% | 25 | 1 | 0 | 17 | 6 | 1 | 1 | – |
| proof-of-human | 15% | 95 | 14 | 17 | 29 | 31 | 4 | 20 | – |
| menubar | 15% | 27 | 4 | 6 | 16 | 0 | 1 | 5 | y |
| time-picker | 18% | 59 | 10 | 12 | 24 | 11 | 2 | 16 | y |
| anchor-nav | 20% | 11 | 2 | 0 | 8 | 0 | 1 | 2 | – |
| time-range-picker | 20% | 88 | 17 | 19 | 33 | 15 | 4 | 22 | y |
| palabras | 22% | 471 | 103 | 1 | 272 | 90 | 5 | 0 | – |
| skin-media-player | 22% | 11 | 2 | 0 | 1 | 6 | 2 | 10 | y |
| date-range-field | 25% | 9 | 2 | 0 | 4 | 2 | 1 | 3 | – |
| time-range-field | 25% | 9 | 2 | 0 | 4 | 2 | 1 | 3 | – |
| surface | 25% | 4 | 1 | 3 | 0 | 0 | 0 | 25 | – |
| callout | 27% | 15 | 4 | 3 | 7 | 1 | 0 | 28 | – |
| onion-menu | 29% | 27 | 7 | 0 | 8 | 9 | 3 | 12 | – |
| empty-state | 29% | 17 | 5 | 7 | 5 | 0 | 0 | 6 | y |
| banner | 30% | 10 | 3 | 5 | 1 | 1 | 0 | 42 | y |
| scroll-frames | 33% | 9 | 3 | 1 | 1 | 4 | 0 | 4 | – |
| toggle-group | 33% | 3 | 1 | 0 | 1 | 1 | 0 | 1 | – |
| link | 36% | 14 | 4 | 4 | 1 | 2 | 3 | 5 | – |
| audio-player | 37% | 19 | 7 | 0 | 8 | 4 | 0 | 5 | – |
| menu-dial | 38% | 8 | 3 | 0 | 1 | 4 | 0 | 3 | y |
| mockup | 38% | 29 | 11 | 0 | 13 | 5 | 0 | 0 | – |
| date-range-picker | 40% | 53 | 20 | 9 | 19 | 2 | 3 | 22 | y |
| password-field | 40% | 36 | 14 | 2 | 9 | 10 | 1 | 23 | y |
| button-group | 40% | 5 | 2 | 0 | 0 | 3 | 0 | 1 | – |
| aura | 43% | 35 | 15 | 0 | 14 | 6 | 0 | 24 | y |
| qr-code | 43% | 7 | 3 | 0 | 0 | 4 | 0 | 10 | – |
| navigation-menu | 44% | 38 | 15 | 1 | 13 | 5 | 4 | 26 | y |
| skeleton | 45% | 11 | 5 | 5 | 0 | 1 | 0 | 9 | – |
| popover | 46% | 51 | 22 | 4 | 22 | 0 | 3 | 36 | y |
| text-scramble | 50% | 4 | 2 | 0 | 0 | 2 | 0 | 0 | – |
| split-button | 50% | 2 | 1 | 0 | 1 | 0 | 0 | 1 | y |
| color-picker | 51% | 82 | 40 | 6 | 22 | 11 | 3 | 54 | y |
| tooltip | 52% | 22 | 11 | 6 | 3 | 1 | 1 | 20 | y |
| context-menu | 53% | 33 | 16 | 0 | 11 | 3 | 3 | 18 | – |
| waveform | 55% | 11 | 6 | 0 | 1 | 4 | 0 | 10 | y |
| sidebar | 55% | 45 | 23 | 0 | 14 | 5 | 3 | 50 | – |
| button | 55% | 30 | 16 | 6 | 0 | 7 | 1 | 105 | y |
| color-field | 56% | 16 | 9 | 0 | 2 | 5 | 0 | 22 | y |
| background | 57% | 26 | 13 | 1 | 6 | 3 | 3 | 31 | – |
| drawer | 57% | 62 | 34 | 8 | 13 | 5 | 2 | 44 | y |
| dropdown-menu | 57% | 30 | 16 | 0 | 9 | 3 | 2 | 19 | – |
| s-text-virtual-list | 58% | 12 | 7 | 0 | 3 | 2 | 0 | 9 | – |
| badge | 59% | 22 | 13 | 6 | 1 | 2 | 0 | 65 | y |
| float-panel | 59% | 53 | 29 | 3 | 12 | 5 | 4 | 28 | – |
| checkbox | 59% | 33 | 19 | 8 | 2 | 3 | 1 | 44 | y |
| radio-cards | 60% | 72 | 42 | 0 | 24 | 4 | 2 | 39 | y |
| image | 60% | 35 | 21 | 1 | 1 | 12 | 0 | 22 | y |
| cropper | 60% | 27 | 15 | 0 | 3 | 7 | 2 | 15 | – |
| mark | 60% | 5 | 3 | 2 | 0 | 0 | 0 | 3 | – |
| emoji-picker | 61% | 32 | 17 | 0 | 8 | 3 | 4 | 17 | – |
| pin-input | 61% | 19 | 11 | 0 | 6 | 1 | 1 | 20 | y |
| clipboard | 63% | 8 | 5 | 2 | 1 | 0 | 0 | 6 | – |
| spin-field | 64% | 27 | 16 | 0 | 8 | 1 | 2 | 20 | – |
| meter | 64% | 14 | 9 | 1 | 2 | 2 | 0 | 28 | y |
| tag-group | 65% | 46 | 28 | 14 | 0 | 1 | 3 | 91 | y |
| image-adjustments | 67% | 18 | 10 | 0 | 4 | 1 | 3 | 12 | – |
| virtual-list | 67% | 13 | 8 | 0 | 0 | 4 | 1 | 12 | – |
| separator | 67% | 6 | 4 | 0 | 0 | 2 | 0 | 3 | – |
| natural-time-picker | 67% | 77 | 51 | 5 | 14 | 6 | 1 | 62 | y |
| listbox | 68% | 37 | 23 | 9 | 1 | 1 | 3 | 21 | – |
| progress | 69% | 35 | 24 | 2 | 3 | 6 | 0 | 41 | y |
| search-field | 69% | 14 | 9 | 0 | 0 | 4 | 1 | 17 | y |
| spinner | 70% | 23 | 16 | 3 | 0 | 4 | 0 | 18 | – |
| color-swatch | 70% | 10 | 7 | 0 | 3 | 0 | 0 | 12 | y |
| form | 71% | 52 | 36 | 8 | 5 | 2 | 1 | 54 | y |
| image-picker | 71% | 20 | 12 | 0 | 1 | 4 | 3 | 13 | – |
| skip-link | 71% | 8 | 5 | 0 | 0 | 2 | 1 | 8 | – |
| field | 72% | 73 | 49 | 1 | 7 | 11 | 5 | 60 | y |
| dialog | 73% | 33 | 24 | 4 | 4 | 1 | 0 | 37 | y |
| virtual-grid | 73% | 12 | 8 | 0 | 0 | 3 | 1 | 12 | – |
| timeline | 73% | 49 | 36 | 10 | 0 | 3 | 0 | 45 | y |
| metrics | 74% | 50 | 37 | 0 | 9 | 4 | 0 | 68 | y |
| editable | 75% | 44 | 30 | 7 | 1 | 2 | 4 | 56 | y |
| avatar | 75% | 36 | 27 | 3 | 0 | 6 | 0 | 0 | y |
| knob | 75% | 27 | 18 | 1 | 1 | 4 | 3 | 33 | – |
| switch | 76% | 22 | 16 | 4 | 1 | 0 | 1 | 51 | y |
| file-upload | 76% | 61 | 45 | 9 | 1 | 4 | 2 | 98 | y |
| carousel | 77% | 35 | 23 | 2 | 0 | 5 | 5 | 27 | – |
| radio-group | 77% | 75 | 56 | 10 | 7 | 0 | 2 | 61 | y |
| chat-log | 77% | 42 | 30 | 0 | 4 | 5 | 3 | 31 | – |
| stepper | 79% | 60 | 44 | 11 | 0 | 1 | 4 | 69 | y |
| chat-composer | 79% | 44 | 31 | 0 | 3 | 5 | 5 | 31 | – |
| card | 80% | 36 | 28 | 7 | 0 | 0 | 1 | 97 | y |
| textarea | 80% | 28 | 20 | 2 | 0 | 3 | 3 | 37 | – |
| rating-group | 80% | 16 | 12 | 0 | 1 | 2 | 1 | 25 | y |
| code-block | 81% | 26 | 21 | 2 | 3 | 0 | 0 | 20 | – |
| toggle | 81% | 28 | 22 | 1 | 1 | 3 | 1 | 119 | y |
| fab | 82% | 17 | 14 | 0 | 2 | 1 | 0 | 10 | – |
| select | 83% | 72 | 54 | 5 | 3 | 3 | 7 | 101 | y |
| calendar | 84% | 71 | 57 | 7 | 0 | 4 | 3 | 74 | – |
| chat-message | 84% | 84 | 65 | 0 | 6 | 6 | 7 | 75 | y |
| grid-list | 85% | 31 | 22 | 2 | 1 | 1 | 5 | 38 | – |
| combobox | 85% | 97 | 76 | 4 | 3 | 6 | 8 | 106 | – |
| kbd | 86% | 14 | 12 | 0 | 1 | 1 | 0 | 13 | – |
| table | 86% | 46 | 37 | 3 | 0 | 3 | 3 | 45 | – |
| result | 86% | 22 | 19 | 0 | 0 | 3 | 0 | 27 | – |
| announce | 88% | 8 | 7 | 0 | 1 | 0 | 0 | 9 | – |
| media-player | 88% | 49 | 43 | 0 | 0 | 6 | 0 | 61 | – |
| tags-input | 88% | 46 | 37 | 4 | 1 | 0 | 4 | 90 | y |
| gradient-picker | 88% | 27 | 23 | 0 | 2 | 1 | 1 | 25 | – |
| drag-drop | 89% | 16 | 8 | 1 | 0 | 0 | 7 | 9 | – |
| chat-typing | 89% | 9 | 8 | 0 | 1 | 0 | 0 | 7 | – |
| chart | 89% | 95 | 81 | 0 | 0 | 10 | 4 | 45 | – |
| card-group | 90% | 22 | 18 | 0 | 0 | 2 | 2 | 20 | – |
| collapsible | 90% | 12 | 9 | 0 | 1 | 0 | 2 | 11 | – |
| accordion | 91% | 65 | 58 | 1 | 0 | 5 | 1 | 76 | y |
| toolbar | 91% | 35 | 30 | 2 | 0 | 1 | 2 | 54 | y |
| tree-view | 92% | 28 | 24 | 1 | 0 | 1 | 2 | 46 | – |
| gradient-builder | 93% | 60 | 52 | 0 | 1 | 3 | 4 | 72 | – |
| feed | 94% | 33 | 30 | 0 | 1 | 1 | 1 | 50 | – |
| scroll-area | 94% | 17 | 15 | 0 | 0 | 1 | 1 | 13 | – |
| tree-grid | 95% | 42 | 37 | 1 | 0 | 1 | 3 | 47 | – |
| picker-shell | 95% | 21 | 20 | 0 | 1 | 0 | 0 | 31 | y |
| tabs | 96% | 76 | 72 | 0 | 2 | 1 | 1 | 79 | y |
| slider | 97% | 33 | 31 | 0 | 0 | 1 | 1 | 39 | y |
| toast | 98% | 42 | 41 | 0 | 0 | 1 | 0 | 85 | – |
| command | 98% | 52 | 49 | 0 | 0 | 1 | 2 | 57 | – |
| heading | — | 36 | 0 | 0 | 0 | 0 | 36 | 0 | – |
| s-text | 100% | 30 | 2 | 0 | 0 | 0 | 28 | 3 | – |
| pagination | 100% | 26 | 24 | 0 | 0 | 0 | 2 | 34 | y |
| text | — | 24 | 0 | 0 | 0 | 0 | 24 | 0 | – |
| breadcrumb | 100% | 23 | 20 | 0 | 0 | 0 | 3 | 28 | y |
| chat-list | 100% | 18 | 18 | 0 | 0 | 0 | 0 | 18 | – |
| splitter | 100% | 18 | 17 | 0 | 0 | 0 | 1 | 15 | – |
| link-preview | 100% | 17 | 14 | 0 | 0 | 0 | 3 | 35 | – |
| code | 100% | 13 | 7 | 0 | 0 | 0 | 6 | 6 | – |
| date-field | 100% | 11 | 11 | 0 | 0 | 0 | 0 | 21 | y |
| time-field | 100% | 11 | 11 | 0 | 0 | 0 | 0 | 21 | y |
| text-gradient | 100% | 10 | 10 | 0 | 0 | 0 | 0 | 7 | – |
| box | 100% | 9 | 9 | 0 | 0 | 0 | 0 | 43 | – |
| label | 100% | 8 | 2 | 0 | 0 | 0 | 6 | 2 | – |
| text-focus | 100% | 8 | 8 | 0 | 0 | 0 | 0 | 8 | – |
| display | — | 6 | 0 | 0 | 0 | 0 | 6 | 0 | – |
| text-circular | 100% | 6 | 6 | 0 | 0 | 0 | 0 | 6 | – |
| barcode | 100% | 5 | 5 | 0 | 0 | 0 | 0 | 7 | – |
| flex | 100% | 2 | 2 | 0 | 0 | 0 | 0 | 7 | – |
| grid | 100% | 2 | 2 | 0 | 0 | 0 | 0 | 13 | – |
| alert-dialog | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| auto-grid | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| container | — | 0 | 0 | 0 | 0 | 0 | 0 | 1 | – |
| css-field | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| float | — | 0 | 0 | 0 | 0 | 0 | 0 | 1 | – |
| group | — | 0 | 0 | 0 | 0 | 0 | 0 | 1 | – |
| highlight | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| icon | — | 0 | 0 | 0 | 0 | 0 | 0 | 2 | – |
| mask-field | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| number-field | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| stack | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| wrap | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
Veredicto y decisiones
(pendiente — lo escribe el autor; se conserva al regenerar)