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

375 lines
22 KiB

# 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.