# PLAN — Theming: que TODO componente sea personalizable por un tema (eje `theme-reach`)
> **Estado: PLAN ENTREGADO 2026-08-19 — NADA CONSTRUIDO (salvo el instrumento
> de medida), NADA FIRMADO.** Falta que el autor firme las decisiones D-TH de
> §4 (una por mensaje, con sus cinco preguntas) y ordene el arranque de F0.
>
> **Origen (conversación 2026-08-19)**: al arreglar `navigation-menu` salió que
> el radio de su trigger vivía en un privado clavado a `--radius-default` y que
> el contrato del componente sólo publicaba tres knobs: ningún tema podía decir
> «píldora» sin mover el radio del sistema entero. El autor, textual: **«esto
> ha abierto una duda importante, y es si los componentes creados son
> themables; si no lo son, es un error como framework. Necesitaríamos
> desarrollar un plan para auditar todos los componentes, verificar qué grado
> de personalización soportan y desarrollar una corrección a cada uno de ellos
> para que sean totalmente personalizables.»** Y sobre la ejecución: **«lo
> ejecutará Opus, y Opus falla mucho, por lo que tienes que establecer procesos
> de verificación en todo el plan.»** Este documento obedece a las dos frases:
> §1 es la auditoría MEDIDA (reproducible por script, no por memoria), §5 son
> las fases, y §7 es el protocolo de verificación que cada sesión ejecuta sin
> excepción.
>
> **Kickoff para sesión nueva**: _«Lee `docs/process/PLAN-theming.md` ENTERO y
> después `CONTINUE-theming.md`; si las D-TH de §4 están firmadas, ejecuta el
> siguiente bloque de §5 con el protocolo de §7, un componente por commit; si
> no, PARA y preséntalas una a una.»_
>
> Fuentes de doctrina que este plan cita y NO copia:
> [`docs/theming/reference.md`](../theming/reference.md) §1bis, §3, §5, §6,
> §7, §12, §16 · [`docs/canon/tsc.md`](../canon/tsc.md) ·
> [`docs/canon/recipe-contract.md`](../canon/recipe-contract.md) §1–§5 ·
> [`docs/theming/motion.md`](../theming/motion.md) §"When both write
> `animation` on ONE node" · [`docs/guides/component-guide.md`](../guides/component-guide.md)
> §Build contract · [`src/uix/eidos/components/README.md`](../../src/uix/eidos/components/README.md)
> · el precedente ejecutado y firmado: [`PLAN-sidebar.md`](./PLAN-sidebar.md)
> §3 + F3 (el eje `size` de la fila, 2026-08-19) y `navigation-menu`
> (`ae9e277fa` · `a82794845`, el cromo del trigger en tokens públicos).
---
## 0. Veredicto corto
1. **La doctrina ya lo exige; los guards no lo comprueban.** recipe-contract §1:
«Every recipe declares its knobs in `lib/recipes/base.ts`»; theming §6: el
token público de componente es `--{c}-{slot}`; la TSC decide dónde se emite.
Pero R-1…R-4 comprueban que **no haya literales** (y que lo que haya sea un
`var(`), no que cada knob sea **alcanzable por un tema**. Una receta con
todo en `var(--radius-md)` y en privados `--_{c}-*` pasa `component:audit`
en PASS y la dimensión 10 «Adopción theming» de la ficha en ✓ — y sólo se
puede temar moviendo el planeta. Es un error de framework porque rompe la
promesa que el framework hace por escrito.
2. **Medido, no opinado** (§1, `scripts/theming-census.ts`): de 5.205 knobs
de apariencia en 162 recetas, **1.621 (33 %) pasan por un token público del
componente**; 1.880 atan directo a un primitivo global; 856 a privados;
614 son literales. **62 componentes no tienen una sola entrada en el
contrato**; 59 tienen alcance < 20 %; 6 están al 100 %. `navigation-menu`,
tras el trabajo de hoy, está en 44 %: el eje no se cierra arreglando un
componente.
3. **La corrección es mecánica y ya tiene forma firmada**: la de la fila del
`Sidebar` (D-SB.5/D-SB.6, `PLAN-sidebar.md` §3) y la del trigger del nav
(`ae9e277fa`): tokens públicos por talla que apuntan al bundle
`--size-{k}-*`, nombre RESUELTO por `data-size`, acentos de estado en
`background-color`, y **el default queda idéntico** — sólo cambia quién
puede moverlo. Lo que NO hay que hacer: rediseñar nada por el camino.
4. **Sin guard no hay cierre**: el censo de hoy se repetirá en seis meses si
R-5 no existe. F0 crea el guard en `warn` con la cifra de hoy como suelo; F3
lo gradúa a `error` cuando el backfill termina. Igual que hizo R-4.x el
2026-07-02.
---
## 1. Inventario medido (2026-08-19)
**Instrumento**: `node --import tsx/esm scripts/theming-census.ts` (añadido
con este plan; `--json` para máquina; `--only {c}` por componente). Cuenta, por
receta CSS, cada declaración de APARIENCIA (lista `KNOB_PROPS` en el script:
fondo, tinta, borde, radio, sombra, padding, gap, tipografía, tamaño, opacidad,
fill/stroke, filtros) y la clasifica por el ORIGEN de su valor:
| Clase | Qué es | ¿La alcanza un tema de componente? |
| --- | --- | --- |
| `public` | `var(--{c}-…)` — token del contrato del componente | **Sí** |
| `private` | `var(--_{c}-…)` — nombre interno | No (salvo que el privado DERIVE de un público — el script no lo distingue, cuenta como no) |
| `global` | cualquier otro `var(--…)`: `--space-*`, `--radius-*`, `--color-*`, `--font-size-*`, `--size-*`… | Sólo moviendo el sistema |
| `literal` | sin `var(`: `8px`, `1.25`, `#fff` | No, y además viola R-4/R-2 |
| `system` | sistemas transversales que una receta CONSUME: 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)`.
### 1.1 Totales
| Medida | Valor |
| --- | --- |
| Componentes con receta CSS | 162 |
| Knobs de apariencia | 5.205 |
| Por token público del componente | **1.621 (33 %)** |
| Por privado | 856 |
| Directo a primitivo global | 1.880 |
| Literales | 614 |
| Sistema transversal (fuera del ratio) | 234 |
| **Sin ninguna entrada en el contrato** | **62** |
| Alcance < 20 % | 59 |
| Alcance = 100 % | 6 |
| Con eje `data-size` en la receta | 82 |
### 1.2 Por familia (propuesta de familias en D-TH.4 — el autor la firma o la cambia)
| Familia | Comp. | Knobs | Alcance | Sin contrato | Fuera del contrato |
| --- | ---: | ---: | ---: | ---: | ---: |
| Tiempo (chronos · fecha/hora) | 15 | 879 | 9 % | 9 | 774 |
| Campos | 24 | 905 | 38 % | 9 | 526 |
| Controles · marcas | 33 | 867 | 39 % | 9 | 502 |
| Menús · listas · navegación | 24 | 735 | 37 % | 10 | 432 |
| Superficies · overlays · estado | 34 | 807 | 45 % | 10 | 429 |
| WIP (`palabras`) | 1 | 471 | 22 % | 1 | 363 |
| Texto · tipografía | 12 | 238 | 9 % | 10 | 217 |
| Media · chat | 9 | 283 | 61 % | 1 | 104 |
| Layout (primitivos) | 9 | 14 | 93 % | 3 | 1 |
| (`separator`, sin familia asignada) | 1 | 6 | 67 % | 0 | 2 |
### 1.3 Los tres defectos que el censo NO ve y la ejecución sí tiene que mirar
El script es un regex sobre texto: sobre-reporta, nunca infra-reporta. Tres
clases de defecto de theming que un alcance del 100 % no garantiza:
- **Privado que no deriva de un público** (`--_c-radius: var(--radius-default)`):
cuenta como `private`; la corrección es que el privado lea el público o
desaparezca.
- **Velo o acento en el nodo equivocado**: `archetype: 'item'` en un `
`
envoltorio (`navigation-menu`, `822b78cf2`), o el shorthand `background`
matando la capa de estado (`Sidebar` F3, `navigation-menu` `ae9e277fa`). No
es un knob: es un nodo. Se detecta midiendo desde el píxel hacia arriba (§7.3).
- **Doble animación al mover un sello** a una superficie con animación propia
(`navigation-menu` `822b78cf2`: `present-rise` + la entrada de receta). Se
detecta con un registro de `animationstart/end`, no con `getComputedStyle`.
### 1.4 Tabla completa por componente
Al final del documento (§9), ordenada por alcance. Se REGENERA con el script
en cada bloque; no se edita a mano.
---
## 2. Contrato objetivo — qué significa «totalmente personalizable» (los cuatro ejes)
Un componente es personalizable cuando un tema puede moverlo **sin tocar el
sistema ni la receta**, en estos ejes:
| Eje | Mecanismo canónico | Cómo se comprueba |
| --- | --- | --- |
| **A · Tokens** (forma, color, espacio, tipografía) | todo knob de apariencia lee `--{c}-{slot}` del contrato (`recipes/base.ts`), nombre RESUELTO por talla/estado; los privados sólo derivan de públicos | censo al 100 % **+** prueba de centinela (§7.4) |
| **B · Talla** | `size` → `data-size` en el wrapper; tokens `{part}-{eje}-{k}` apuntando al bundle `--size-{k}-*` (theming §5; recipe-contract §1) | computed por talla = bundle; `md` idéntico al default anterior |
| **C · Color / variante** | `data-color` / `data-variant` vía paleta (`--{c}-{role}-{slot}`, `--{c}-palette-*`), los 9 roles (theming §4) | cada variante lee tokens, ninguna un `--scale-*` (R-4.6) |
| **D · Estados** | hover/press/selected neutros = la capa del sistema en el nodo CON forma; acentos de estado (`on-bg`…) en `background-color` para que la capa componga | `background-image` presente en hover, ``/envoltorios sin velo, abierto + hover = acento + capa |
**Regla de oro del eje**: **el default no cambia.** Cada componente se mide
antes y después en sus estados (reposo · hover · activo/abierto · disabled ·
foco) y tallas, y el diff de computed styles es **cero** salvo donde una
decisión firmada diga lo contrario. Este eje mueve QUIÉN puede cambiar cada
cosa, no QUÉ se ve.
---
## 3. Contrato objetivo — la regla R-5 «alcance de tema»
Nueva regla de `component-audit` (familia R-5), misma mecánica que R-4.x:
- **R-5.1** — Toda declaración de apariencia (`KNOB_PROPS`) en una receta lee
(a) un público `var(--{c}-…)`, (b) un privado `--_{c}-*` cuya definición en
la misma receta lee un público, o (c) un token de sistema transversal (la
lista `SYSTEM` del censo). Otra cosa es fallo.
- **R-5.2** — Todo componente con receta tiene entrada en `recipes/base.ts`.
- **R-5.3** — Los nombres de los tokens siguen recipe-contract §1 (ejes
lógicos, `{part}-{eje}-{k}`) y theming §6 regla 7 (slots de color: `bg, fg,
border, on-bg, on-fg, on-border, hover-bg, on-hover-bg`). **Hoy no se
cumple** ni en recetas viejas (`tabs`: `trigger-color-*`) ni en las de hoy
(`navigation-menu`: `trigger-bg-active`, `trigger-color-active`) — ver
D-TH.6.
- **Válvula** (recipe-contract §3): `R-5.1 exception: ` en el README
del componente, greppable, UNA por knob o grupo de knobs con la razón (p. ej.
«glifo ∝ diámetro, excepción canónica avatar/marker»). Sin razón escrita no
hay excepción.
- **Severidad**: `warn` desde F0 con el suelo del censo; `error` en F3 (D-TH.7).
---
## 4. Decisiones que firma el autor (una por mensaje, con sus cinco preguntas)
| Id | Decisión | Propuesta | Estado |
| --- | --- | --- | --- |
| **D-TH.1** | ¿R-5 es regla DURA? («todo knob público salvo sistema transversal o excepción firmada») | Sí. Es lo que recipe-contract §1 ya dice sin guard. | SIN FIRMAR |
| **D-TH.2** | Perímetro de «knob» | La lista `KNOB_PROPS` del script (apariencia; layout fuera). Cambios a la lista = cambio del guard, firmados aquí. | SIN FIRMAR |
| **D-TH.3** | Tracks WIP (`palabras`, `words`, `chronos` y su familia de fecha/hora) | En el CENSO siempre; en R-5 `error` sólo cuando salgan de su carril (coherente con R-4.x, que los excluye). | SIN FIRMAR |
| **D-TH.4** | Familias y ORDEN del backfill | El de §5 F2: menús/navegación → campos → controles → superficies → texto → media/chat → tiempo (el último por ser el más grande y estar en carril propio). Dentro de cada familia, sin contrato primero, luego por knobs fuera del contrato. | SIN FIRMAR |
| **D-TH.5** | El default NO cambia en este eje | Sí. Toda mejora visual que aparezca se anota como deuda y se firma aparte (como hoy con `navigation-menu`). | SIN FIRMAR |
| **D-TH.6** | Vocabulario de slots (R-5.3): ¿se NORMALIZAN los nombres que ya se desvían (`trigger-color-*` de `tabs`, `*-bg-active`/`*-color-active` del nav de hoy, …) con un codemod value-preserving como el de px/py del 2026-07-06? | Sí, en F1, ANTES del backfill — si no, el backfill escribirá más nombres fuera de vocabulario. Inventario exacto de desviaciones sale en F0 (el censo añade `--names`). | SIN FIRMAR |
| **D-TH.7** | Cuándo gradúa R-5 a `error` | Cuando el censo global dé 100 % o excepción documentada (F3), nunca antes de terminar F2 — un `error` a medio backfill rompe el `component:audit` de todo el parque. | SIN FIRMAR |
| **D-TH.8** | ¿Entra la exposición en la demo de temas (`/temas`: un panel que liste los tokens públicos por componente y permita moverlos)? | Sí pero DESPUÉS (F4, opcional): es la prueba viva de A; sin el backfill no hay nada que exponer. | SIN FIRMAR |
---
## 5. Fases y gates
Cada fase: **qué leer ANTES** (entero), **qué producir**, **guard**. Ninguna
arranca sin la anterior en verde. Todo bloque de F2 ejecuta el protocolo de §7
**por componente**, sin atajos: es la parte del plan que existe porque «Opus
falla mucho».
### F0 — Instrumento + guard en `warn` (una sesión)
- Leer: theming §1bis, §3, §5, §6, §7, §12, §16 · tsc.md entero ·
recipe-contract §1–§5 · `scripts/component-audit.ts` (cómo se declara una
regla R-x, `readmeException`, severidades) · `src/uix/eidos/recipe-css-contract.test.ts`
(qué ya guarda) · `PLAN-sidebar.md` §3 y F3 · este plan entero.
- Producir: (a) `scripts/theming-census.ts` endurecido: `--names` (inventario
de nombres de token fuera del vocabulario §1/§6.7), detección «privado que
deriva de público» (leer `--_c-x: var(--c-y)` en la misma receta y
reclasificar), salida `--json` estable; (b) **R-5.1/5.2/5.3 en
`component-audit`** con severidad `warn` y la válvula `R-5.x exception:`;
(c) `docs/theming/reference.md` §12 (tabla de tooling) gana la fila del censo;
(d) un test `scripts/theming-census.test.ts` o bajo `src/uix/eidos/` que
**fije el suelo**: alcance global ≥ el de hoy (33 %) y literales ≤ 614 — el
número SUBE o el test falla (muta-prueba: meter un literal en una receta y ver
el rojo antes de dar el test por bueno — memoria `a-guard-that-inspects-nothing-passes`.
Ya pasó una vez HOY: la primera versión del censo no contaba una regla de una
sola línea, y la mutación `padding-inline: 8px` no movió la cifra; se endureció
el regex y la mutación pasó a 6→7→8).
- Guard: `node --import tsx/esm scripts/theming-census.ts` reproduce §1.1 a
la cifra · `npm run component:audit` NO añade ningún `error` (sólo `warn`) ·
`npx vitest run src/uix/eidos` sin nuevos rojos (el rojo conocido es
`skin-media-player`, ajeno) · `npm run check` con cero errores en los
ficheros tocados (§7.6).
### F1 — Doctrina + vocabulario (una sesión)
- Leer: F0 + theming §6 (reglas estrictas) + el codemod del 2026-07-06
(recipe-contract §1, bloque «Normalización ejecutada») como molde.
- Producir: recipe-contract §1 gana la regla R-5 (y §2 una fila «Alcance de
tema»), §4 la tabla de enforcement, §5 el checklist (punto 12: «censo del
componente al 100 % o excepción»); theming §6 referencia cruzada; el
checklist de cierre (`docs/guides/completion-checklist.md`) y el
`COMPONENT_AUDIT_GUIDE` endurecen las dimensiones 5 y 10 («usa tokens» ≠
«es temable»: la ficha exige el % del censo). **D-TH.6**: codemod
value-preserving de los nombres fuera de vocabulario (lista de F0), con
`generated/base.css` regenerado y diff = renombres 1:1, computed idéntico en
navegador (mismo triple muro que px/py).
- Guard: `docs:check` 0 · el diff de `generated/base.css` son SOLO renombres ·
censo idéntico antes/después del codemod (un rename no cambia el alcance) ·
sonda de computed en 3 componentes renombrados = idéntico.
### F2 — Backfill por familias (6–8 bloques de sesión; **un componente = un commit**)
Orden propuesto (D-TH.4). Dentro de cada bloque: sin contrato primero, luego
por knobs fuera del contrato. Cada componente sigue §7 entero.
| Bloque | Familia | Comp. | Knobs fuera | Entran primero (sin contrato) |
| --- | --- | ---: | ---: | --- |
| B1 | Menús · listas · navegación | 24 | 432 | `command`, `listbox`, `table`, `tree-grid`, `tree-view`, `grid-list`, `virtual-list`, `virtual-grid`, `menu-dial`, `feed` — y `navigation-menu` (44 %) hasta el 100 % |
| B2 | Campos | 24 | 526 | `combobox` (1), `textarea`, `css-field`, `mask-field`, `number-field`, `field-langs`, `gradient-builder`, `gradient-picker`, `label` |
| B3 | Controles · marcas | 33 | 502 | `knob`, `avatar`, `kbd`, `link`, `mark`, `highlight`, `clipboard`, `proof-of-human`, `aspect-ratio` |
| B4 | Superficies · overlays · estado | 34 | 429 | `alert-dialog`, `announce`, `carousel`, `link-preview`, `skeleton`, `spinner`, `scroll-frames`, `motion`, `mockup`, `cascade`, `card-group` |
| B5 | Texto · tipografía | 12 | 217 | `heading`, `text`, `s-text`, `display`, `code`, `code-block`, `text-*` (los que no tienen contrato) |
| B6 | Media · chat | 9 | 104 | `skin-media-player` |
| B7 | Tiempo (chronos · fecha/hora) — **si D-TH.3 lo mete** | 15 | 774 | `range-calendar`, `month-grid`, `year-grid`, `date-picker`, `date-range-picker`, `time-picker`, `time-range-picker`, `natural-time-picker`, `picker-shell` |
| B8 | `palabras` — **si D-TH.3 lo mete** | 1 | 363 | — |
Por componente se produce: entrada (o ampliación) en `recipes/base.ts` con los
nombres de §1/§6.7 — dimensionales por talla si el componente tiene `size`
(patrón `Sidebar`: `{part}-{eje}-{k}` + nombre resuelto con `declarations` por
`scope: 'size:{k}'`), colores por rol/slot, acentos de estado; la receta
consume SOLO nombres resueltos (nunca un primitivo crudo, nunca un literal);
privados sólo si derivan de públicos; `generated/base.css` regenerado por el
script (**jamás a mano**, theming §16 F); README de eidos con la tabla de
tokens («Talla y tema», molde: `navigation-menu`); registro en §8.
- Guard por componente: §7 entero, y el censo `--only {c}` al **100 %** (o
`R-5.x exception:` con razón) — si no llega, el commit NO se hace.
- Guard por bloque: **revisión adversarial** (§7.7) antes de dar el bloque por
cerrado: un pase escéptico por componente que intente REFUTAR «el default es
idéntico» y «cada token alcanza» con medición propia, no con la del autor
del commit.
### F3 — R-5 a `error` + cierre (una sesión)
- Producir: severidad `error` en R-5.1/5.2/5.3 (D-TH.7); el test del suelo
pasa a exigir 100 % global (o excepciones listadas); theming §12 y
recipe-contract §4 lo reflejan; `CONTINUE-theming.md` cerrado.
- Guard: `component:audit` PASS en los 162 · censo 100 % · `docs:check` 0.
### F4 — Exposición en la demo de temas (opcional, D-TH.8)
- Producir: en `/temas` (o en cada demo) un panel que lista los públicos del
componente desde `recipes/base.ts` y los mueve en vivo — la prueba viva del
eje A, y el detector de tokens que no alcanzan (mover y que nada cambie).
- Guard: mover cada token del componente en la demo y ver cambiar el computed
(automatizable con el centinela de §7.4).
---
## 6. Deuda transversal que este plan destapa — NO se ejecuta aquí
| Id | Dónde | Qué |
| --- | --- | --- |
| **T-TH.1** | `component-audit` dims 5 y 10 | Dan ✓ a «usa tokens»; el criterio se endurece en F1. Las fichas ya escritas (`docs/audit/components/*.md`) quedan con el ✓ viejo: se corrigen al pasar cada componente por F2, no en masa. |
| **T-TH.2** | Vocabulario de slots (theming §6.7) | Existe y no se guarda: `tabs` (`trigger-color-*`), `navigation-menu` (`-bg-active`, `-color-active`) y más lo incumplen. D-TH.6. |
| **T-TH.3** | `menubar` | Misma clase que el nav: cromo de barra en privados («the bar itself styles via foundation privates», `recipes/base.ts`) + `commit-select` en el trigger al DESPLEGAR (otra sesión, sema). |
| **T-TH.4** | `archetype: 'item'` en envoltorios | Hay que censar qué otras partes ``/wrapper lo declaran (memoria `archetype-item-pulls-interactive-styling`: Timeline, Breadcrumb, NavigationMenu ya corregidos). Un guard de morfo («un `item` no envuelve a un `trigger`») es candidato. |
| **T-TH.5** | `--control-height-*` en `:root` (T-6 del sidebar) | La densidad/zoom por SUBÁRBOL no re-deriva; afecta a la promesa de theming «por ámbito». Eje de theming aparte. |
| **T-TH.6** | 25 knobs visuales fuera del morfo (`component-visual-attrs`, eje Affix) | Filas sin firmar `image/skeleton/qr-code/s-text`. El eje C (variantes) depende de ellas. |
---
## 7. PROTOCOLO DE VERIFICACIÓN — por componente, sin excepciones
Existe porque el que ejecuta falla, y porque en un solo día (2026-08-19,
`navigation-menu`) cuatro de estos pasos ausentes costaron cuatro commits de
corrección: se dio por arreglado un hover leyendo el nodo equivocado; se
resolvió un doble de animación en la dirección contraria a la doctrina; se
inventó un hover por componente; y se declaró «idéntico» sin captura. Cada paso
tiene un **artefacto** (número, captura, salida de comando) que va al mensaje
de commit. Sin artefacto, el paso no se ha hecho.
### 7.1 Antes de tocar el componente (las cinco preguntas + lectura)
1. Las cinco preguntas del autor, UN componente por mensaje cuando haya
decisión (nombres fuera de vocabulario, una excepción, un default que
parece defecto): qué soluciona · leído todo o presupuesto · qué afecta ·
qué doctrina · qué modificaciones. Si no hay decisión nueva (el componente
sólo necesita tokens con nombres canónicos), se ejecuta sin preguntar y se
reporta.
2. Leer ENTEROS: la receta del componente, su entrada en `recipes/base.ts`,
su README de eidos y de soma, su morfo (¿qué partes llevan arquetipo?), su
ficha en `docs/audit/components/`. Y la doctrina de §0 (fuentes) si la
sesión no la ha leído hoy.
3. `git status` + `git log -1`: ¿hay otra sesión con el árbol sucio?
**Nunca `git stash`** para medir una base (le arranca el trabajo a otro).
### 7.2 Medir ANTES (el suelo del «idéntico»)
4. `node --import tsx/esm scripts/theming-census.ts --only {c}` → guardar la
línea.
5. Sonda Playwright **headless desde la raíz del repo** (la pantalla oculta
congela rAF; `playwright` no resuelve desde el scratchpad) contra la demo
del componente: para cada parte visible y cada talla, computed de los
knobs (`backgroundColor`, `backgroundImage`, `color`, `borderRadius`,
`padding*`, `fontSize`, `lineHeight`, `blockSize`/`inlineSize`,
`boxShadow`, `borderColor`) en reposo · hover · activo/abierto ·
disabled · `:focus-visible`. Muestrear FUERA de callbacks de
`MutationObserver` (miden el instante anterior al flush). Guardar JSON.
6. Captura 2× de la demo en reposo y en hover (sirve de «antes» para la
comparación a ojo).
### 7.3 Editar (la forma firmada — nada más)
7. Tokens con los nombres de recipe-contract §1 / theming §6.7. Por talla si
hay `size`. Acentos de estado en `background-color`. Hover/press/selected
neutros = la capa del sistema (nunca `background:` a mano, nunca
`background-image: none` para «limpiar», nunca una escalera de color
propia). Privados sólo derivando de públicos. Literales: cero.
8. `npm run generate:eidos-css` y `git diff --stat src/uix/eidos/generated/`:
el diff son SOLO los tokens del componente. Si toca otra cosa, parar.
9. Si un envoltorio lleva `archetype: 'item'`/`'option'`/`'trigger'` sin ser
el interactivo, es una DECISIÓN (morfo): se presenta, no se toca de oficio.
### 7.4 Medir DESPUÉS (los tres artefactos)
10. **Idéntico**: repetir la sonda de 7.2 → diff de computed por parte/estado/talla
= **vacío**. Un solo valor distinto = parar y explicar (o es defecto
previo que se firma, o es error del commit).
11. **Alcanza**: la **prueba de centinela** — por cada token NUEVO del
componente, poner en el root del componente un valor imposible de
confundir (`9999px`, `rgb(1, 2, 3)`, `0.123`) y leer el computed del
nodo que lo consume: tiene que seguirlo. Si un token no mueve nada, es
un token que miente (fantasma o nombre equivocado). Se automatiza en la
misma sonda (`el.style.setProperty('--{c}-…', sentinel)` → `getComputedStyle`).
12. **Desde el píxel hacia arriba** en los estados de hover: `elementsFromPoint`
en el centro del control y la pila de ancestros con `backgroundImage` /
`backgroundColor` — el velo cae en el nodo CON forma y en ningún
envoltorio (memoria `measure-the-node-the-user-points-at`). Si hay
animación propia y un sello de sema en el mismo nodo, registro de
`animationstart/end` (no `getAnimations` en un solo instante): UNA
entrada por ocurrencia (memoria motion.md §"when both write `animation`").
13. Captura 2× después, al lado de la de antes. Mirarla.
### 7.5 Guards mecánicos (todos, cada componente)
14. `node --import tsx/esm scripts/theming-census.ts --only {c}` → **100 %** o
excepción escrita. Y el global NO baja.
15. `node --import tsx/esm scripts/component-audit.ts --only {c}` → PASS, sin
R-5 nuevos (en `warn` durante F2, pero se listan y se resuelven).
16. `node --import tsx/esm scripts/eidos-lint.ts {c}` → 0 invalid.
17. `npx vitest run src/uix/eidos` → sin rojos nuevos (comparar **por
fichero de test**, no por cifra: el rojo conocido es `skin-media-player`).
18. `npm run rtl:check` 0 · `npm run docs:check` 0 · `npx prettier --check`
en los ficheros tocados (ojo: `autocrlf=true` hace que prettier se queje
del CRLF del checkout — comprobar el contenido normalizado a LF, no el
fichero; git guarda LF).
19. `npm run check`: atribuir errores **por fichero** (`grep ERROR | grep {c}`),
nunca por cifra global; la rama tiene sesiones concurrentes y el total
se mueve solo. Si el log no llega a `COMPLETED`, es `tmp/lexical/`:
relanzar.
### 7.6 Commit (uno por componente)
20. `git add` con rutas EXPLÍCITAS (sólo lo del componente + `recipes/base.ts`
+ `generated/base.css` + su README + este plan §8); `git diff --cached --stat`
y `git show :ruta` para verificar el ÁRBOL INDEXADO, no el de trabajo;
`git status` para ver que no entra nada de otra sesión. Mensaje con
`git commit -F fichero` (backticks en `-m` se ejecutan en bash), en
castellano, con: qué knobs entran, la cifra del censo antes → después,
el diff de computed (vacío), el centinela (n tokens probados), los
guards. **Nunca `--amend`** en la rama compartida.
21. Registro en §8 de este plan (componente · commit · censo antes→después ·
excepciones) y, si el bloque cierra, en `CONTINUE-theming.md`.
### 7.7 Revisión adversarial por bloque
22. Al cerrar un bloque, una pasada escéptica (otra sesión o un pase separado)
que por componente intente REFUTAR con medición propia: «el default es
idéntico» (sonda 7.2 vs 7.4 rehecha, no leída del commit), «cada token
alcanza» (centinela rehecho), «el velo cae donde debe» (píxel arriba),
«no hay doble animación». Hallazgos reales se arreglan en el bloque; los
refutados se anotan. El molde es la revisión del `Sidebar` (21
hallazgos · 17 refutados · 4 reales, `bae03e55c`).
### 7.8 Lo que NO se hace en este eje (y se anota como deuda si aparece)
- Rediseñar un default (radio, color, altura…): deuda firmable, no cambio.
- Tocar sema/morfo salvo un arquetipo en un envoltorio (decisión aparte).
- Cascadear a otro componente «ya que estamos» (memoria `no-cascade-changes`).
- Inventar nombres de token fuera de §1/§6.7, o un hover/estado por componente.
- Editar `generated/base.css` a mano.
- Dar un paso por hecho sin su artefacto.
---
## 8. Registro
- 2026-08-19 — Censo medido (§1) con `scripts/theming-census.ts` (añadido en
este commit como instrumento, sin guard). Plan entregado. **Nada firmado,
nada construido.** Precedente ejecutado hoy que fija la forma:
`navigation-menu` (`ae9e277fa`: tokens del trigger por talla, default
idéntico; `a82794845`: el hover es el del sistema; `822b78cf2`: el ``
sin arquetipo) — y aun así el componente queda en 44 %, prueba de que hace
falta el guard.
---
## 9. Tabla completa por componente (2026-08-19 — regenerar con el script, no editar)
| Componente | Familia | Alcance | Knobs | Público | Privado | Global | Literal | Sistema | Contrato | size |
| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | :-: |
| `range-calendar` | Tiempo (chronos · fecha/hora) | 0% | 98 | 0 | 7 | 84 | 4 | 3 | 0 | y |
| `combobox` | Campos | 0% | 96 | 0 | 15 | 67 | 6 | 8 | 1 | y |
| `proof-of-human` | Controles · marcas | 0% | 95 | 0 | 18 | 36 | 37 | 4 | 0 | y |
| `time-range-picker` | Tiempo (chronos · fecha/hora) | 0% | 88 | 0 | 6 | 61 | 17 | 4 | 0 | y |
| `natural-time-picker` | Tiempo (chronos · fecha/hora) | 0% | 78 | 0 | 0 | 69 | 7 | 2 | 0 | y |
| `gradient-builder` | Campos | 0% | 62 | 0 | 8 | 46 | 3 | 5 | 0 | y |
| `month-grid` | Tiempo (chronos · fecha/hora) | 0% | 61 | 0 | 13 | 40 | 5 | 3 | 0 | y |
| `year-grid` | Tiempo (chronos · fecha/hora) | 0% | 61 | 0 | 13 | 40 | 5 | 3 | 0 | y |
| `time-picker` | Tiempo (chronos · fecha/hora) | 0% | 59 | 0 | 3 | 42 | 12 | 2 | 0 | y |
| `date-range-picker` | Tiempo (chronos · fecha/hora) | 0% | 55 | 0 | 0 | 49 | 3 | 3 | 0 | y |
| `command` | Menús · listas · navegación | 0% | 52 | 0 | 9 | 35 | 6 | 2 | 0 | y |
| `table` | Menús · listas · navegación | 0% | 46 | 0 | 23 | 14 | 5 | 4 | 0 | y |
| `media-player` | Media · chat | 0% | 45 | 0 | 0 | 37 | 8 | 0 | 2 | y |
| `tree-grid` | Menús · listas · navegación | 0% | 42 | 0 | 29 | 8 | 2 | 3 | 0 | y |
| `field-langs` | Campos | 0% | 41 | 0 | 0 | 31 | 8 | 2 | 0 | – |
| `gradient-picker` | Campos | 0% | 38 | 0 | 7 | 23 | 5 | 3 | 0 | y |
| `listbox` | Campos | 0% | 38 | 0 | 9 | 23 | 3 | 3 | 0 | – |
| `carousel` | Superficies · overlays · estado | 0% | 36 | 0 | 14 | 12 | 5 | 5 | 0 | y |
| `heading` | Texto · tipografía | 0% | 36 | 0 | 36 | 0 | 0 | 0 | 0 | – |
| `feed` | Menús · listas · navegación | 0% | 33 | 0 | 13 | 16 | 3 | 1 | 0 | y |
| `grid-list` | Menús · listas · navegación | 0% | 32 | 0 | 8 | 17 | 2 | 5 | 0 | y |
| `textarea` | Campos | 0% | 28 | 0 | 16 | 5 | 4 | 3 | 0 | y |
| `tree-view` | Menús · listas · navegación | 0% | 28 | 0 | 8 | 14 | 4 | 2 | 0 | y |
| `code-block` | Texto · tipografía | 0% | 26 | 0 | 2 | 24 | 0 | 0 | 0 | – |
| `card-group` | Superficies · overlays · estado | 0% | 24 | 0 | 0 | 18 | 4 | 2 | 0 | y |
| `text` | Texto · tipografía | 0% | 24 | 0 | 24 | 0 | 0 | 0 | 0 | – |
| `spinner` | Superficies · overlays · estado | 0% | 23 | 0 | 15 | 4 | 4 | 0 | 0 | y |
| `picker-shell` | Tiempo (chronos · fecha/hora) | 0% | 21 | 0 | 4 | 17 | 0 | 0 | 0 | y |
| `link-preview` | Superficies · overlays · estado | 0% | 17 | 0 | 4 | 9 | 1 | 3 | 0 | y |
| `drag-drop` | Controles · marcas | 0% | 16 | 0 | 3 | 6 | 0 | 7 | 1 | – |
| `kbd` | Controles · marcas | 0% | 14 | 0 | 1 | 9 | 4 | 0 | 0 | – |
| `link` | Controles · marcas | 0% | 14 | 0 | 4 | 2 | 5 | 3 | 0 | – |
| `code` | Texto · tipografía | 0% | 13 | 0 | 6 | 3 | 4 | 0 | 0 | – |
| `virtual-list` | Menús · listas · navegación | 0% | 13 | 0 | 2 | 6 | 4 | 1 | 0 | y |
| `virtual-grid` | Menús · listas · navegación | 0% | 12 | 0 | 2 | 6 | 3 | 1 | 0 | y |
| `skeleton` | Superficies · overlays · estado | 0% | 11 | 0 | 9 | 1 | 1 | 0 | 0 | y |
| `skin-media-player` | Media · chat | 0% | 11 | 0 | 0 | 2 | 7 | 2 | 0 | y |
| `scroll-frames` | Superficies · overlays · estado | 0% | 9 | 0 | 1 | 4 | 4 | 0 | 0 | – |
| `announce` | Superficies · overlays · estado | 0% | 8 | 0 | 0 | 8 | 0 | 0 | 0 | – |
| `clipboard` | Controles · marcas | 0% | 8 | 0 | 2 | 5 | 1 | 0 | 0 | – |
| `label` | Campos | 0% | 8 | 0 | 6 | 2 | 0 | 0 | 0 | – |
| `menu-dial` | Menús · listas · navegación | 0% | 8 | 0 | 3 | 1 | 4 | 0 | 0 | y |
| `display` | Texto · tipografía | 0% | 6 | 0 | 6 | 0 | 0 | 0 | 0 | – |
| `mark` | Controles · marcas | 0% | 5 | 0 | 2 | 1 | 2 | 0 | 0 | – |
| `aspect-ratio` | Controles · marcas | 0% | 3 | 0 | 0 | 1 | 2 | 0 | 0 | – |
| `date-picker` | Tiempo (chronos · fecha/hora) | 0% | 2 | 0 | 0 | 0 | 2 | 0 | 0 | – |
| `sticky` | Superficies · overlays · estado | 0% | 2 | 0 | 1 | 0 | 1 | 0 | 1 | – |
| `text-blur` | Texto · tipografía | 0% | 2 | 0 | 0 | 0 | 2 | 0 | 0 | – |
| `cascade` | Menús · listas · navegación | 0% | 1 | 0 | 0 | 0 | 1 | 0 | 0 | – |
| `motion` | Superficies · overlays · estado | 0% | 1 | 0 | 0 | 0 | 1 | 0 | 0 | – |
| `section` | Layout (primitivos) | 0% | 1 | 0 | 0 | 0 | 1 | 0 | 4 | – |
| `chronos` | Tiempo (chronos · fecha/hora) | 1% | 245 | 2 | 22 | 202 | 12 | 7 | 9 | y |
| `prose` | Texto · tipografía | 1% | 74 | 1 | 0 | 43 | 30 | 0 | 2 | y |
| `nav-tree` | Menús · listas · navegación | 4% | 25 | 1 | 0 | 17 | 6 | 1 | 1 | – |
| `s-text` | Texto · tipografía | 7% | 30 | 2 | 28 | 0 | 0 | 0 | 3 | – |
| `chart` | Controles · marcas | 9% | 92 | 8 | 0 | 66 | 14 | 4 | 17 | – |
| `result` | Superficies · overlays · estado | 9% | 22 | 2 | 1 | 15 | 4 | 0 | 8 | – |
| `mockup` | Superficies · overlays · estado | 14% | 29 | 4 | 7 | 13 | 5 | 0 | 0 | – |
| `menubar` | Menús · listas · navegación | 15% | 27 | 4 | 6 | 16 | 0 | 1 | 5 | y |
| `anchor-nav` | Menús · listas · navegación | 20% | 11 | 2 | 0 | 8 | 0 | 1 | 2 | – |
| `banner` | Superficies · overlays · estado | 20% | 10 | 2 | 6 | 1 | 1 | 0 | 42 | y |
| `button` | Controles · marcas | 21% | 30 | 6 | 16 | 0 | 7 | 1 | 105 | y |
| `palabras` | WIP (tracks paralelos) | 22% | 471 | 103 | 1 | 272 | 90 | 5 | 0 | – |
| `toggle` | Controles · marcas | 22% | 28 | 6 | 17 | 1 | 3 | 1 | 119 | y |
| `date-range-field` | Tiempo (chronos · fecha/hora) | 25% | 9 | 2 | 0 | 4 | 2 | 1 | 3 | – |
| `time-range-field` | Tiempo (chronos · fecha/hora) | 25% | 9 | 2 | 0 | 4 | 2 | 1 | 3 | – |
| `surface` | Superficies · overlays · estado | 25% | 4 | 1 | 3 | 0 | 0 | 0 | 25 | – |
| `callout` | Superficies · overlays · estado | 27% | 15 | 4 | 3 | 7 | 1 | 0 | 28 | – |
| `meter` | Superficies · overlays · estado | 29% | 14 | 4 | 6 | 2 | 2 | 0 | 28 | y |
| `knob` | Controles · marcas | 29% | 27 | 7 | 4 | 9 | 4 | 3 | 0 | y |
| `onion-menu` | Menús · listas · navegación | 29% | 27 | 7 | 0 | 8 | 9 | 3 | 12 | – |
| `empty-state` | Superficies · overlays · estado | 29% | 17 | 5 | 7 | 5 | 0 | 0 | 6 | y |
| `search-field` | Campos | 31% | 14 | 4 | 5 | 0 | 4 | 1 | 17 | y |
| `password-field` | Campos | 31% | 36 | 11 | 5 | 9 | 10 | 1 | 23 | y |
| `aura` | Superficies · overlays · estado | 31% | 35 | 11 | 4 | 14 | 6 | 0 | 24 | y |
| `toggle-group` | Controles · marcas | 33% | 3 | 1 | 0 | 1 | 1 | 0 | 1 | – |
| `background` | Superficies · overlays · estado | 35% | 26 | 8 | 6 | 6 | 3 | 3 | 31 | – |
| `avatar` | Controles · marcas | 36% | 36 | 13 | 17 | 0 | 6 | 0 | 0 | y |
| `audio-player` | Media · chat | 37% | 19 | 7 | 0 | 8 | 4 | 0 | 5 | – |
| `tooltip` | Superficies · overlays · estado | 38% | 22 | 8 | 9 | 3 | 1 | 1 | 20 | y |
| `button-group` | Controles · marcas | 40% | 5 | 2 | 0 | 0 | 3 | 0 | 1 | – |
| `badge` | Controles · marcas | 41% | 22 | 9 | 10 | 1 | 2 | 0 | 65 | y |
| `popover` | Superficies · overlays · estado | 42% | 51 | 20 | 6 | 22 | 0 | 3 | 36 | y |
| `qr-code` | Controles · marcas | 43% | 7 | 3 | 0 | 0 | 4 | 0 | 10 | – |
| `navigation-menu` | Menús · listas · navegación | 44% | 38 | 15 | 1 | 13 | 5 | 4 | 26 | y |
| `waveform` | Media · chat | 45% | 11 | 5 | 1 | 1 | 4 | 0 | 10 | y |
| `color-picker` | Campos | 46% | 82 | 36 | 10 | 22 | 11 | 3 | 54 | y |
| `checkbox` | Controles · marcas | 48% | 34 | 16 | 12 | 2 | 3 | 1 | 45 | y |
| `progress` | Superficies · overlays · estado | 49% | 35 | 17 | 9 | 3 | 6 | 0 | 41 | y |
| `tag-group` | Controles · marcas | 49% | 46 | 21 | 21 | 0 | 1 | 3 | 91 | y |
| `color-swatch` | Controles · marcas | 50% | 10 | 5 | 2 | 3 | 0 | 0 | 12 | y |
| `text-scramble` | Texto · tipografía | 50% | 4 | 2 | 0 | 0 | 2 | 0 | 0 | – |
| `split-button` | Controles · marcas | 50% | 2 | 1 | 0 | 1 | 0 | 0 | 1 | y |
| `timeline` | Superficies · overlays · estado | 51% | 49 | 25 | 21 | 0 | 3 | 0 | 45 | y |
| `field` | Campos | 51% | 73 | 35 | 15 | 7 | 11 | 5 | 60 | y |
| `context-menu` | Menús · listas · navegación | 53% | 33 | 16 | 0 | 11 | 3 | 3 | 18 | – |
| `rating-group` | Controles · marcas | 53% | 16 | 8 | 4 | 1 | 2 | 1 | 25 | y |
| `metrics` | Controles · marcas | 54% | 50 | 27 | 10 | 9 | 4 | 0 | 68 | y |
| `sidebar` | Menús · listas · navegación | 55% | 45 | 23 | 0 | 14 | 5 | 3 | 50 | – |
| `drawer` | Superficies · overlays · estado | 55% | 62 | 33 | 9 | 13 | 5 | 2 | 44 | y |
| `color-field` | Campos | 56% | 16 | 9 | 0 | 2 | 5 | 0 | 22 | y |
| `image` | Controles · marcas | 57% | 35 | 20 | 2 | 1 | 12 | 0 | 22 | y |
| `dropdown-menu` | Menús · listas · navegación | 57% | 30 | 16 | 0 | 9 | 3 | 2 | 19 | – |
| `switch` | Controles · marcas | 57% | 22 | 12 | 8 | 1 | 0 | 1 | 52 | y |
| `s-text-virtual-list` | Menús · listas · navegación | 58% | 12 | 7 | 0 | 3 | 2 | 0 | 9 | – |
| `form` | Campos | 59% | 52 | 30 | 14 | 5 | 2 | 1 | 54 | y |
| `float-panel` | Superficies · overlays · estado | 59% | 53 | 29 | 3 | 12 | 5 | 4 | 28 | – |
| `slider` | Controles · marcas | 59% | 33 | 19 | 12 | 0 | 1 | 1 | 39 | y |
| `radio-cards` | Controles · marcas | 60% | 72 | 42 | 0 | 24 | 4 | 2 | 39 | y |
| `card` | Superficies · overlays · estado | 60% | 36 | 21 | 14 | 0 | 0 | 1 | 97 | y |
| `cropper` | Controles · marcas | 60% | 27 | 15 | 0 | 3 | 7 | 2 | 15 | – |
| `emoji-picker` | Campos | 61% | 32 | 17 | 0 | 8 | 3 | 4 | 17 | – |
| `pin-input` | Campos | 61% | 19 | 11 | 0 | 6 | 1 | 1 | 20 | y |
| `editable` | Campos | 63% | 44 | 25 | 12 | 1 | 2 | 4 | 56 | y |
| `text-focus` | Texto · tipografía | 63% | 8 | 5 | 0 | 1 | 2 | 0 | 0 | – |
| `spin-field` | Campos | 64% | 27 | 16 | 0 | 8 | 1 | 2 | 20 | – |
| `file-upload` | Campos | 64% | 61 | 38 | 16 | 1 | 4 | 2 | 98 | y |
| `tags-input` | Campos | 67% | 46 | 28 | 13 | 1 | 0 | 4 | 90 | y |
| `toolbar` | Menús · listas · navegación | 67% | 35 | 22 | 10 | 0 | 1 | 2 | 54 | y |
| `image-adjustments` | Controles · marcas | 67% | 18 | 10 | 0 | 4 | 1 | 3 | 12 | – |
| `text-gradient` | Texto · tipografía | 67% | 9 | 6 | 0 | 0 | 3 | 0 | 0 | – |
| `separator` | SIN FAMILIA | 67% | 6 | 4 | 0 | 0 | 2 | 0 | 3 | – |
| `select` | Campos | 69% | 72 | 45 | 14 | 3 | 3 | 7 | 101 | y |
| `dialog` | Superficies · overlays · estado | 70% | 33 | 23 | 5 | 4 | 1 | 0 | 37 | y |
| `image-picker` | Campos | 71% | 20 | 12 | 0 | 1 | 4 | 3 | 13 | – |
| `pagination` | Menús · listas · navegación | 71% | 26 | 17 | 7 | 0 | 0 | 2 | 34 | y |
| `stepper` | Menús · listas · navegación | 71% | 60 | 40 | 15 | 0 | 1 | 4 | 69 | y |
| `skip-link` | Superficies · overlays · estado | 71% | 8 | 5 | 0 | 0 | 2 | 1 | 8 | – |
| `date-field` | Tiempo (chronos · fecha/hora) | 73% | 11 | 8 | 3 | 0 | 0 | 0 | 21 | y |
| `time-field` | Tiempo (chronos · fecha/hora) | 73% | 11 | 8 | 3 | 0 | 0 | 0 | 21 | y |
| `calendar` | Tiempo (chronos · fecha/hora) | 75% | 71 | 51 | 13 | 0 | 4 | 3 | 76 | y |
| `accordion` | Superficies · overlays · estado | 77% | 65 | 49 | 10 | 0 | 5 | 1 | 76 | y |
| `radio-group` | Controles · marcas | 77% | 75 | 56 | 10 | 7 | 0 | 2 | 61 | y |
| `chat-log` | Media · chat | 77% | 42 | 30 | 0 | 4 | 5 | 3 | 31 | – |
| `chat-composer` | Media · chat | 79% | 44 | 31 | 0 | 3 | 5 | 5 | 31 | – |
| `chat-message` | Media · chat | 82% | 84 | 63 | 2 | 6 | 6 | 7 | 75 | y |
| `fab` | Controles · marcas | 82% | 17 | 14 | 0 | 2 | 1 | 0 | 10 | – |
| `toast` | Superficies · overlays · estado | 83% | 42 | 35 | 6 | 0 | 1 | 0 | 85 | – |
| `text-circular` | Texto · tipografía | 83% | 6 | 5 | 0 | 1 | 0 | 0 | 0 | – |
| `tabs` | Menús · listas · navegación | 88% | 76 | 66 | 6 | 2 | 1 | 1 | 79 | y |
| `chat-typing` | Media · chat | 89% | 9 | 8 | 0 | 1 | 0 | 0 | 7 | – |
| `breadcrumb` | Menús · listas · navegación | 90% | 23 | 18 | 2 | 0 | 0 | 3 | 28 | y |
| `collapsible` | Superficies · overlays · estado | 90% | 12 | 9 | 0 | 1 | 0 | 2 | 11 | – |
| `scroll-area` | Superficies · overlays · estado | 94% | 17 | 15 | 0 | 0 | 1 | 1 | 13 | – |
| `splitter` | Superficies · overlays · estado | 100% | 19 | 18 | 0 | 0 | 0 | 1 | 16 | – |
| `chat-list` | Media · chat | 100% | 18 | 18 | 0 | 0 | 0 | 0 | 18 | – |
| `box` | Layout (primitivos) | 100% | 9 | 9 | 0 | 0 | 0 | 0 | 43 | – |
| `barcode` | Controles · marcas | 100% | 5 | 5 | 0 | 0 | 0 | 0 | 7 | – |
| `flex` | Layout (primitivos) | 100% | 2 | 2 | 0 | 0 | 0 | 0 | 7 | – |
| `grid` | Layout (primitivos) | 100% | 2 | 2 | 0 | 0 | 0 | 0 | 13 | – |
| `alert-dialog` | Superficies · overlays · estado | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| `auto-grid` | Layout (primitivos) | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| `container` | Layout (primitivos) | — | 0 | 0 | 0 | 0 | 0 | 0 | 1 | – |
| `css-field` | Campos | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| `float` | Superficies · overlays · estado | — | 0 | 0 | 0 | 0 | 0 | 0 | 1 | – |
| `group` | Layout (primitivos) | — | 0 | 0 | 0 | 0 | 0 | 0 | 1 | – |
| `highlight` | Controles · marcas | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| `icon` | Controles · marcas | — | 0 | 0 | 0 | 0 | 0 | 0 | 2 | – |
| `mask-field` | Campos | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| `number-field` | Campos | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| `stack` | Layout (primitivos) | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| `wrap` | Layout (primitivos) | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |