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

326 lines
26 KiB

docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear. Ahora `theming-census.ts --report` escribe la auditoría entera bajo `docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162 recetas con CSS + 8 componentes sin receta, el árbol completo de `eidos/components/`. Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector (agrupados por clase), sistema transversal aparte, los privados con la columna que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado, un token prestado de otra receta, un shorthand o un eje físico. Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el componente que componen (icon-button → button), la capa que consumen (affix → viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button). Defectos del instrumento corregidos en el mismo pase, todos encontrados mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía como estado y producía `hover-disabled-*`; un knob que lee un privado se proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se reportaba como préstamo del componente `icon` (ahora el préstamo se verifica contra las claves del dueño en `base.ts`); y el default de talla salía sin nombrar en vez de `-md`. Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 · 1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0 diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs · prettier limpio (el árbol generado entra en .prettierignore junto a eidos/generated). Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco correcciones — dos de ellas evitaban romper el default), y 5 que no se tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado declarados en su propio CSS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
# 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`](../../process/PLAN-theming.md).
> La auditoría del SISTEMA de theming (fase 1, cerrada 2026-07-07) es
> [`theming-audit.md`](../theming-audit.md); ésta es la deuda de adopción que
> aquélla dejó apuntada en su §5.3-3.
- **Medido**: 2026-08-25 · **162 recetas** con CSS + **8 componentes sin receta** = 170 fichas, el árbol entero de `eidos/components/`
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear. Ahora `theming-census.ts --report` escribe la auditoría entera bajo `docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162 recetas con CSS + 8 componentes sin receta, el árbol completo de `eidos/components/`. Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector (agrupados por clase), sistema transversal aparte, los privados con la columna que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado, un token prestado de otra receta, un shorthand o un eje físico. Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el componente que componen (icon-button → button), la capa que consumen (affix → viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button). Defectos del instrumento corregidos en el mismo pase, todos encontrados mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía como estado y producía `hover-disabled-*`; un knob que lee un privado se proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se reportaba como préstamo del componente `icon` (ahora el préstamo se verifica contra las claves del dueño en `base.ts`); y el default de talla salía sin nombrar en vez de `-md`. Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 · 1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0 diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs · prettier limpio (el árbol generado entra en .prettierignore junto a eidos/generated). Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco correcciones — dos de ellas evitaban romper el default), y 5 que no se tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado declarados en su propio CSS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- **La pregunta**: ¿cuánto de la apariencia de cada componente puede cambiar un tema **sin tocar el sistema ni la receta**?
- **Alcance global**: **73%** — 3074 de 4228 knobs pasan por un token público del componente
- **Reparto**: público 3074 · privado 66 · global 763 · literal 325 · sistema transversal 509 · excepción firmada 273 · estructural 9 · puente de paleta 132 · canal de valor 36 _(los cinco últimos, fuera del ratio)_
- **Sin token público propio**: 16 · **alcance < 20 %**: 6 · **alcance 100 %**: 65 · **con eje `size`**: 55 · **estructurales**: 5
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear. Ahora `theming-census.ts --report` escribe la auditoría entera bajo `docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162 recetas con CSS + 8 componentes sin receta, el árbol completo de `eidos/components/`. Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector (agrupados por clase), sistema transversal aparte, los privados con la columna que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado, un token prestado de otra receta, un shorthand o un eje físico. Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el componente que componen (icon-button → button), la capa que consumen (affix → viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button). Defectos del instrumento corregidos en el mismo pase, todos encontrados mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía como estado y producía `hover-disabled-*`; un knob que lee un privado se proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se reportaba como préstamo del componente `icon` (ahora el préstamo se verifica contra las claves del dueño en `base.ts`); y el default de talla salía sin nombrar en vez de `-md`. Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 · 1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0 diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs · prettier limpio (el árbol generado entra en .prettierignore junto a eidos/generated). Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco correcciones — dos de ellas evitaban romper el default), y 5 que no se tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado declarados en su propio CSS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
## 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 |
tooling(theming): el censo aprende a leer la válvula del canon — clase `exception` Global 58 % → **59 %** sin tocar una línea de CSS: 54 knobs que eran deuda resultan ser desviaciones que el canon ya había firmado. `recipe-contract` §3 dice que una desviación anotada `/* literal: <razón> */` EN SU PROPIA DECLARACIÓN es válida, y `component-audit` la honra desde siempre (`declarationWindow` + `/literal:/i` en R-2.1, R-2.6 y R-2.7). El censo era el único instrumento que no: su `strip()` blanquea los comentarios ANTES de clasificar, así que las **81 anotaciones del catálogo** contaban como literales sin excusa. Dos instrumentos, un canon, dos respuestas — y la que mandaba era la del que no sabía leerlo. Ahora un literal anotado cae en la clase **`exception`, fuera del ratio** como `system`, y por la misma razón: no es que un tema no lo alcance, es que el canon ya aceptó por escrito que ahí no va un token. **54 knobs en 12 componentes**: `prose` 21 (su escala editorial em-relativa, que su README documenta como decisión — «No 5× duplicated scale»), `proof-of-human` 16 (la escena de tono fijo), `palabras` 3, `timeline` 2 (73 % → **77 %**), `result` 1 (86 % → **90 %**), y siete más con una cada uno. Detalles que costaron medición: - **La anotación vale para la DECLARACIÓN entera**, no sólo para su primera línea: un valor partido en varias líneas lleva la nota al final. Se comprueban todas las líneas que abarca la declaración. - **`strip()` blanquea en el sitio** (sustituye por espacios, conserva los saltos), así que el cuerpo despojado y el crudo quedan alineados línea a línea y basta con guardar el crudo al lado. - Muta-prueba: retirar una anotación de `prose` mueve el knob de `exception` a `literal` (21 → 20) y el conteo vuelve al restaurarla. Efecto colateral bueno: cada ficha gana una **§1.4 «Excepciones firmadas»** que las LISTA con su razón. Estaban escritas en el CSS y no se leían desde ninguna parte; ahora la ficha del componente las enseña, que es donde se decide si una sigue siendo cierta. Las 163 fichas cambian porque la plantilla cambia (sección nueva + la línea de reparto); el README de la auditoría gana la fila de la clase y la nota de que `system` y `exception` quedan fuera del denominador. Guards: `--names` 4.105 claves, DESVIADAS 0 · component-audit PASS · docs:check 0 · `check` con 72 errores globales y 0 en los ficheros tocados. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
| `exception` | un literal con su anotación `/* literal: <razón> */` en la propia declaración | no hace falta: es la válvula de recipe-contract §3, una desviación ya firmada — fuera del ratio |
| `structural` | **todos** los knobs de un componente cuyo 0 % es su NATURALEZA y no su deuda: no hay contrato que escribir (lista firmada abajo) | no hace falta: no hay superficie que un tema pueda nombrar — fuera del ratio, y su fila lee `strct`, no `0 %` |
| `bridge` | `var(--_{c}-palette-{slot})`: el privado que **escribe el forward** de la cascada de paleta (THM-2) | sí, **por la paleta** — y también por los tonos públicos del componente; acuñar encima mataría el `color=` por instancia — fuera del ratio |
| `channel` | `var(--_{c}-x, fallback)` que **nadie declara**: lo escribe soma o el envoltorio por INSTANCIA (un %, un rect medido, la talla de una prop) | **no debe**: fijarlo desde un tema rompe el comportamiento — fuera del ratio |
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear. Ahora `theming-census.ts --report` escribe la auditoría entera bajo `docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162 recetas con CSS + 8 componentes sin receta, el árbol completo de `eidos/components/`. Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector (agrupados por clase), sistema transversal aparte, los privados con la columna que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado, un token prestado de otra receta, un shorthand o un eje físico. Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el componente que componen (icon-button → button), la capa que consumen (affix → viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button). Defectos del instrumento corregidos en el mismo pase, todos encontrados mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía como estado y producía `hover-disabled-*`; un knob que lee un privado se proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se reportaba como préstamo del componente `icon` (ahora el préstamo se verifica contra las claves del dueño en `base.ts`); y el default de talla salía sin nombrar en vez de `-md`. Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 · 1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0 diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs · prettier limpio (el árbol generado entra en .prettierignore junto a eidos/generated). Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco correcciones — dos de ellas evitaban romper el default), y 5 que no se tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado declarados en su propio CSS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
**Alcance** = `public / (public + private + global + literal)`. `system`,
`exception`, `structural`, `bridge` y `channel` quedan fuera del denominador: el
primero es tematizable a nivel de sistema por diseño, el segundo es una
desviación que el canon ya aceptó por escrito, el tercero no tiene nada que un
tema pueda nombrar, y los dos últimos son las dos formas de privado que la
doctrina de este eje ya adjudicó como NO deuda — el mecanismo de la paleta y el
valor de runtime. Lo que queda en `private` es el **residuo**: se adjudica clave
por clave (`theming-census.ts --residue`).
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear. Ahora `theming-census.ts --report` escribe la auditoría entera bajo `docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162 recetas con CSS + 8 componentes sin receta, el árbol completo de `eidos/components/`. Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector (agrupados por clase), sistema transversal aparte, los privados con la columna que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado, un token prestado de otra receta, un shorthand o un eje físico. Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el componente que componen (icon-button → button), la capa que consumen (affix → viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button). Defectos del instrumento corregidos en el mismo pase, todos encontrados mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía como estado y producía `hover-disabled-*`; un knob que lee un privado se proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se reportaba como préstamo del componente `icon` (ahora el préstamo se verifica contra las claves del dueño en `base.ts`); y el default de talla salía sin nombrar en vez de `-md`. Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 · 1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0 diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs · prettier limpio (el árbol generado entra en .prettierignore junto a eidos/generated). Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco correcciones — dos de ellas evitaban romper el default), y 5 que no se tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado declarados en su propio CSS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
**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.
## Estructurales (5) — 0 % por naturaleza, no por deuda
Medidos uno a uno el 2026-08-22, con **veredicto §5 escrito** en su ficha:
ninguno tiene contrato que escribir. Mientras el censo los contaba como a un
componente con deuda real, la cifra global mentía por abajo y el gate de F3
(«censo 100 %») era inalcanzable **por construcción** (next-features §13). Sus
knobs se siguen contando y listando —un knob estructural sigue siendo un knob—;
sólo salen del **denominador**, igual que `system`. La lista firmada vive en
`scripts/theming-census.ts` (`STRUCTURAL_COMPONENTS`), con su razón al lado.
- [`aspect-ratio`](./aspect-ratio.md) (3): es una FACETA de `box`, no un componente: el eje lo posee `box` y su knob viene prestado (⤴); `--aspect-ratio` es canal de valor por instancia
- [`cascade`](./cascade.md) (1): su knob es el `opacity` del gate antiparpadeo: mecánica del canal de motion, cuyo valor tematizable vive en `EidosConfig.motion`
- [`date-picker`](./date-picker.md) (2): sus dos knobs son la corrección `max-content` del pie del popover — un único valor correcto; su cromo vive en `field`, `calendar` y `picker-shell`
- [`motion`](./motion.md) (1): igual que `cascade`: el `opacity` del gate `[data-animation-pending]` es mecánica del canal, tematizable desde `EidosConfig.motion`
- [`text-blur`](./text-blur.md) (2): sus dos knobs son el `1px` de la técnica sr-only: receta de accesibilidad idéntica en todo el catálogo, no estética
## Puente de paleta (132 knobs) y canal de valor (36) — privados que NO son deuda
Las dos formas de privado que la doctrina de este eje ya adjudicó, y que ahora
se distinguen **mecánicamente** — medidas contra el emisor, no adivinadas por el
nombre. La fuente es la SALIDA del generador (`src/uix/eidos/generated/base.css`,
que `generated-css.test.ts` mantiene idéntica a `renderStaticCss()`):
- **puente** — el nombre aparece en la CSS generada como la línea del forward
`--_{c}-palette-{slot}: var(--palette-{slot}, …)`. Un nombre con forma de
puente que nadie reenvía **no cuenta**.
- **canal** — el privado no lo declara nadie: ni la CSS del componente ni el
generador. Lo escribe soma o el envoltorio por instancia.
**Puente** (30): [`tag-group`](./tag-group.md) 14 · [`stepper`](./stepper.md) 11 · [`radio-group`](./radio-group.md) 10 · [`checkbox`](./checkbox.md) 8 · [`file-upload`](./file-upload.md) 8 · [`calendar`](./calendar.md) 7 · [`editable`](./editable.md) 7 · [`month-grid`](./month-grid.md) 7 · [`range-calendar`](./range-calendar.md) 7 · [`year-grid`](./year-grid.md) 7 · [`select`](./select.md) 5 · [`card`](./card.md) 4 · [`combobox`](./combobox.md) 4 · [`tags-input`](./tags-input.md) 4 · [`callout`](./callout.md) 3 · [`listbox`](./listbox.md) 3 · [`table`](./table.md) 3 · [`badge`](./badge.md) 2 · [`carousel`](./carousel.md) 2 · [`clipboard`](./clipboard.md) 2 · [`grid-list`](./grid-list.md) 2 · [`link`](./link.md) 2 · [`mark`](./mark.md) 2 · [`switch`](./switch.md) 2 · [`color-picker`](./color-picker.md) 1 · [`drag-drop`](./drag-drop.md) 1 · [`feed`](./feed.md) 1 · [`field`](./field.md) 1 · [`tree-grid`](./tree-grid.md) 1 · [`tree-view`](./tree-view.md) 1
**Canal** (14): [`chronos`](./chronos.md) 7 · [`drawer`](./drawer.md) 6 · [`dialog`](./dialog.md) 4 · [`popover`](./popover.md) 3 · [`tooltip`](./tooltip.md) 3 · [`code-block`](./code-block.md) 2 · [`color-picker`](./color-picker.md) 2 · [`link`](./link.md) 2 · [`progress`](./progress.md) 2 · [`background`](./background.md) 1 · [`file-upload`](./file-upload.md) 1 · [`knob`](./knob.md) 1 · [`meter`](./meter.md) 1 · [`scroll-frames`](./scroll-frames.md) 1
Lo que NO cae en ninguna de las dos es el **residuo** (66 knobs en 10 componentes):
conmutadores por variante, privados que leen un global en una rama, escaleras
por talla sin público detrás. Se adjudican clave a clave — `theming-census.ts
--residue` los vuelca con las fuentes de cada declaración.
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear. Ahora `theming-census.ts --report` escribe la auditoría entera bajo `docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162 recetas con CSS + 8 componentes sin receta, el árbol completo de `eidos/components/`. Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector (agrupados por clase), sistema transversal aparte, los privados con la columna que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado, un token prestado de otra receta, un shorthand o un eje físico. Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el componente que componen (icon-button → button), la capa que consumen (affix → viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button). Defectos del instrumento corregidos en el mismo pase, todos encontrados mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía como estado y producía `hover-disabled-*`; un knob que lee un privado se proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se reportaba como préstamo del componente `icon` (ahora el préstamo se verifica contra las claves del dueño en `base.ts`); y el default de talla salía sin nombrar en vez de `-md`. Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 · 1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0 diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs · prettier limpio (el árbol generado entra en .prettierignore junto a eidos/generated). Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco correcciones — dos de ellas evitaban romper el default), y 5 que no se tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado declarados en su propio CSS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
## 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 de `eidos/components`, «Capas compartidas» regla 2);
- **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 `calendar` en los
pickers): la corrección es la decisión de familia registrada en
[`theming-audit.md`](../theming-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`](./affix.md) · [`count-up`](./count-up.md) · [`format-date`](./format-date.md) · [`format-number`](./format-number.md) · [`icon-button`](./icon-button.md) · [`relative-time`](./relative-time.md) · [`svg`](./svg.md) · [`trans`](./trans.md)
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 |
| ---: | --- | ---: | ---: | ---: | ---: |
tooling(theming): el censo aprende a leer la válvula del canon — clase `exception` Global 58 % → **59 %** sin tocar una línea de CSS: 54 knobs que eran deuda resultan ser desviaciones que el canon ya había firmado. `recipe-contract` §3 dice que una desviación anotada `/* literal: <razón> */` EN SU PROPIA DECLARACIÓN es válida, y `component-audit` la honra desde siempre (`declarationWindow` + `/literal:/i` en R-2.1, R-2.6 y R-2.7). El censo era el único instrumento que no: su `strip()` blanquea los comentarios ANTES de clasificar, así que las **81 anotaciones del catálogo** contaban como literales sin excusa. Dos instrumentos, un canon, dos respuestas — y la que mandaba era la del que no sabía leerlo. Ahora un literal anotado cae en la clase **`exception`, fuera del ratio** como `system`, y por la misma razón: no es que un tema no lo alcance, es que el canon ya aceptó por escrito que ahí no va un token. **54 knobs en 12 componentes**: `prose` 21 (su escala editorial em-relativa, que su README documenta como decisión — «No 5× duplicated scale»), `proof-of-human` 16 (la escena de tono fijo), `palabras` 3, `timeline` 2 (73 % → **77 %**), `result` 1 (86 % → **90 %**), y siete más con una cada uno. Detalles que costaron medición: - **La anotación vale para la DECLARACIÓN entera**, no sólo para su primera línea: un valor partido en varias líneas lleva la nota al final. Se comprueban todas las líneas que abarca la declaración. - **`strip()` blanquea en el sitio** (sustituye por espacios, conserva los saltos), así que el cuerpo despojado y el crudo quedan alineados línea a línea y basta con guardar el crudo al lado. - Muta-prueba: retirar una anotación de `prose` mueve el knob de `exception` a `literal` (21 → 20) y el conteo vuelve al restaurarla. Efecto colateral bueno: cada ficha gana una **§1.4 «Excepciones firmadas»** que las LISTA con su razón. Estaban escritas en el CSS y no se leían desde ninguna parte; ahora la ficha del componente las enseña, que es donde se decide si una sigue siendo cierta. Las 163 fichas cambian porque la plantilla cambia (sección nueva + la línea de reparto); el README de la auditoría gana la fila de la clase y la nota de que `system` y `exception` quedan fuera del denominador. Guards: `--names` 4.105 claves, DESVIADAS 0 · component-audit PASS · docs:check 0 · `check` con 72 errores globales y 0 en los ficheros tocados. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
| 1 | [palabras](./palabras.md) | 360 | 22% | 468 | 0 |
| 2 | [chronos](./chronos.md) | 223 | 1% | 240 | 9 |
tooling(theming): el censo aprende a leer la válvula del canon — clase `exception` Global 58 % → **59 %** sin tocar una línea de CSS: 54 knobs que eran deuda resultan ser desviaciones que el canon ya había firmado. `recipe-contract` §3 dice que una desviación anotada `/* literal: <razón> */` EN SU PROPIA DECLARACIÓN es válida, y `component-audit` la honra desde siempre (`declarationWindow` + `/literal:/i` en R-2.1, R-2.6 y R-2.7). El censo era el único instrumento que no: su `strip()` blanquea los comentarios ANTES de clasificar, así que las **81 anotaciones del catálogo** contaban como literales sin excusa. Dos instrumentos, un canon, dos respuestas — y la que mandaba era la del que no sabía leerlo. Ahora un literal anotado cae en la clase **`exception`, fuera del ratio** como `system`, y por la misma razón: no es que un tema no lo alcance, es que el canon ya aceptó por escrito que ahí no va un token. **54 knobs en 12 componentes**: `prose` 21 (su escala editorial em-relativa, que su README documenta como decisión — «No 5× duplicated scale»), `proof-of-human` 16 (la escena de tono fijo), `palabras` 3, `timeline` 2 (73 % → **77 %**), `result` 1 (86 % → **90 %**), y siete más con una cada uno. Detalles que costaron medición: - **La anotación vale para la DECLARACIÓN entera**, no sólo para su primera línea: un valor partido en varias líneas lleva la nota al final. Se comprueban todas las líneas que abarca la declaración. - **`strip()` blanquea en el sitio** (sustituye por espacios, conserva los saltos), así que el cuerpo despojado y el crudo quedan alineados línea a línea y basta con guardar el crudo al lado. - Muta-prueba: retirar una anotación de `prose` mueve el knob de `exception` a `literal` (21 → 20) y el conteo vuelve al restaurarla. Efecto colateral bueno: cada ficha gana una **§1.4 «Excepciones firmadas»** que las LISTA con su razón. Estaban escritas en el CSS y no se leían desde ninguna parte; ahora la ficha del componente las enseña, que es donde se decide si una sigue siendo cierta. Las 163 fichas cambian porque la plantilla cambia (sección nueva + la línea de reparto); el README de la auditoría gana la fila de la clase y la nota de que `system` y `exception` quedan fuera del denominador. Guards: `--names` 4.105 claves, DESVIADAS 0 · component-audit PASS · docs:check 0 · `check` con 72 errores globales y 0 en los ficheros tocados. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
| 3 | [time-range-picker](./time-range-picker.md) | 67 | 20% | 88 | 22 |
| 4 | [time-picker](./time-picker.md) | 47 | 18% | 59 | 16 |
| 5 | [proof-of-human](./proof-of-human.md) | 44 | 24% | 62 | 20 |
uix(color-picker): temable — 51 % → 75 %, y un prefijo ABREVIADO fuera de todo el árbol 20 claves de costura (54 → 71) y cinco nombres renombrados. Global del eje 60 % → **61 %**. `--cp-*` VIOLABA theming §6 r5 («no abrevies el nombre del componente»). Cinco nombres —`checker`, `checker-cell`, `current-color`, `channel-gradient`, `trigger-color`— y **45 ocurrencias en 8 ficheros**, tres de ellos de SOMA: el provider los estampa inline, su test los lee y sus tipos los declaran. Renombrados a `--_color-picker-*` con `git ls-files` delante; dos de ellos —el damero y el gradiente de canal— ya derivaban de un público, así que el censo los reclasifica solo. Precedente: los `--gb-*` / `--_ntp-*` de F2-A. Cosido: el ritmo del componente, la tinta de la etiqueta, la tipografía del disparador y la del VALOR —que es metadato: mono, regular, tinta e interlineado propios—, el borde de la muestra, el raíl del canal y su pulgar, el hueco del anillo de la muestra y el borde del cuentagotas. UNA SOMBRA QUE SON DOS Y HACEN UNA COSA: el pulgar del canal lleva contorno + elevación en un solo `box-shadow`, así que se acuña como UN token y la regla de foco lo compone con su tercer anillo, en vez de repetir las dos sombras. EL INSTRUMENTO NO VEÍA DOS FAMILIAS DE NODO, y las dos se arreglaron midiendo: las muestras del picker **son `<ColorSwatch>`** (llevan `data-color-swatch`, no `data-color-picker-*`) y sus sliders de canal **son SLIDERS** (el pulgar y el raíl llevan `data-slider-*`). Cuatro tokens vivos leían muertos por eso. LO ROJO QUE QUEDA ES ANTERIOR A ESTE COMMIT: el guard reporta 23 sin adjudicar —las muestras, la rueda de tono (`hue-0..360` + `saturation-floor`), el ancho del panel por talla, el cuentagotas y las dos de transición—, todos del bloque que ya existía. Antes eran **24**, así que la cifra baja. Su pase de medición queda anotado en §13 con dos causas ya descartadas: el panel SÍ abre y sobrevive al blur, al parqueo del ratón y al forzado de `data-size`, y el guard ya ve sus nodos. Lo que este commit añadió está medido: dos adjudicadas montando el disparador de muestra dentro del panel abierto, el resto en vivo. Gates: sonda antes/después **0 diffs en 1.350 valores** (8 estados), medida DOS veces —una tras el renombrado, otra tras la costura— · tests de soma del color-picker **7/7** tras el renombrado · censo --only 75 % · `--names` sin desviadas · component-audit PASS · eidos-lint 0 invalid · suite eidos con el único rojo conocido · rtl:check 0 · docs:check 0 · `check` con 72 errores globales y 0 en los ficheros tocados. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
| 6 | [field-langs](./field-langs.md) | 37 | 0% | 39 | 0 |
| 7 | [date-range-picker](./date-range-picker.md) | 30 | 40% | 53 | 22 |
| 8 | [radio-cards](./radio-cards.md) | 27 | 61% | 72 | 40 |
| 9 | [drawer](./drawer.md) | 21 | 67% | 72 | 54 |
| 10 | [natural-time-picker](./natural-time-picker.md) | 20 | 74% | 77 | 62 |
| 11 | [sidebar](./sidebar.md) | 19 | 55% | 45 | 50 |
| 12 | [mockup](./mockup.md) | 18 | 38% | 29 | 9 |
| 13 | [color-picker](./color-picker.md) | 16 | 79% | 81 | 71 |
| 14 | [password-field](./password-field.md) | 13 | 62% | 35 | 30 |
| 15 | [popover](./popover.md) | 12 | 73% | 50 | 46 |
| 16 | [chart](./chart.md) | 10 | 89% | 95 | 45 |
| 17 | [combobox](./combobox.md) | 9 | 89% | 97 | 106 |
| 18 | [prose](./prose.md) | 9 | 83% | 53 | 37 |
| 19 | [audio-player](./audio-player.md) | 8 | 56% | 18 | 8 |
| 20 | [spin-field](./spin-field.md) | 8 | 67% | 26 | 20 |
## Sin ningún token público propio (16)
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear. Ahora `theming-census.ts --report` escribe la auditoría entera bajo `docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162 recetas con CSS + 8 componentes sin receta, el árbol completo de `eidos/components/`. Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector (agrupados por clase), sistema transversal aparte, los privados con la columna que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado, un token prestado de otra receta, un shorthand o un eje físico. Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el componente que componen (icon-button → button), la capa que consumen (affix → viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button). Defectos del instrumento corregidos en el mismo pase, todos encontrados mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía como estado y producía `hover-disabled-*`; un knob que lee un privado se proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se reportaba como préstamo del componente `icon` (ahora el préstamo se verifica contra las claves del dueño en `base.ts`); y el default de talla salía sin nombrar en vez de `-md`. Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 · 1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0 diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs · prettier limpio (el árbol generado entra en .prettierignore junto a eidos/generated). Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco correcciones — dos de ellas evitaban romper el default), y 5 que no se tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado declarados en su propio CSS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
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`](./alert-dialog.md) · [`auto-grid`](./auto-grid.md) · [`css-field`](./css-field.md) · [`display`](./display.md) · [`field-langs`](./field-langs.md) · [`heading`](./heading.md) · [`highlight`](./highlight.md) · [`mask-field`](./mask-field.md) · [`month-grid`](./month-grid.md) · [`number-field`](./number-field.md) · [`palabras`](./palabras.md) · [`range-calendar`](./range-calendar.md) · [`stack`](./stack.md) · [`text`](./text.md) · [`wrap`](./wrap.md) · [`year-grid`](./year-grid.md)
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear. Ahora `theming-census.ts --report` escribe la auditoría entera bajo `docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162 recetas con CSS + 8 componentes sin receta, el árbol completo de `eidos/components/`. Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector (agrupados por clase), sistema transversal aparte, los privados con la columna que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado, un token prestado de otra receta, un shorthand o un eje físico. Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el componente que componen (icon-button → button), la capa que consumen (affix → viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button). Defectos del instrumento corregidos en el mismo pase, todos encontrados mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía como estado y producía `hover-disabled-*`; un knob que lee un privado se proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se reportaba como préstamo del componente `icon` (ahora el préstamo se verifica contra las claves del dueño en `base.ts`); y el default de talla salía sin nombrar en vez de `-md`. Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 · 1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0 diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs · prettier limpio (el árbol generado entra en .prettierignore junto a eidos/generated). Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco correcciones — dos de ellas evitaban romper el default), y 5 que no se tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado declarados en su propio CSS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
## 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 | estructural | puente | canal | contrato | size |
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | :-: |
| [range-calendar](./range-calendar.md) | 0% | 98 | 0 | 0 | 2 | 4 | 85 | 0 | 7 | 0 | 0 | – |
| [month-grid](./month-grid.md) | 0% | 61 | 0 | 0 | 0 | 5 | 49 | 0 | 7 | 0 | 0 | – |
| [year-grid](./year-grid.md) | 0% | 61 | 0 | 0 | 0 | 5 | 49 | 0 | 7 | 0 | 0 | – |
| [field-langs](./field-langs.md) | 0% | 39 | 0 | 0 | 31 | 6 | 2 | 0 | 0 | 0 | 0 | – |
| [chronos](./chronos.md) | 1% | 240 | 3 | 14 | 198 | 11 | 7 | 0 | 0 | 7 | 9 | y |
| [time-picker](./time-picker.md) | 18% | 59 | 10 | 12 | 24 | 11 | 2 | 0 | 0 | 0 | 16 | y |
| [time-range-picker](./time-range-picker.md) | 20% | 88 | 17 | 19 | 33 | 15 | 4 | 0 | 0 | 0 | 22 | y |
| [skin-media-player](./skin-media-player.md) | 22% | 11 | 2 | 0 | 1 | 6 | 2 | 0 | 0 | 0 | 10 | y |
| [palabras](./palabras.md) | 22% | 468 | 103 | 1 | 272 | 87 | 5 | 0 | 0 | 0 | 0 | – |
| [proof-of-human](./proof-of-human.md) | 24% | 62 | 14 | 0 | 29 | 15 | 4 | 0 | 0 | 0 | 20 | – |
| [scroll-frames](./scroll-frames.md) | 38% | 9 | 3 | 0 | 1 | 4 | 0 | 0 | 0 | 1 | 4 | – |
| [menu-dial](./menu-dial.md) | 38% | 8 | 3 | 0 | 1 | 4 | 0 | 0 | 0 | 0 | 3 | y |
| [mockup](./mockup.md) | 38% | 29 | 11 | 0 | 13 | 5 | 0 | 0 | 0 | 0 | 9 | – |
| [date-range-picker](./date-range-picker.md) | 40% | 53 | 20 | 9 | 19 | 2 | 3 | 0 | 0 | 0 | 22 | y |
| [text-scramble](./text-scramble.md) | 50% | 4 | 2 | 0 | 0 | 2 | 0 | 0 | 0 | 0 | 2 | – |
| [split-button](./split-button.md) | 50% | 2 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | y |
| [toggle-group](./toggle-group.md) | 50% | 2 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | – |
| [sidebar](./sidebar.md) | 55% | 45 | 23 | 0 | 14 | 5 | 3 | 0 | 0 | 0 | 50 | – |
| [audio-player](./audio-player.md) | 56% | 18 | 10 | 0 | 8 | 0 | 0 | 0 | 0 | 0 | 8 | – |
| [link](./link.md) | 57% | 14 | 4 | 0 | 1 | 2 | 3 | 0 | 2 | 2 | 5 | – |
| [radio-cards](./radio-cards.md) | 61% | 72 | 43 | 0 | 23 | 4 | 2 | 0 | 0 | 0 | 40 | y |
| [password-field](./password-field.md) | 62% | 35 | 21 | 2 | 2 | 9 | 1 | 0 | 0 | 0 | 30 | y |
| [spin-field](./spin-field.md) | 67% | 26 | 16 | 0 | 8 | 0 | 2 | 0 | 0 | 0 | 20 | – |
| [virtual-list](./virtual-list.md) | 67% | 13 | 8 | 0 | 0 | 4 | 1 | 0 | 0 | 0 | 12 | – |
| [drawer](./drawer.md) | 67% | 72 | 43 | 0 | 16 | 5 | 2 | 0 | 0 | 6 | 54 | y |
| [s-text-virtual-list](./s-text-virtual-list.md) | 70% | 10 | 7 | 0 | 3 | 0 | 0 | 0 | 0 | 0 | 9 | – |
| [popover](./popover.md) | 73% | 50 | 32 | 0 | 12 | 0 | 3 | 0 | 0 | 3 | 46 | y |
| [virtual-grid](./virtual-grid.md) | 73% | 12 | 8 | 0 | 0 | 3 | 1 | 0 | 0 | 0 | 12 | – |
| [natural-time-picker](./natural-time-picker.md) | 74% | 77 | 56 | 0 | 14 | 6 | 1 | 0 | 0 | 0 | 62 | y |
| [color-picker](./color-picker.md) | 79% | 81 | 59 | 3 | 2 | 11 | 3 | 0 | 1 | 2 | 71 | y |
| [aura](./aura.md) | 80% | 35 | 28 | 0 | 1 | 6 | 0 | 0 | 0 | 0 | 37 | y |
| [spinner](./spinner.md) | 80% | 20 | 16 | 0 | 0 | 4 | 0 | 0 | 0 | 0 | 18 | – |
| [knob](./knob.md) | 82% | 26 | 18 | 0 | 1 | 3 | 3 | 0 | 0 | 1 | 33 | – |
| [meter](./meter.md) | 82% | 12 | 9 | 0 | 2 | 0 | 0 | 0 | 0 | 1 | 28 | y |
| [carousel](./carousel.md) | 82% | 35 | 23 | 0 | 0 | 5 | 5 | 0 | 2 | 0 | 27 | – |
| [prose](./prose.md) | 83% | 53 | 44 | 0 | 0 | 9 | 0 | 0 | 0 | 0 | 37 | y |
| [clipboard](./clipboard.md) | 83% | 8 | 5 | 0 | 1 | 0 | 0 | 0 | 2 | 0 | 6 | – |
| [skeleton](./skeleton.md) | 83% | 6 | 5 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 9 | – |
| [onion-menu](./onion-menu.md) | 84% | 22 | 16 | 0 | 1 | 2 | 3 | 0 | 0 | 0 | 19 | – |
| [waveform](./waveform.md) | 86% | 7 | 6 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 11 | y |
| [context-menu](./context-menu.md) | 87% | 33 | 26 | 0 | 1 | 3 | 3 | 0 | 0 | 0 | 28 | – |
| [kbd](./kbd.md) | 87% | 15 | 13 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 13 | – |
| [textarea](./textarea.md) | 87% | 26 | 20 | 0 | 0 | 3 | 3 | 0 | 0 | 0 | 37 | – |
| [metrics](./metrics.md) | 87% | 47 | 41 | 2 | 0 | 4 | 0 | 0 | 0 | 0 | 72 | y |
| [announce](./announce.md) | 88% | 8 | 7 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 9 | – |
| [media-player](./media-player.md) | 88% | 50 | 44 | 0 | 0 | 6 | 0 | 0 | 0 | 0 | 61 | – |
| [code-block](./code-block.md) | 88% | 27 | 22 | 0 | 3 | 0 | 0 | 0 | 0 | 2 | 20 | – |
| [gradient-picker](./gradient-picker.md) | 88% | 27 | 23 | 0 | 2 | 1 | 1 | 0 | 0 | 0 | 25 | – |
| [menubar](./menubar.md) | 88% | 27 | 23 | 0 | 3 | 0 | 1 | 0 | 0 | 0 | 35 | y |
| [chart](./chart.md) | 89% | 95 | 81 | 0 | 0 | 10 | 4 | 0 | 0 | 0 | 45 | – |
| [float-panel](./float-panel.md) | 89% | 50 | 41 | 0 | 0 | 5 | 4 | 0 | 0 | 0 | 40 | – |
| [combobox](./combobox.md) | 89% | 97 | 76 | 0 | 3 | 6 | 8 | 0 | 4 | 0 | 106 | – |
| [card-group](./card-group.md) | 90% | 22 | 18 | 0 | 0 | 2 | 2 | 0 | 0 | 0 | 20 | – |
| [result](./result.md) | 90% | 21 | 19 | 0 | 0 | 2 | 0 | 0 | 0 | 0 | 27 | – |
| [grid-list](./grid-list.md) | 92% | 31 | 22 | 0 | 1 | 1 | 5 | 0 | 2 | 0 | 38 | – |
| [listbox](./listbox.md) | 92% | 31 | 23 | 0 | 1 | 1 | 3 | 0 | 3 | 0 | 21 | – |
| [accordion](./accordion.md) | 92% | 64 | 58 | 0 | 0 | 5 | 1 | 0 | 0 | 0 | 76 | y |
| [table](./table.md) | 93% | 46 | 37 | 0 | 0 | 3 | 3 | 0 | 3 | 0 | 45 | – |
| [gradient-builder](./gradient-builder.md) | 93% | 60 | 52 | 0 | 1 | 3 | 4 | 0 | 0 | 0 | 72 | – |
| [progress](./progress.md) | 93% | 30 | 26 | 0 | 2 | 0 | 0 | 0 | 0 | 2 | 43 | y |
| [color-field](./color-field.md) | 93% | 15 | 14 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 26 | y |
| [form](./form.md) | 93% | 47 | 43 | 3 | 0 | 0 | 1 | 0 | 0 | 0 | 64 | y |
| [feed](./feed.md) | 94% | 34 | 30 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 50 | – |
| [tooltip](./tooltip.md) | 94% | 20 | 15 | 1 | 0 | 0 | 1 | 0 | 0 | 3 | 23 | y |
| [cropper](./cropper.md) | 94% | 20 | 17 | 0 | 1 | 0 | 2 | 0 | 0 | 0 | 17 | – |
| [picker-shell](./picker-shell.md) | 95% | 21 | 20 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 31 | y |
| [nav-tree](./nav-tree.md) | 96% | 24 | 22 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 18 | – |
| [tree-view](./tree-view.md) | 96% | 28 | 24 | 0 | 0 | 1 | 2 | 0 | 1 | 0 | 46 | – |
| [dropdown-menu](./dropdown-menu.md) | 96% | 30 | 27 | 0 | 1 | 0 | 2 | 0 | 0 | 0 | 28 | – |
| [tag-group](./tag-group.md) | 97% | 46 | 28 | 0 | 0 | 1 | 3 | 0 | 14 | 0 | 91 | y |
| [timeline](./timeline.md) | 97% | 37 | 36 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 45 | y |
| [tabs](./tabs.md) | 97% | 77 | 74 | 0 | 2 | 0 | 1 | 0 | 0 | 0 | 79 | y |
| [tree-grid](./tree-grid.md) | 97% | 42 | 37 | 0 | 0 | 1 | 3 | 0 | 1 | 0 | 47 | – |
| [stepper](./stepper.md) | 98% | 60 | 44 | 0 | 0 | 1 | 4 | 0 | 11 | 0 | 69 | y |
| [radio-group](./radio-group.md) | 98% | 59 | 46 | 0 | 1 | 0 | 2 | 0 | 10 | 0 | 69 | – |
| [command](./command.md) | 98% | 52 | 49 | 0 | 0 | 1 | 2 | 0 | 0 | 0 | 57 | – |
| [chat-message](./chat-message.md) | 100% | 78 | 71 | 0 | 0 | 0 | 7 | 0 | 0 | 0 | 81 | y |
| [select](./select.md) | 100% | 69 | 57 | 0 | 0 | 0 | 7 | 0 | 5 | 0 | 103 | y |
| [calendar](./calendar.md) | 100% | 67 | 57 | 0 | 0 | 0 | 3 | 0 | 7 | 0 | 74 | – |
| [field](./field.md) | 100% | 64 | 58 | 0 | 0 | 0 | 5 | 0 | 1 | 0 | 68 | y |
| [file-upload](./file-upload.md) | 100% | 56 | 45 | 0 | 0 | 0 | 2 | 0 | 8 | 1 | 98 | y |
| [tags-input](./tags-input.md) | 100% | 46 | 38 | 0 | 0 | 0 | 4 | 0 | 4 | 0 | 91 | y |
| [toast](./toast.md) | 100% | 45 | 45 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 85 | – |
| [editable](./editable.md) | 100% | 42 | 31 | 0 | 0 | 0 | 4 | 0 | 7 | 0 | 56 | y |
| [chat-composer](./chat-composer.md) | 100% | 39 | 34 | 0 | 0 | 0 | 5 | 0 | 0 | 0 | 33 | – |
| [chat-log](./chat-log.md) | 100% | 37 | 34 | 0 | 0 | 0 | 3 | 0 | 0 | 0 | 35 | – |
| [navigation-menu](./navigation-menu.md) | 100% | 37 | 33 | 0 | 0 | 0 | 4 | 0 | 0 | 0 | 44 | – |
| [heading](./heading.md) | — | 36 | 0 | 0 | 0 | 0 | 36 | 0 | 0 | 0 | 0 | – |
| [dialog](./dialog.md) | 100% | 35 | 31 | 0 | 0 | 0 | 0 | 0 | 0 | 4 | 44 | y |
| [card](./card.md) | 100% | 33 | 28 | 0 | 0 | 0 | 1 | 0 | 4 | 0 | 98 | y |
| [slider](./slider.md) | 100% | 32 | 31 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 39 | y |
| [toolbar](./toolbar.md) | 100% | 32 | 30 | 0 | 0 | 0 | 2 | 0 | 0 | 0 | 54 | y |
| [checkbox](./checkbox.md) | 100% | 30 | 21 | 0 | 0 | 0 | 1 | 0 | 8 | 0 | 47 | y |
| [s-text](./s-text.md) | 100% | 30 | 2 | 0 | 0 | 0 | 28 | 0 | 0 | 0 | 3 | – |
| [emoji-picker](./emoji-picker.md) | 100% | 26 | 22 | 0 | 0 | 0 | 4 | 0 | 0 | 0 | 21 | – |
| [pagination](./pagination.md) | 100% | 26 | 24 | 0 | 0 | 0 | 2 | 0 | 0 | 0 | 34 | y |
| [toggle](./toggle.md) | 100% | 25 | 24 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 126 | y |
| [avatar](./avatar.md) | 100% | 24 | 24 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 86 | y |
| [text](./text.md) | — | 24 | 0 | 0 | 0 | 0 | 24 | 0 | 0 | 0 | 0 | – |
| [background](./background.md) | 100% | 23 | 19 | 0 | 0 | 0 | 3 | 0 | 0 | 1 | 37 | – |
| [breadcrumb](./breadcrumb.md) | 100% | 23 | 20 | 0 | 0 | 0 | 3 | 0 | 0 | 0 | 28 | y |
| [image](./image.md) | 100% | 22 | 22 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 23 | y |
| [button](./button.md) | 100% | 21 | 20 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 106 | y |
| [switch](./switch.md) | 100% | 20 | 17 | 0 | 0 | 0 | 1 | 0 | 2 | 0 | 52 | y |
| [chat-list](./chat-list.md) | 100% | 18 | 18 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 18 | – |
| [pin-input](./pin-input.md) | 100% | 18 | 17 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 22 | y |
| [splitter](./splitter.md) | 100% | 18 | 17 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 15 | – |
| [badge](./badge.md) | 100% | 17 | 15 | 0 | 0 | 0 | 0 | 0 | 2 | 0 | 71 | y |
| [empty-state](./empty-state.md) | 100% | 17 | 17 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 26 | y |
| [image-adjustments](./image-adjustments.md) | 100% | 17 | 14 | 0 | 0 | 0 | 3 | 0 | 0 | 0 | 16 | – |
| [link-preview](./link-preview.md) | 100% | 17 | 14 | 0 | 0 | 0 | 3 | 0 | 0 | 0 | 35 | – |
| [drag-drop](./drag-drop.md) | 100% | 16 | 8 | 0 | 0 | 0 | 7 | 0 | 1 | 0 | 9 | – |
| [fab](./fab.md) | 100% | 16 | 16 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 12 | – |
| [image-picker](./image-picker.md) | 100% | 16 | 13 | 0 | 0 | 0 | 3 | 0 | 0 | 0 | 14 | – |
| [scroll-area](./scroll-area.md) | 100% | 16 | 15 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 13 | – |
| [callout](./callout.md) | 100% | 14 | 11 | 0 | 0 | 0 | 0 | 0 | 3 | 0 | 34 | – |
| [rating-group](./rating-group.md) | 100% | 14 | 13 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 29 | – |
| [code](./code.md) | 100% | 13 | 7 | 0 | 0 | 0 | 6 | 0 | 0 | 0 | 6 | – |
| [anchor-nav](./anchor-nav.md) | 100% | 12 | 11 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 12 | – |
| [collapsible](./collapsible.md) | 100% | 12 | 10 | 0 | 0 | 0 | 2 | 0 | 0 | 0 | 12 | – |
| [date-field](./date-field.md) | 100% | 11 | 11 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 21 | y |
| [search-field](./search-field.md) | 100% | 11 | 10 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 17 | y |
| [time-field](./time-field.md) | 100% | 11 | 11 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 21 | y |
| [text-gradient](./text-gradient.md) | 100% | 10 | 10 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 7 | – |
| [banner](./banner.md) | 100% | 9 | 9 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 53 | y |
| [box](./box.md) | 100% | 9 | 9 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 43 | – |
| [chat-typing](./chat-typing.md) | 100% | 9 | 9 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 8 | – |
| [label](./label.md) | 100% | 8 | 2 | 0 | 0 | 0 | 6 | 0 | 0 | 0 | 2 | – |
| [text-focus](./text-focus.md) | 100% | 8 | 8 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 8 | – |
| [color-swatch](./color-swatch.md) | 100% | 7 | 7 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 14 | y |
| [date-range-field](./date-range-field.md) | 100% | 7 | 6 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 7 | – |
| [time-range-field](./time-range-field.md) | 100% | 7 | 6 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 7 | – |
| [display](./display.md) | — | 6 | 0 | 0 | 0 | 0 | 6 | 0 | 0 | 0 | 0 | – |
| [skip-link](./skip-link.md) | 100% | 6 | 5 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 8 | – |
| [text-circular](./text-circular.md) | 100% | 6 | 6 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 6 | – |
| [barcode](./barcode.md) | 100% | 5 | 5 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 7 | – |
| [mark](./mark.md) | 100% | 5 | 3 | 0 | 0 | 0 | 0 | 0 | 2 | 0 | 3 | – |
| [separator](./separator.md) | 100% | 4 | 4 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 3 | – |
| [aspect-ratio](./aspect-ratio.md) | strct | 3 | 0 | 0 | 0 | 0 | 0 | 3 | 0 | 0 | 0 | – |
| [qr-code](./qr-code.md) | 100% | 3 | 3 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 10 | – |
| [button-group](./button-group.md) | 100% | 2 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | – |
| [date-picker](./date-picker.md) | strct | 2 | 0 | 0 | 0 | 0 | 0 | 2 | 0 | 0 | 0 | – |
| [flex](./flex.md) | 100% | 2 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 7 | – |
| [grid](./grid.md) | 100% | 2 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 13 | – |
| [text-blur](./text-blur.md) | strct | 2 | 0 | 0 | 0 | 0 | 0 | 2 | 0 | 0 | 0 | – |
| [cascade](./cascade.md) | strct | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | – |
| [motion](./motion.md) | strct | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | – |
| [sticky](./sticky.md) | 100% | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | – |
| [surface](./surface.md) | 100% | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 25 | – |
| [alert-dialog](./alert-dialog.md) | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| [auto-grid](./auto-grid.md) | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| [container](./container.md) | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | – |
| [css-field](./css-field.md) | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| [float](./float.md) | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | – |
| [group](./group.md) | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | – |
| [highlight](./highlight.md) | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| [icon](./icon.md) | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 2 | – |
| [mask-field](./mask-field.md) | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| [number-field](./number-field.md) | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| [section](./section.md) | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 4 | – |
| [stack](./stack.md) | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| [wrap](./wrap.md) | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear. Ahora `theming-census.ts --report` escribe la auditoría entera bajo `docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162 recetas con CSS + 8 componentes sin receta, el árbol completo de `eidos/components/`. Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector (agrupados por clase), sistema transversal aparte, los privados con la columna que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado, un token prestado de otra receta, un shorthand o un eje físico. Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el componente que componen (icon-button → button), la capa que consumen (affix → viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button). Defectos del instrumento corregidos en el mismo pase, todos encontrados mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía como estado y producía `hover-disabled-*`; un knob que lee un privado se proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se reportaba como préstamo del componente `icon` (ahora el préstamo se verifica contra las claves del dueño en `base.ts`); y el default de talla salía sin nombrar en vez de `-md`. Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 · 1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0 diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs · prettier limpio (el árbol generado entra en .prettierignore junto a eidos/generated). Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco correcciones — dos de ellas evitaban romper el default), y 5 que no se tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado declarados en su propio CSS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
## Veredicto y decisiones
<!-- veredicto:start -->
_(pendiente — lo escribe el autor; se conserva al regenerar)_
<!-- veredicto:end -->

Powered by TurnKey Linux.