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

375 lines
22 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# Auditoría de herencia y escalado — capa Eidos / Theming
> **Fecha:** 2026-06-27 · **Alcance:** `src/uix/eidos` (foundation + recipes + 80
> componentes). **Excluidos** (tracks WIP, regla de proyecto): `words/**`,
> `palabras/**`, `chronos/**`.
>
> **Método:** lectura directa de la foundation (`lib/render-css.ts`,
> `lib/primitives/static.ts`, `lib/config-types.ts`), de la doctrina
> (`THEMING.md`, `SCALING_RFC.md`, `SHAPE_ENGINE_RFC.md`,
> `STRUCTURE_ENGINE_RFC.md`, `TYPOGRAPHY_ENGINE_RFC.md`) y de los recipes
> (`lib/recipes/base.ts`) + CSS por componente. Cada hallazgo lleva evidencia
> `archivo:línea`. No se usaron agentes; los datos provienen de grep/lectura
> verificables.
---
## 0. Las 5 reglas auditadas
| # | Regla (enunciado del usuario) | Veredicto global |
|---|---|---|
| **R1** | Los componentes deben escalar con el **nivel de escala del tema** (`data-scaling`). | ✅ **Cumplida en lo esencial** (vía tokens) — 2 excepciones puntuales. |
| **R2** | Los componentes deben reaccionar a la **densidad del tema** (`data-density`). | ✅ **Cumplida en lo esencial** (vía tokens) — mismas excepciones. |
| **R3** | Un componente sin `size` propio debe **heredar el `size` de su padre**. | ❌ **Incumplida de forma sistémica** — ~todos hardcodean `size='md'`. |
| **R4** | Los sub-componentes internos deben escalar con el `size` (tipografía, controles, etc.). | 🟡 **Parcial** — geometría + tipografía sí; el **radio NO**. |
| **R5** | Las **formas de esquina/radios** deben heredar/escalar (radios concéntricos). | ❌ **Incumplida** — radio desacoplado del `size`; concéntrico casi sin adoptar. |
**Tesis de la auditoría:** la disciplina de *tokenización* del framework hace que
R1 y R2 se cumplan casi gratis (todo lo que es un token hereda `--scaling` y la
densidad). Donde el sistema falla es en las tres reglas de **herencia/coherencia
de tamaño y forma** (R3, R4-radio, R5): no existe herencia ambiental de `size`,
el **radio no forma parte de la cascada `[data-size]`** salvo en `toggle`, y
conviven **tres filosofías de radio incompatibles** sin doctrina única.
---
## 1. Cómo la foundation implementa cada eje (el contrato)
### 1.1 Scaling (zoom global `data-scaling` 90–110)
`--scaling` se emite en `:root` (`render-css.ts:708` `appendScalingDeclarations`)
y **multiplica** las métricas px en su `calc`:
- `font-size` → `calc(<size> * var(--scaling))` (`render-css.ts:1028`).
- `icon-size`, `blur` → `appendScaledMetricDeclarations` (`render-css.ts:260,266`).
- `space`, `control-height` → `calc(value * var(--density-*-scale) * var(--scaling))`
(`render-css.ts:162-168`).
- **NO** escalan: `radius`, `border-width`, `shadow`, `z`, `line-height` (ratio),
`opacity`, `motion` (decisión deliberada, `SCALING_RFC.md:34-36`).
→ **Consecuencia:** cualquier componente que dimensione con `var(--font-size-*)`,
`var(--icon-size-*)`, `var(--space-*)`, `var(--control-height-*)` (o tokens de
recipe que los referencian) escala con `--scaling` **automáticamente**. Solo los
**literales px/rem** quedan fuera.
### 1.2 Densidad (`data-density` compact/comfortable/spacious)
`--density-space-scale` + `--density-control-scale` (`static.ts:169` `STATIC_DENSITY`)
multiplican `--space-*` y `--control-height-*` (`render-css.ts:162-168`). La
tipografía **no** lleva densidad por diseño (`THEMING.md:687-694`, paridad Radix).
→ **Consecuencia:** los componentes reaccionan a densidad si su padding/gap salen
de `--space-*` y sus alturas de `--control-height-*`. Confirmado en los recipes:
`button.height-md → var(--control-height-md)`, `field`, `combobox`, etc.
(`base.ts:841,1065,1222`).
### 1.3 Size (prop `size` discreto xxs…xxl)
La foundation **emite** un bundle `--size-{k}-{control-height,font-size,icon-size,
padding-inline,padding-block,gap,radius}` (`render-css.ts:1328` `appendSizeDeclarations`,
desde `STATIC_SIZE` en `static.ts:393`). **PERO ese bundle está huérfano: 0
consumidores** (`THEMING.md:605-606`). En su lugar, **cada recipe re-declara su
propia familia de tokens por size** (`--{c}-height-{size}`, `--{c}-font-size-{size}`,
…) y el CSS rebindea `--_{c}-*` en bloques `[data-size='X']`. El patrón canónico
(piloto) es `toggle` (`toggle.css:64-98`).
### 1.4 Radio / forma (shape)
- Escala `--radius-{none…full}` (4/6/10/16/20px + pill) (`static.ts:40`).
- `STATIC_SIZE` mapea **cada size a un radio** (xxs/xs→`sm`, sm/md→`md`, lg→`lg`,
xl→`xl`, xxl→`xxl`) (`static.ts:393-457`) — pero ese mapeo viaja en el bundle
`--size-*` huérfano, así que **nadie lo consume**.
- Concéntrico: `[data-shape-nest]` deriva `border-radius: max(0px, var(--shape-outer-radius)
− var(--shape-nest-gap))` (`render-css.ts:1153-1159`, `SHAPE_ENGINE_RFC.md` Fase 2).
- Familias `data-shape='rounded|continuous|cut|scoop'` + `--shape-smoothing`
(superelipse) (`render-css.ts:1140-1151`).
---
## 2. Hallazgos por regla
### R1 — Scaling · ✅ cumplida en lo esencial · severidad de los huecos: BAJA
Todo el dimensionado pasa por tokens scaling-coupled, así que el grueso cumple.
Excepciones reales (literales que **no** siguen `--scaling`):
| Evidencia | Problema |
|---|---|
| `carousel.css:128-142` | `--_carousel-indicator-size: 0.375rem … 0.75rem` — **literal rem por size**, no token. Los puntos de paginación no siguen `--scaling` (sí el zoom de navegador, pero no el eje del tema). |
| `base.ts:1030-1031` (meter/progress) | `height-md: '6px'`, `height-lg: '8px'` — grosor de barra en px fijo (no escala). Menor: es grosor visual, discutible. |
| Hairlines `1px`/`2px` (separadores, indicadores) — `button.css:245`, `command.css:153`, `navigation-menu.css:288`, etc. | **Legítimo**: un separador de 1px no debe escalar. No es violación. |
### R2 — Densidad · ✅ cumplida en lo esencial · severidad: BAJA
Padding/gap salen de `--space-*` y alturas de `--control-height-*` (ambos
density-coupled). Confirmado en recipes (`base.ts:341-352,481-525,841-842,1065-1076`).
Mismas excepciones que R1 (el rem de carousel tampoco lleva densidad; grosor de
barra fijo). La tipografía no escala con densidad **a propósito** (correcto).
### R3 — Herencia de `size` del padre · ❌ incumplida sistémica · severidad: ALTA
**No existe herencia ambiental de `size`.** Prácticamente **todos** los
componentes hardcodean `size = 'md'` como default (grep: 90+ wrappers
`.svelte` con `size = 'md'`). Un componente sin `size` **no mira a su padre**:
fija `md`.
Las **únicas** propagaciones contenedor→parte (las excepciones que confirman la
regla):
| Mecanismo | Evidencia | Alcance |
|---|---|---|
| `dialog/context.ts` → `Dialog.Close` deriva el size del dialog (cap en su subset) | `dialog/context.ts:21-31`, `dialog-close.svelte:49` | solo Dialog→Close |
| `list-surface-context.ts` — paneles flotantes anidados igualan el size de la superficie raíz | `lib/list-surface-context.ts:14-30` | menús/popups anidados (SubContent, listbox combobox) |
| `toggle-group/context.ts` — propaga `variant`/`size` a los items | (contexto de toggle-group) | solo items de toggle-group |
Casos que **deberían** heredar y NO lo hacen:
- Un `<Select>` / `<Switch>` / `<Checkbox>` junto a hermanos `lg` o dentro de un
bloque sizeado: salen `md`.
- `<Form size="lg">` **no** propaga el size a los `<Field>` / controles anidados
por contexto ambiental — el recipe `form` solo sizea su propio gap + sus
acciones (`form.css:25-51,118-137`), no inyecta un `size` a Fields arbitrarios.
- `Dialog.*` / `Drawer.*` solo propagan al Close; el resto de partes internas no.
**Causa raíz:** no hay un token/contexto `--ui-size` ambiental ni un
`SizeContext` genérico que un control lea cuando su prop `size` es `undefined`.
El default es un literal `'md'`, no `inherit`.
### R4 — Escalado interno con el `size` propio · 🟡 parcial · severidad: MEDIA
**Lo que SÍ escala** (verificado en ~35 componentes): por cada `[data-size='X']`
el componente rebindea **altura, padding, gap, font-size, icon-size** y sus
dimensiones específicas (track/thumb/indicator/control-size/swatch/preview/day-size…).
Ejemplos confirmados: `button.css:111-154`, `field.css:56-78`, `select.css:55-80,198-220`,
`switch.css:35-61`, `slider.css:10-36`, `radio-group.css:96-114,212-230`,
`tabs.css:23-42`, `tag-group.css:20-41`, `toolbar.css:30-55`, `pagination.css:15-34`,
`accordion.css:42-150`, `calendar.css:28-47`, `card.css:95-129`, `checkbox.css:34-62`,
`combobox.css:109-135,293-315`, `feed.css:45-68`, `editable.css:23-49`,
`file-upload.css:17-31`, `avatar.css:50-73`, `banner.css:38-54`, `tooltip.css:46-56`.
→ **La tipografía interna y la geometría de sub-controles escalan correctamente.**
**Lo que NO escala con el `size`:**
1. **El radio** — ausente de casi todas las cascadas `[data-size]` (ver R5). Solo
`toggle` lo rebindea.
2. **Decoraciones con literal** — `carousel` puntos en rem (R1).
### R5 — Radio / forma · ❌ incumplida · severidad: ALTA
**5.a — El radio NO escala con el `size`.** En casi todos los componentes el radio
es un token **fijo**, idéntico en todas las tallas:
| Componente | Radio | Evidencia |
|---|---|---|
| `field` (toda la familia de campos) | `var(--field-control-radius)` único | `field.css:191` |
| `select` | `--select-trigger-radius` / `--select-content-radius` fijos | `select.css:39,189` |
| `tabs` | `--tabs-trigger/content/list/pills-*-radius` fijos | `tabs.css:72,115,166,174,198` |
| `pagination` | `--pagination-control-radius` fijo | `pagination.css:52` |
| `tag-group` | `--tag-group-item-radius` fijo | `tag-group.css:70` |
| `stepper`, `toolbar`, `combobox`, `tooltip`, `listbox`, menús, calendars | `var(--radius-*)` fijo | grep `border-radius: var(--radius-` |
**Único componente que acopla radio↔size:** `toggle` —
`--_toggle-radius: var(--toggle-radius-{size})` rebindeado por `[data-size]`
(`toggle.css:7,70,79,88,97`). Es el patrón correcto según `STATIC_SIZE`.
**5.b — Incoherencia de filosofía: 3 modelos de radio coexisten sin doctrina.**
- **size-coupled:** `toggle` (radio sigue el size).
- **prop `rounded` independiente:** `button` y `card` declaran `--{c}-radius-{sm..xl}`
pero los cablean a `data-rounded` (default `md`), **no** a `data-size`
(`button.css:27,216-220`; `card.css:40,133-134`). → un `<Button size="xl">`
conserva esquinas `md`.
- **token fijo único:** todos los demás (sin `rounded`, sin acoplar a size).
El primitivo canónico que zanjaría esto — el campo `radius` por size de
`STATIC_SIZE` y el token `--size-{k}-radius` (`render-css.ts:1344`) — **está
huérfano (0 consumidores)** (`THEMING.md:605`). El contrato existe pero nadie lo usa.
**5.c — Radios concéntricos: adopción casi nula.** `[data-shape-nest]` /
`--shape-outer-radius` solo aparece en **11 archivos** (card-group, combobox,
select, command, menubar + sus items). Las superficies anidadas más comunes —
campo dentro de `Form`, card dentro de panel, contenido de `Dialog`/`Drawer`,
calendar dentro de un picker, items de la mayoría de menús — **no** derivan su
radio del padre. El concéntrico es opt-in por diseño (`SHAPE_ENGINE_RFC.md:80`),
pero su penetración real es marginal.
---
## 3. Incoherencias estructurales transversales
1. **Bundle `--size-*` huérfano + contradictorio.** La foundation emite
`--size-md-font-size: 14px` (`appendSizeDeclarations`), pero la regla viva es
el **1:1** (`md = 16px`) re-declarado en cada recipe (`THEMING.md:605-606`).
El sistema mantiene un contrato de tamaño que (a) nadie consume y (b) miente
sobre los valores actuales. Es deuda que invita a drift.
2. **Re-declaración del mapeo size→token en cada recipe.** Como nadie consume
`--size-{k}-*`, cada componente repite a mano `height/px/gap/font/radius` por
size (decenas de tokens × 80 componentes). Un solo punto de verdad (consumir
el bundle) eliminaría la duplicación — hoy el único guard contra drift es el
test de "no literales px en font/icon" (`THEMING.md:608-611`), que **no cubre
padding/gap/radio**.
3. **Sin herencia ambiental de size (R3)** ni de **forma** (R5.c): los dos ejes
que el usuario espera "heredables por defecto" son justo los que el sistema
trata como per-instancia con default literal.
---
## 4. Matriz por componente
Leyenda: ✅ cumple · 🟡 parcial · ❌ incumple · `—` no aplica (sin eje de
`size`/densidad). R1=scaling · R2=densidad · R3=hereda size del padre ·
R4=escalado interno con size · R5=radio escala/concéntrico.
### 4.1 Controles e inputs (tienen `size`)
| Componente | R1 | R2 | R3 | R4 | R5 | Notas |
|---|---|---|---|---|---|---|
| `toggle` | ✅ | ✅ | ❌ md | ✅ | ✅ | **Referencia**: radio acoplado a size. |
| `button` | ✅ | ✅ | ❌ md | 🟡 | ❌ | radio por `rounded`, no size. |
| `badge` | ✅ | ✅ | ❌ md | 🟡 | ❌ | radio fijo / `rounded`. |
| `checkbox` | ✅ | ✅ | ❌ md | ✅ | ❌ | radio del box fijo. |
| `radio-group` | ✅ | ✅ | ❌ md | ✅ | ❌ | indicador circular (radio N/A), pero item radio fijo. |
| `switch` | ✅ | ✅ | ❌ md | ✅ | ❌ | track/thumb son pills (radio full, OK). |
| `slider` | ✅ | ✅ | ❌ md | ✅ | ❌ | track/thumb pills (OK). |
| `field` (genérico) | ✅ | ✅ | ❌ md | ✅ | ❌ | `--field-control-radius` único. |
| `spin/number/css-field` | ✅ | ✅ | ❌ md | ✅ | ❌ | comparten superficie spin-field. |
| `date/time/color-field` | ✅ | ✅ | ❌ md | ✅ | ❌ | segmentos + swatch escalan; radio fijo. |
| `search/password-field` | ✅ | ✅ | ❌ md | ✅ | ❌ | radio fijo. |
| `pin-input` | ✅ | ✅ | ❌ md | ✅ | ❌ | celdas: font escala (`cell-font-size-*`), radio fijo. |
| `textarea` | ✅ | ✅ | ❌ md | ✅ | ❌ | radio fijo. |
| `tags-input` | ✅ | ✅ | ❌ md | ✅ | ❌ | radio fijo. |
| `editable` | ✅ | ✅ | ❌ md | ✅ | ❌ | radio fijo. |
| `select` | ✅ | ✅ | ❌ md | ✅ | ❌ | trigger+content escalan; radios fijos. |
| `combobox` | ✅ | ✅ | ❌ md | ✅ | ❌ | usa concéntrico en items (`data-shape-nest`). |
| `rating-group` | ✅ | ✅ | ❌ md | ✅ | — | iconos (estrellas); radio N/A. |
| `stepper` | ✅ | ✅ | ❌ md | ✅ | ❌ | indicador/trigger/content radio fijo. |
### 4.2 Acciones compuestas (composición sobre Button/Toggle)
| Componente | R1 | R2 | R3 | R4 | R5 | Notas |
|---|---|---|---|---|---|---|
| `toggle-group` | ✅ | ✅ | 🟡 ctx | ✅ | ❌ | hereda size/variant del grupo (contexto) — **único buen caso de R3 en controles**; items son Toggle → radio podría seguir el size pero se aplana en grupo. |
| `button-group` | ✅ | ✅ | ❌ md | 🟡 | ❌ | radio en extremos via Button. |
| `split-button` | ✅ | ✅ | ❌ md | 🟡 | ❌ | compone Button. |
| `fab` | ✅ | ✅ | ❌ md | ✅ | ❌ | redondo (radio full, OK). |
| `menu-dial` / `onion-menu` | ✅ | ✅ | ❌ md | ✅ | — | radial; radio full. |
### 4.3 Overlays y superficies (size = densidad/anchura del panel)
| Componente | R1 | R2 | R3 | R4 | R5 | Notas |
|---|---|---|---|---|---|---|
| `dialog` | ✅ | ✅ | 🟡 Close | ✅ | 🟡 | propaga size al Close (ctx). Radio panel fijo (en `full` lo cambia, `dialog.css:177`). |
| `drawer` | ✅ | ✅ | ❌ | ✅ | 🟡 | width/height/padding por size; radio fijo. |
| `popover` | ✅ | ✅ | ❌ md | 🟡 | ❌ | radio `--radius-md` fijo. |
| `tooltip` | ✅ | ✅ | ❌ md | ✅ | ❌ | py/px/font por size; radio fijo. |
| `dropdown/context-menu` | ✅ | ✅ | 🟡 ctx | 🟡 | ❌ | list-surface ctx para anidados; usan `data-shape-nest` en items. |
| `menubar` / `navigation-menu` | ✅ | ✅ | 🟡 ctx | 🟡 | ❌ | concéntrico parcial. |
| `command` | ✅ | ✅ | ❌ md | ✅ | ❌ | input/item escalan; concéntrico en items. |
| `listbox` | ✅ | ✅ | 🟡 ctx | 🟡 | ❌ | radio fijo. |
| `tabs` | ✅ | ✅ | ❌ md | ✅ | ❌ | trigger/content/list/pills radio TODO fijo. |
| `toolbar` | ✅ | ✅ | ❌ md | ✅ | ❌ | controles escalan; radios fijos. |
| `banner` / `toast` / `announce` | ✅ | ✅ | ❌ md | ✅ | ❌ | radio fijo. |
### 4.4 Pickers y calendarios
| Componente | R1 | R2 | R3 | R4 | R5 | Notas |
|---|---|---|---|---|---|---|
| `calendar` / `range-calendar` | ✅ | ✅ | ❌ md | ✅ | ❌ | padding/control/day/font por size; celdas radio fijo. |
| `month-grid` / `year-grid` | ✅ | ✅ | ❌ md | ✅ | ❌ | escalan font/celda. |
| `date/time/date-range/time-range-picker` | ✅ | ✅ | ❌ md | ✅ | ❌ | reusan tokens de field+calendar por size. |
| `color-picker` | ✅ | ✅ | ❌ md | ✅ | ❌ | trigger/content/swatch por size. |
| `gradient-builder` / `gradient-picker` | ✅ | ✅ | ❌ md | 🟡 | ❌ | (nuevos, sin commitear) radio fijo. |
### 4.5 Datos / navegación / feedback
| Componente | R1 | R2 | R3 | R4 | R5 | Notas |
|---|---|---|---|---|---|---|
| `accordion` | ✅ | ✅ | ❌ md | ✅ | ❌ | trigger/content/indicator por size (incl. `full`); radio item fijo. |
| `breadcrumb` | ✅ | ✅ | ❌ md | ✅ | — | solo gap+font (sin radio relevante). |
| `pagination` | ✅ | ✅ | ❌ md | ✅ | ❌ | control radio fijo. |
| `tag-group` | ✅ | ✅ | ❌ md | ✅ | ❌ | item radio fijo. |
| `tabs` | (ver 4.3) | | | | | |
| `table` / `tree-view` / `tree-grid` / `grid-list` | ✅ | ✅ | ❌ md | ✅ | ❌ | densidad de fila por size; radios de celda/chip fijos. |
| `feed` | ✅ | ✅ | ❌ md | ✅ | — | padding/indent/font por size. |
| `carousel` | 🟡 | 🟡 | ❌ md | 🟡 | — | **indicadores en rem literal** (`carousel.css:128-142`). |
| `virtual-list` / `virtual-grid` | ✅ | ✅ | ❌ md | ✅ | — | densidad por size. |
| `progress` / `meter` | 🟡 | 🟡 | ❌ md | ✅ | — | **grosor de barra px fijo** (`base.ts:1030`); radio full (OK). |
| `spinner` / `skeleton` | ✅ | ✅ | ❌ md | ✅ | — | radio circular/sm. |
| `avatar` | ✅ | ✅ | ❌ md | ✅ | 🟡 | tamaño+font por size; radio via `data-radius`, no size. |
| `card` | ✅ | ✅ | ❌ md | ✅ | ❌ | título/desc/body+padding por size; radio por `rounded`. |
| `card-group` | ✅ | ✅ | ❌ md | ✅ | 🟡 | usa `data-shape-nest` (concéntrico). |
| `metrics` / `timeline` | ✅ | ✅ | ❌ md | ✅ | ❌ | radios fijos. |
| `file-upload` | ✅ | ✅ | ❌ md | ✅ | ❌ | gap/padding/control/preview por size. |
| `form` | ✅ | ✅ | ❌ md | 🟡 | — | sizea gap+acciones; **no propaga size a Fields** (R3). |
### 4.6 Primitivas de layout / tipografía (sin eje `size`-densidad)
`box · stack · flex · grid · wrap · group · float · aspect-ratio · auto-grid ·
container · section` (layout) y `text · heading · display · code · code-block ·
kbd · mark · highlight · link · separator` (tipografía/inline):
- **R1/R2:** ✅ — escalan vía `var(--space-*)` (layout) y `var(--font-size-*)` /
`var(--style-*)` (tipografía), todos scaling/densidad-coupled.
- **R3/R4/R5:** `—` — no exponen `size` discreto de control (excepto `container`
default `xl` / `section` default `lg`, que son anchuras de layout, no densidad).
- Radio relevante solo en `code-block`/`kbd`/`mark`/`link` → `--radius-sm/md`
fijo (aceptable: no tienen eje de size).
---
## 5. Recomendaciones priorizadas
### P0 — Herencia ambiental de `size` (cierra R3)
Introducir un **contexto/cascada de size ambiental** que un control lea cuando su
prop `size` es `undefined`, en lugar de hardcodear `'md'`. Dos vías:
- **Contexto Svelte genérico** `SizeContext` (como `list-surface-context.ts` pero
universal), set por contenedores sizeados (`Form`, `Card`, `Toolbar`, `Dialog`…)
y leído por cada wrapper: `size = props.size ?? ctx?.size ?? 'md'`.
- O un token CSS heredable `--ui-size` + `data-size` que cascadee por DOM (cuando
no haya portal de por medio).
Aplicar primero a `Form → Field/controles` (el caso más esperado) reutilizando la
norma cap-en-`md` ya documentada (`THEMING.md:560-579`).
### P1 — Acoplar el radio al `size` (cierra R5.a / R4-radio)
Decidir **una** doctrina de radio y aplicarla:
- **Opción canónica (recomendada):** consumir el `radius` por size de
`STATIC_SIZE` — añadir `--_{c}-radius` a cada cascada `[data-size]` como hace
`toggle`. Resucita el contrato `--size-{k}-radius` hoy huérfano.
- Mantener `rounded` como **override** explícito sobre ese default por size
(no como sustituto), unificando `button`/`card` con el resto.
### P1 — Eliminar el bundle `--size-*` huérfano o forzar su consumo
O bien (a) los recipes **consumen** `--size-{k}-*` en vez de re-declarar el mapeo
(un punto de verdad, mata el drift y la mentira del `14px`), o bien (b) se poda el
bundle y se documenta que el canon vivo es el 1:1 por recipe. Hoy coexisten ambos
y se contradicen (`THEMING.md:605`).
### P2 — Ampliar radios concéntricos (R5.c)
Extender `[data-shape-nest]` a las superficies anidadas obvias: contenido de
`Dialog`/`Drawer`, `Field` dentro de `Form`, calendar dentro de los pickers,
items de menús que aún no lo usan.
### P3 — Tokenizar los literales residuales (R1/R2)
- `carousel` indicadores: `0.375rem…0.75rem` → tokens (`--icon-size-*` o
`--space-*`) para que sigan `--scaling`/densidad (`carousel.css:128-142`).
- Evaluar grosor de barra `progress`/`meter` (`base.ts:1030`) — si debe seguir el
zoom, pasarlo a un token escalado.
### P3 — Guard de coherencia ampliado
El test actual solo prohíbe literales px en `font-size`/`icon-size`
(`THEMING.md:608`). Ampliarlo a `padding`/`gap`/`radius` de recipe y a una regla
"si hay `[data-size]`, debe rebindear el radio" cerraría R4/R5 a nivel de CI.
---
## 6. Resumen de severidad
| Hallazgo | Regla | Severidad | Amplitud |
|---|---|---|---|
| Sin herencia ambiental de `size` (default literal `md`) | R3 | **ALTA** | ~todos los componentes |
| Radio desacoplado del `size` (solo `toggle` lo acopla) | R5.a / R4 | **ALTA** | ~todos los controles/superficies |
| 3 filosofías de radio sin doctrina única | R5.b | **ALTA** (coherencia) | toggle vs button/card vs resto |
| Bundle `--size-*` huérfano y contradictorio | transversal | MEDIA | foundation + 80 recipes |
| Concéntrico casi sin adoptar | R5.c | MEDIA | superficies anidadas |
| Literales rem/px (carousel, barras) | R1/R2 | BAJA | 2–3 componentes |
**Conclusión:** el sistema es **fuerte** en R1/R2 (la tokenización fuerza
scaling+densidad casi gratis) y **débil** justo donde el usuario apunta: la
**herencia** (R3 inexistente, R5.c marginal) y la **coherencia del radio con el
size** (R5.a/R4, con una inconsistencia de fondo entre `toggle`, `button/card` y
el resto). Las cuatro acciones P0–P1 resuelven el 80% del problema reutilizando
mecanismos que el framework ya tiene a medio cablear (el contexto de size de
`dialog`, el `radius` por size de `STATIC_SIZE`, el patrón `toggle`).

Powered by TurnKey Linux.