uix(code): temable — 0 % → 100 %, y el harness que también le pisa el radio

6 claves fusionadas en el bloque `code` de `recipes/base.ts` (donde ya vivía el
forward de paleta). Censo 0 % → **100 %**: no queda un knob de apariencia fuera
del contrato.

**Seis de sus trece knobs son SISTEMA y no se acuñan.** `code` es el primitivo
tipográfico canónico: sus seis ejes de tipo consumen `--style-code-*` con la
escotilla por instancia (`var(--_code-{eje}, var(--style-code-{eje}))`), la
forma que D-TH.2-b fijó, y un primitivo no re-declara el vocabulario de su
capa. Lo que entra al contrato es el CROMO de las variantes `soft` y `outline`.

- El padding es em-relativo a propósito (la píldora crece con el código que
  envuelve) y lo declaran IGUAL las dos variantes con cromo: un knob por eje,
  no cuatro.
- `--_code-color` sigue privado: conmutador de dos fuentes (tinta de contenido
  en línea / forward de paleta cuando llega `data-color`), mismo patrón que el
  borde de foco de textarea.
- El `margin: 0` / `padding: 0` / `background: transparent` / `border: 0` de la
  base es RESET: la variante `plain` es texto desnudo por identidad.

EL HALLAZGO — el harness pisa también a `code` (→ next-features §13, ampliado):

Medido con CDP: `[data-uix-docs] code` declara `font-family`, `font-size`,
`padding` y `border-radius` y gana **(0,1,1)** contra la regla BASE de la receta
**(0,1,0)**. En la página de documentación el radio del componente es el del
sitio (3px), no el suyo (4px). Sus reglas de VARIANTE (0,2,0) sí ganan, así que
el efecto es selectivo y por eso sólo `radius` lee muerto. **No es un caso
aislado**: es la misma familia de reglas del harness que tapa el `<pre>` de
code-block con siete propiedades. Cualquier decisión sobre esa regla cubre los
dos componentes.

Artefactos y gates:
- Sonda antes/después = 0 diffs en 174 valores × 7 estados. Determinismo
  verificado.
- R-5.4: 0/6, los seis adjudicados con su medición — `radius` por el harness
  (CDP), los otros cinco porque la demo monta sólo la variante `plain`, sin
  cromo; forzando las variantes, los cinco alcanzan.
- censo --only 100 % · component:audit PASS · rtl:check 0 · docs:check 0 ·
  check por fichero limpio · suite eidos sin rojos nuevos.
- README con «Talla y tema» y tab `Tokens` en la demo (6 claves).

Nota de proceso: el fichero de excepciones llegó a quedar con sintaxis TS
inválida por un apóstrofe sin escapar, y la comprobación que hice lo dio por
bueno porque el `$?` capturado era el del `grep` de la tubería, no el del
guard. Corregido, y re-verificados los ONCE componentes con ledger comprobando
el código de salida REAL: cero fallos.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
alpha-0.1-background
dev 2 months ago
parent d3089e880c
commit e4bcd505d1

@ -10,9 +10,9 @@
- **Medido**: 2026-08-21 · **162 recetas** con CSS + **8 componentes sin receta** = 170 fichas, el árbol entero de `eidos/components/`
- **La pregunta**: ¿cuánto de la apariencia de cada componente puede cambiar un tema **sin tocar el sistema ni la receta**?
- **Alcance global**: **49%** — 2281 de 4676 knobs pasan por un token público del componente
- **Reparto**: público 2281 · privado 635 · global 1189 · literal 571 · sistema transversal 509 _(fuera del ratio)_
- **Sin token público propio**: 39 · **alcance < 20 %**: 32 · **alcance 100 %**: 8 · **con eje `size`**: 62
- **Alcance global**: **49%** — 2288 de 4676 knobs pasan por un token público del componente
- **Reparto**: público 2288 · privado 635 · global 1186 · literal 567 · sistema transversal 509 _(fuera del ratio)_
- **Sin token público propio**: 38 · **alcance < 20 %**: 31 · **alcance 100 %**: 9 · **con eje `size`**: 62
## Cómo se lee
@ -91,12 +91,12 @@ theming §6.7 lo pone delante (`hover-bg`). Las fichas proponen la forma
| 19 | [timeline](./timeline.md) | 24 | 51% | 49 | 45 |
| 20 | [avatar](./avatar.md) | 23 | 36% | 36 | 0 |
## Sin ningún token público propio (39)
## Sin ningún token público propio (38)
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) · [`announce`](./announce.md) · [`aspect-ratio`](./aspect-ratio.md) · [`auto-grid`](./auto-grid.md) · [`avatar`](./avatar.md) · [`cascade`](./cascade.md) · [`clipboard`](./clipboard.md) · [`code`](./code.md) · [`css-field`](./css-field.md) · [`date-picker`](./date-picker.md) · [`display`](./display.md) · [`field-langs`](./field-langs.md) · [`heading`](./heading.md) · [`highlight`](./highlight.md) · [`knob`](./knob.md) · [`label`](./label.md) · [`mark`](./mark.md) · [`mask-field`](./mask-field.md) · [`menu-dial`](./menu-dial.md) · [`mockup`](./mockup.md) · [`month-grid`](./month-grid.md) · [`motion`](./motion.md) · [`number-field`](./number-field.md) · [`palabras`](./palabras.md) · [`range-calendar`](./range-calendar.md) · [`scroll-frames`](./scroll-frames.md) · [`skeleton`](./skeleton.md) · [`skin-media-player`](./skin-media-player.md) · [`stack`](./stack.md) · [`text`](./text.md) · [`text-blur`](./text-blur.md) · [`text-circular`](./text-circular.md) · [`text-focus`](./text-focus.md) · [`text-gradient`](./text-gradient.md) · [`text-scramble`](./text-scramble.md) · [`virtual-grid`](./virtual-grid.md) · [`virtual-list`](./virtual-list.md) · [`wrap`](./wrap.md) · [`year-grid`](./year-grid.md)
[`alert-dialog`](./alert-dialog.md) · [`announce`](./announce.md) · [`aspect-ratio`](./aspect-ratio.md) · [`auto-grid`](./auto-grid.md) · [`avatar`](./avatar.md) · [`cascade`](./cascade.md) · [`clipboard`](./clipboard.md) · [`css-field`](./css-field.md) · [`date-picker`](./date-picker.md) · [`display`](./display.md) · [`field-langs`](./field-langs.md) · [`heading`](./heading.md) · [`highlight`](./highlight.md) · [`knob`](./knob.md) · [`label`](./label.md) · [`mark`](./mark.md) · [`mask-field`](./mask-field.md) · [`menu-dial`](./menu-dial.md) · [`mockup`](./mockup.md) · [`month-grid`](./month-grid.md) · [`motion`](./motion.md) · [`number-field`](./number-field.md) · [`palabras`](./palabras.md) · [`range-calendar`](./range-calendar.md) · [`scroll-frames`](./scroll-frames.md) · [`skeleton`](./skeleton.md) · [`skin-media-player`](./skin-media-player.md) · [`stack`](./stack.md) · [`text`](./text.md) · [`text-blur`](./text-blur.md) · [`text-circular`](./text-circular.md) · [`text-focus`](./text-focus.md) · [`text-gradient`](./text-gradient.md) · [`text-scramble`](./text-scramble.md) · [`virtual-grid`](./virtual-grid.md) · [`virtual-list`](./virtual-list.md) · [`wrap`](./wrap.md) · [`year-grid`](./year-grid.md)
## Tabla completa (162 recetas, por alcance ascendente)
@ -108,7 +108,6 @@ La columna «contrato» cuenta las claves **públicas** del bloque del component
| [month-grid](./month-grid.md) | 0% | 61 | 0 | 7 | 0 | 5 | 49 | 0 | – |
| [year-grid](./year-grid.md) | 0% | 61 | 0 | 7 | 0 | 5 | 49 | 0 | – |
| [field-langs](./field-langs.md) | 0% | 41 | 0 | 0 | 31 | 8 | 2 | 0 | – |
| [code](./code.md) | 0% | 13 | 0 | 0 | 3 | 4 | 6 | 0 | – |
| [virtual-list](./virtual-list.md) | 0% | 13 | 0 | 2 | 6 | 4 | 1 | 0 | y |
| [virtual-grid](./virtual-grid.md) | 0% | 12 | 0 | 2 | 6 | 3 | 1 | 0 | y |
| [skeleton](./skeleton.md) | 0% | 11 | 0 | 9 | 1 | 1 | 0 | 0 | y |
@ -249,6 +248,7 @@ La columna «contrato» cuenta las claves **públicas** del bloque del component
| [chat-list](./chat-list.md) | 100% | 18 | 18 | 0 | 0 | 0 | 0 | 18 | – |
| [splitter](./splitter.md) | 100% | 18 | 17 | 0 | 0 | 0 | 1 | 15 | – |
| [link-preview](./link-preview.md) | 100% | 15 | 12 | 0 | 0 | 0 | 3 | 33 | – |
| [code](./code.md) | 100% | 13 | 7 | 0 | 0 | 0 | 6 | 6 | – |
| [box](./box.md) | 100% | 9 | 9 | 0 | 0 | 0 | 0 | 43 | – |
| [display](./display.md) | — | 6 | 0 | 0 | 0 | 0 | 6 | 0 | – |
| [barcode](./barcode.md) | 100% | 5 | 5 | 0 | 0 | 0 | 0 | 7 | – |

@ -5,33 +5,24 @@
> Vista de conjunto: [README](./README.md) · método y protocolo:
> [`PLAN-theming.md`](../../process/PLAN-theming.md) §1, §2, §7.
- **Medido**: 2026-08-20 · **Alcance**: **0%** — 0 de 7 knobs por token público
- **Knobs de apariencia**: 13 — público 0 · privado 0 · global 3 · literal 4 · sistema 6 _(fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 0 pública(s) · 1 privada(s) forward — `_palette-text`
- **Medido**: 2026-08-21 · **Alcance**: **100%** — 7 de 7 knobs por token público
- **Knobs de apariencia**: 13 — público 7 · privado 0 · global 0 · literal 0 · sistema 6 _(fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 6 pública(s) — `radius`, `soft-bg`, `border-width`, `border`, `padding-inline`, `padding-block` · 1 privada(s) forward — `_palette-text`
- **Eje `size`**: no · **ficheros**: `code.css`
## 1. Knobs fuera de alcance
### 1.1 Directo a primitivo global (3)
### 1.1 Directo a primitivo global (0)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `code.css:24` | `[data-code]` | `border-radius` | `var(--radius-sm)` |
| 2 | `code.css:38` | `[data-code][data-variant='soft']` | `background` | `var(--color-neutral-element)` |
| 3 | `code.css:44` | `[data-code][data-variant='outline']` | `border` | `var(--border-width) solid var(--color-neutral-border)` |
_Ninguno._
### 1.2 A través de un privado (0)
_Ninguno._
### 1.3 Literales (4)
### 1.3 Literales (0)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `code.css:39` | `[data-code][data-variant='soft']` | `padding-inline` | `0.32em` |
| 2 | `code.css:40` | `[data-code][data-variant='soft']` | `padding-block` | `0.1em` |
| 3 | `code.css:45` | `[data-code][data-variant='outline']` | `padding-inline` | `0.32em` |
| 4 | `code.css:46` | `[data-code][data-variant='outline']` | `padding-block` | `0.1em` |
_Ninguno._
## 2. Sistema transversal (6) — informativo, fuera del ratio
@ -56,26 +47,7 @@ Consumidos y **no declarados en el CSS** (vienen de `base.ts` o de un estilo inl
## 4. Propuesta de corrección
### 4.1 Tokens a declarar en `lib/recipes/base.ts` (6)
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
al final) y theming §6.7 (slots de color, modificador delante). Un token con
DOS valores distintos es una colisión de nombre: son dos knobs, o el nombre
no distingue lo que debería — se marca `⚠`.
| token (`--code-…`) | scope TSC | valor propuesto | usos |
| --- | --- | --- | ---: |
| `radius` | `root` | `var(--radius-sm)` | 1 |
| `soft-bg` | `root` | `var(--color-neutral-element)` | 1 |
| `soft-padding-inline` | `root` | `0.32em` | 1 |
| `soft-padding-block` | `root` | `0.1em` | 1 |
| `outline-padding-inline` | `root` | `0.32em` | 1 |
| `outline-padding-block` | `root` | `0.1em` | 1 |
### 4.2 Sin nombre mecánico (1)
- **⚠ decisión: `border` es shorthand o eje físico — hay que partirlo en ejes lógicos antes de nombrarlo (recipe-contract §1, R-4.4)** — 1: `border`.
_Nada que proponer: no hay knobs fuera de alcance._
### 4.4 Lo que hay que comprobar a mano (PLAN-theming §1.3 · §7.4)
@ -89,6 +61,24 @@ no distingue lo que debería — se marca `⚠`.
<!-- veredicto:start -->
_(pendiente — lo escribe el autor; se conserva al regenerar)_
**Medido 2026-08-21.** `code` es el PRIMITIVO TIPOGRÁFICO canónico, y el censo
ya lo trata como tal: **seis de sus trece knobs son sistema** porque sus seis
ejes de tipo consumen `--style-code-*` con la escotilla por instancia
(`var(--_code-{eje}, var(--style-code-{eje}))`), que es la forma que D-TH.2-b
fijó. **Ninguno de esos seis se acuña** — la capa los posee.
Quedan siete puntuables, y son todos CROMO DE VARIANTE:
1. `radius` (el pill), `soft-bg` y el par `border-width` + `border` del
outline.
2. El `padding` em-relativo (`0.32em` / `0.1em`), declarado IGUAL en las dos
variantes con cromo: un knob por eje, no cuatro. Es em-relativo a propósito
— la píldora crece con la letra del código que envuelve.
3. **`--_code-color` no se toca**: es el conmutador de dos fuentes (la tinta de
contenido en línea y el forward de paleta cuando llega `data-color`), el
mismo patrón que el borde de foco de `textarea`.
4. `margin: 0`, `padding: 0`, `background: transparent` y `border: 0` de la
base son RESET, no knobs: la variante `plain` es texto desnudo por
identidad.
<!-- veredicto:end -->

@ -729,6 +729,14 @@ Lo que `code-block` añadió (2026-08-21):
páginas y a los bloques de código de las fichas: no entra en el commit de un
componente. Decisión aparte.
**No es un caso aislado — la misma familia de reglas pisa también a `code`**
(medido 2026-08-21 al tokenizarlo, con CDP): `[data-uix-docs] code` declara
`font-family`, `font-size`, `padding` y `border-radius`, y gana (0,1,1)
contra la regla BASE del componente (0,1,0). Sus reglas de VARIANTE (0,2,0)
sí ganan, así que el efecto es selectivo y por eso sólo su `radius` lee
muerto — 3px del sitio donde el componente pide 4px. Cualquier decisión sobre
la regla del harness cubre los dos componentes.
- **⚠ El guard de claves duplicadas del handoff INSPECCIONA EL VACÍO.** El
comando documentado en `CONTINUE-theming.md` («Trampas que costaron un commit
cada una») es

@ -223,6 +223,18 @@ export const SENTINEL_EXCEPTIONS: Record<string, Record<string, string>> = {
'title-padding-block-end':
'static heading not mounted in the demo; forced → reaches (8 → 1234px)'
},
code: {
radius:
'the DEMO HARNESS wins: `[data-uix-docs] code` (uix.css, 0-1-1) beats the recipe base rule `[data-code]` (0-1-0) and sets border-radius/padding/font on the inline code — confirmed with CDP matched-rules. The variant rules (0,2,0) DO win, which is why only this base-rule token reads dead. Same harness rule that hides the <pre> of code-block (next-features §13)',
'soft-bg':
"the demo mounts the `plain` variant only (no chrome); forced variant='soft' → reaches (rgb(1,2,3))",
'border-width':
"the demo mounts `plain` only; forced variant='outline' → reaches (1 → 9px)",
border: "the demo mounts `plain` only; forced variant='outline' → reaches (rgb(4,5,6))",
'padding-inline':
"only the chromed variants pad; forced → reaches (4.12 → 77px), and the variant rule (0,2,0) beats the harness",
'padding-block': "only the chromed variants pad; forced → reaches (1.29 → 66px)"
},
'code-block': {
'pre-padding':
'the DEMO HARNESS outweighs the component: `[data-uix-docs] pre` (uix.css:801, 0-1-1) beats `[data-code-block-pre]` (0-1-0) and paints padding/border/radius/bg/font on the <pre>. Forced with !important → reaches (16px 20px → 12px). Seven properties measured, registered in next-features §13'

@ -65,6 +65,42 @@ Adaptaciones para eidos:
Code declara 0 eventos. Visual-only primitive — no estado interactivo.
## Talla y tema
**Guard R-5.4** (`npm run theming:sentinel -- code <url>`): 0/6 en vivo, los
seis adjudicados con su medición en
`scripts/theming-sentinel-exceptions.ts`. Corrido 2026-08-21.
Contrato en `lib/recipes/base.ts > code` (6 claves, fusionadas con el forward
de paleta). Alcance del censo: **100 %** (era 0 %).
**Seis de sus trece knobs son SISTEMA y no se acuñan.** `code` es el primitivo
tipográfico canónico: sus seis ejes de tipo consumen `--style-code-*` a través
de la escotilla por instancia (`var(--_code-{eje}, var(--style-code-{eje}))`),
que es la forma que D-TH.2-b fijó, y un primitivo no re-declara el vocabulario
de su capa. Lo que entra al contrato es el CROMO que añaden las variantes
`soft` y `outline`.
El padding es em-relativo a propósito —la píldora crece con el código que
envuelve— y lo declaran IGUAL las dos variantes con cromo: un knob por eje, no
cuatro. `--_code-color` se queda privado: es el conmutador de dos fuentes (la
tinta de contenido en línea y el forward de paleta cuando llega `data-color`),
el mismo patrón que el borde de foco de `textarea`. El `margin: 0` /
`padding: 0` / `background: transparent` / `border: 0` de la base es RESET: la
variante `plain` es texto desnudo por identidad.
### ⚠ En la demo, el harness del sitio le pisa el radio
Medido con CDP el 2026-08-21: `[data-uix-docs] code` (`web/routes/uix/uix.css`)
declara `font-family`, `font-size`, `padding` y `border-radius`, y gana
**(0,1,1)** contra la regla BASE de esta receta **(0,1,0)** — en la página de
documentación el radio del componente es el del sitio (3px) y no el suyo (4px).
Sus reglas de VARIANTE (0,2,0) sí ganan, así que el efecto es selectivo y por
eso sólo `radius` lee muerto. Es la misma regla del harness que tapa el `<pre>`
de `code-block`: cualquier decisión sobre ella cubre los dos
(`next-features.md` §13). Los otros cinco tokens callan porque la demo monta
sólo la variante `plain`, sin cromo.
## Gaps
| Gap | Disposición | Detalle |

@ -21,7 +21,7 @@
padding: 0;
background: transparent;
border: 0;
border-radius: var(--radius-sm);
border-radius: var(--code-radius);
vertical-align: baseline;
}
@ -35,13 +35,13 @@
}
[data-code][data-variant='soft'] {
background: var(--color-neutral-element);
padding-inline: 0.32em;
padding-block: 0.1em;
background: var(--code-soft-bg);
padding-inline: var(--code-padding-inline);
padding-block: var(--code-padding-block);
}
[data-code][data-variant='outline'] {
border: var(--border-width) solid var(--color-neutral-border);
padding-inline: 0.32em;
padding-block: 0.1em;
border: var(--code-border-width) solid var(--code-border);
padding-inline: var(--code-padding-inline);
padding-block: var(--code-padding-block);
}

@ -2121,6 +2121,12 @@
--link-external-icon-size: 0.85em;
--link-underline-width: 1px;
--link-underline-offset: 2px;
--code-radius: var(--radius-sm);
--code-soft-bg: var(--color-neutral-element);
--code-border-width: var(--border-width);
--code-border: var(--color-neutral-border);
--code-padding-inline: 0.32em;
--code-padding-block: 0.1em;
--proof-of-human-min-height-sm: calc(var(--size-sm-control-height) * 3);
--proof-of-human-min-height-md: calc(var(--size-md-control-height) * 3);
--proof-of-human-min-height-lg: calc(var(--size-lg-control-height) * 3);

@ -3084,6 +3084,18 @@ export const THEME_BASE_RECIPE_TOKENS = defineRecipes({
'_palette-text': { value: 'var(--color-primary-text)', scope: 'host' }
},
code: {
// The six TYPE axes are absent on purpose: they consume `--style-code-*`,
// the typographic layer, through the per-instance hatch. A primitive does
// not re-coin its layer's vocabulary (D-TH.2-b). What lives here is the
// chrome the `soft` / `outline` variants add on top.
radius: 'var(--radius-sm)',
'soft-bg': 'var(--color-neutral-element)',
'border-width': 'var(--border-width)',
border: 'var(--color-neutral-border)',
// em-relative on purpose: the pill grows with the code it wraps. Declared
// identically by both chromed variants, so one knob per axis.
'padding-inline': '0.32em',
'padding-block': '0.1em',
'_palette-text': { value: 'var(--color-primary-text)', scope: 'host' }
},
label: {

@ -7,8 +7,9 @@
} from '$uix/eidos/components/code';
import { compileMorfo } from '$uix/morfo';
import { codeMorfo } from '@/uix/morfo/components/code';
import TokensPanel from '../../lib/TokensPanel.svelte';
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'a11y';
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'tokens' | 'a11y';
type TraceEntry = { event: string; family: string; intent?: string; at: number };
let tab = $state<Tab>('live');
let trace = $state<TraceEntry[]>([]);
@ -148,6 +149,9 @@
<button data-uix-tab data-active={tab === 'recipe'} onclick={() => (tab = 'recipe')}
>Recipe</button
>
<button data-uix-tab data-active={tab === 'tokens'} onclick={() => (tab = 'tokens')}>
Tokens <span data-uix-tab-count>6</span>
</button>
<button data-uix-tab data-active={tab === 'a11y'} onclick={() => (tab = 'a11y')}>A11y</button>
</div>
@ -379,6 +383,10 @@
</section>
{/if}
{#if tab === 'tokens'}
<TokensPanel component="code" stage={stageRef ?? undefined} />
{/if}
{#if tab === 'a11y'}
<section data-uix-section>
<h2 data-uix-section-title>Accessibility</h2>

Loading…
Cancel
Save

Powered by TurnKey Linux.