Ocho claves acuñadas, diecisiete bloques `[data-size]` retirados y veinticuatro
adjudicaciones medidas. El default no se mueve: diff de computed 0 sobre 25.263
valores en 47 estados (20 combinaciones de variante x estado x talla con su
pasada de hover, más las 7 de la sonda estándar) y capturas 2x idénticas.
Censo: 77 % → 81 % (knobs 75 → 59, público 56 → 46, global 7 → 1, privado 10
invariante, sistema 2). Contrato 61 → 69 claves. Global no baja (68 %).
QUÉ ENTRA (8 claves)
item-size · dot-size · font-size — los tres ejes RESUELTOS por talla en el
TSC, con `scope: 'size:{k}'` sobre el proveedor, que es quien lleva
`data-size`. Con ellos se van los DIECISIETE bloques `[data-size]` del CSS
(§7.4-4: dejarlos vivos hace que el diff dé 0 por la ruta vieja) y el privado
`--_radio-item-size`, que llevaba el nombre del componente ABREVIADO —
theming §6 r5 lo prohíbe, es la clase de `--_mp-*` y `--gp-*` — y por eso el
censo contaba el ancho y el alto del ítem como deuda.
invalid-indicator-border · invalid-ring · invalid-ring-gap ·
invalid-item-label-fg — el tinte de validación leía `--color-threat-element`
y `--color-threat-text` a pelo. El trío del anillo toma el vocabulario del
gemelo `checkbox`, que ya lo tenía; el borde se llama `indicator-*` porque en
ESTE componente «indicator» es el círculo exterior y `dot-*` el punto.
segmented-segment-padding-block — el hermano que le faltaba a
`-padding-inline`.
UNA CLAVE ACUÑADA Y RETIRADA EN EL ACTO
`row-min-height-touch: var(--touch-target)` para el suelo táctil de la fila.
La suite `recipe-css-contract` la rechazó como referencia FANTASMA: nadie
emite `--touch-target`, es una constante ergonómica escrita a mano en
`archetypes.css` («a hard ergonomic constant — the fingertip, NOT a theme
value»). Y recipe-contract §2 ya lista el touch-target entre los sistemas
transversales que una receta CONSUME y nunca acuña, así que la lectura cruda
es el contrato cumpliéndose. El censo la cuenta `global` sólo porque su lista
`system` aún no conoce el token — el pendiente de firma «extender la clase
system a las capas compartidas».
POR QUÉ 81 % ES EL TECHO
Los diez knobs que quedan fuera leen `--_radio-group-palette-*`: el puente
THM-2 que la capa compartida alimenta por instancia desde `[data-color]`. Un
público encima dejaría que un tema lo fijara y matara el `color=` de cada
instancia. No es deuda; es el componente cerrado en su número.
TRES DECLARACIONES QUE NO PINTAN (medidas, y las tres mueven píxel → firma)
1. La escala del punto NUNCA ha pintado. `<SvgDot>` escribe
`style="width: 1em; height: 1em"` y un inline gana a cualquier regla: el
punto computa 13,3281px en las CINCO tallas mientras el token dice
5/6/7/9/11px, el valor centinela no lo mueve, y quitando el estilo inline
el MISMO token pinta 1234px. Clase «canal de valor inline» del trío
`carousel.item-gap*`, pero con arreglo posible: la forma `!important` que
recipe-contract §3 autoriza justo para saltarse un estilo inline.
2. `[data-radio-group-label][data-disabled]` no casa jamás. Con el grupo
entero deshabilitado la etiqueta lleva `[id, data-radio-group-label,
data-archetype]` y nada más: soma no estampa `data-disabled` en la parte
`Label` (el morfo declara `data: []`). Forzado a mano el token alcanza, o
sea que falta el ATRIBUTO, no la clave — decisión de MORFO.
3. El fondo de un radio marcado Y deshabilitado no llega: la regla de
variante es (0,4,0) y `[data-radio-group-item][data-disabled][data-state=
'checked']` sólo (0,3,0). Medido con el centinela escrito: solid da el
acento a plena intensidad, outline y segmented transparente, ghost la
mezcla al 18 %. Quitar `data-variant` no ayuda: el gemelo
`:not([data-variant])` también es (0,4,0).
Y una cuarta, de escala del SISTEMA y por tanto fuera de este eje (D-TH.5):
el `:hover` del círculo es (0,4,0) y el tinte inválido (0,3,0), así que pasar
el ratón por encima TAPA el borde de error. Las cuatro, en next-features §13.
EL INSTRUMENTO MEDÍA 27 DE 61
No veía el punto (es un `<svg data-svg='dot'>` sin atributo del componente),
ni la variante `segmented` (doce claves), ni los estados disabled / invalid.
El guard lleva ahora `extraNodes` para el punto, el barrido de `data-variant`
y DOS URL más — `?perm.disabled=true` y `?perm.invalid=true`, que esta demo
honra: once adjudicaciones sustituidas por medición real.
CENTINELA 45/69, 24 adjudicadas: 6 del punto (estilo inline), 8 tonos por la
cascada de paleta (mismo patrón que su gemelo checkbox, medido aquí: la clave
no mueve nada, `--palette-solid` en el nodo repinta), 4 del par de transición
congelado, 2 del anillo inválido (necesita `:focus-visible` con modalidad de
teclado), `gap-horizontal` (el par de gap, sólo pinta la mitad en vigor),
`segmented-pill-shadow` (la píldora sólo la renderiza el envoltorio bajo la
variante real), `disabled-item-label-fg` y `checked-disabled-indicator-bg` (las
dos declaraciones muertas de arriba).
GUARDS
censo --only radio-group 81 % · global 68 % (no baja) · component-audit PASS ·
eidos-lint 0 invalid (13 morfo-backed, 24 eidos-only) · vitest src/uix/eidos
sin rojos nuevos (el único es `skin-media-player`, conocido) · rtl:check 0 ·
docs:check 0 · prettier OK sobre el contenido normalizado a LF · check sin
errores en ficheros del componente.
README con su sección «Talla y tema» y la demo con su pestaña Tokens (69
claves, verificada en navegador).
Índice construido a mano desde HEAD sobre los seis ficheros compartidos
(recipes/base.ts, generated/base.css, theming-sentinel.ts,
theming-sentinel-exceptions.ts, PLAN-theming.md, next-features.md): otras
cuatro sesiones los escriben a la vez. Fuera del commit, docs/audit/theming/
README.md — es un agregado global que mezcla trabajo en vuelo.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
## 3. Privados de la receta — ¿de dónde sale su valor?
@ -71,9 +65,8 @@ Consumidos y **no declarados en el CSS** (vienen de `base.ts` o de un estilo inl
## 4. Propuesta de corrección
- **Consume la capa compartida `sliding-indicator`.** Un eje que la capa posee se consume como `var(--_x, var(--x))`; el consumidor **no acuña**`--radio-group-{eje}` para él — sería un vocabulario paralelo (README de `eidos/components`, «Capas compartidas» regla 2).
- **Tiene eje `size`**: los tokens dimensionales van por talla (`{part}-{eje}-{k}`) apuntando al bundle `--size-{k}-*`, nunca al primitivo crudo (theming §5; el guard `recipe-css-contract` prohíbe el primitivo).
### 4.1 Tokens a declarar en `lib/recipes/base.ts` (9)
### 4.1 Tokens a declarar en `lib/recipes/base.ts` (3)
Valor **verbatim** del CSS de hoy: el default no se mueve, sólo cambia quién
puede moverlo. Nombres derivados de recipe-contract §1 (ejes lógicos, talla
@ -85,13 +78,7 @@ no distingue lo que debería — se marca `⚠`.
'the guard freezes transitions and this token IS the transition; measured unfrozen on a real day cell -> reaches (0.12s -> 11.5s on all five properties)',
// 2026-08-23. The guard already sweeps its four variants and both
// orientations and measures the disabled / invalid stages through the
// demo's URL switches; these are what is left, each measured on the REAL
// nodes. Three of them are DEFECTS, not instrument limits, and say so.
//
// (1) The dot's whole scale — a value channel written INLINE.
'dot-size':
'DEAD BY INLINE STYLE, not by the demo: <SvgDot> writes style="width: 1em; height: 1em" on the glyph, and an inline declaration beats any stylesheet rule. Measured 2026-08-23: the dot computes 13.3281px (1em of the button UA font) at EVERY step while the token says 5/6/7/9/11px, the sentinel value changes nothing, and removing the inline style makes the same token paint 1234px. The recipe scale has never painted — a pixel-moving fix (the `!important` shape recipe-contract §3 allows for exactly this, bypassing an inline style) that needs a signature; registered in next-features §13',
'dot-size-xs':'same inline style; the step reads 5px and the dot paints 13.3281px',
'dot-size-sm':'same inline style; the step reads 6px and the dot paints 13.3281px',
'dot-size-md':'same inline style; the step reads 7px and the dot paints 13.3281px',
'dot-size-lg':'same inline style; the step reads 9px and the dot paints 13.3281px',
'dot-size-xl':'same inline style; the step reads 11px and the dot paints 13.3281px',
// (2) A rule whose selector no runtime ever matches.
'disabled-item-label-fg':
'DEAD SELECTOR: it is consumed by [data-radio-group-label][data-disabled], and soma never stamps data-disabled on the Label part (the morfo declares `data: []` for it). Measured 2026-08-23 with the group disabled through ?perm.disabled=true: the label carries [id, data-radio-group-label, data-archetype] and nothing else. Forced on the label by hand -> reaches (oklch(0.2435 0 0) -> rgb(1, 2, 3)), so the token is sound and the ATTRIBUTE is missing — a morfo decision (checkbox\'s twin label does carry its own), registered in next-features §13',
// (3) A declaration the variant rules out-rank in every variant.
'checked-disabled-indicator-bg':
'SUPERSEDED by the variant rules: [data-radio-group][data-variant=X] [data-radio-group-item][data-state=checked] is (0,4,0) and [data-radio-group-item][data-disabled][data-state=checked] only (0,3,0). Measured 2026-08-23 on a disabled+checked item with the sentinel value written: solid oklch(0.5556 0.1829 305.86) (the full accent), outline and segmented rgba(0,0,0,0), ghost the 18% mix — never rgb(1,2,3). And removing data-variant does not help: the `:not([data-variant])` twin is (0,4,0) too. So a disabled+checked radio shows the FULL accent today; raising the specificity moves pixels -> signature, registered in next-features §13',
// Instrument limits, measured.
'gap-horizontal':
'the row gap is a PAIR and only the half in force paints: the demo boots `vertical`, and the guard sweeps ONE axis (data-variant, where twelve of its keys live). Switched the real demo to horizontal -> reaches (column-gap 16px -> 1234px)',
'invalid-ring':
'needs [data-invalid] AND :focus-visible, and the guard blurs on purpose; on a button :focus-visible only latches with the keyboard modality, so measured 2026-08-23 with a Tab followed by focus() on the real item -> reaches (the 4px ring oklch(0.9555 0.0207 13.86) -> rgb(1, 2, 3))',
'invalid-ring-gap':'same double ring; measured the same way -> reaches (the 2px gap oklch(0.9911 0 0) -> rgb(4, 5, 6))',
'transition-duration':
'the guard freezes transitions and this token IS the transition; measured unfrozen on the real item -> reaches (0.12s -> 11.5s on all four transitioned properties)',
'same freeze, on the segmented pair; measured unfrozen with the real variant on -> reaches (segment 0.18s -> 11.5s, and the pill\'s --indicator-duration 180ms -> 11.5s)',
'the sliding pill is a node the WRAPPER only renders under variant="segmented" (like Switch\'s default Thumb), so stamping data-variant while measuring cannot create it; measured with the demo\'s real segmented chip on -> reaches (rgba(15, 23, 42, 0.08) 0 1px 2px -> rgb(1, 2, 3) 0 0 0 7px)'
// `hover-segmented-segment-fg` had an entry here for one run and the guard
// reported it STALE on the next: it needs hover AND the segmented variant,
// which the hover pass does not sweep — yet the pointer's resting place is
// enough for the static pass to catch it. Verified alive twice before the
' — the same four tones x two slots as its twin checkbox, and PRIMARY falls with them: the eidos wrapper always resolves a colour and stamps data-color, so the generic block matches on every instance and the fallback that names the component key never applies. Measured 2026-08-23 on an affirm instance: --radio-group-affirm-solid moves nothing (oklch(0.6491 0.1136 181.96) stays), --palette-solid on the same node repaints (rgb(4, 5, 6))'