uix(theming): cola pequeña — seis componentes, y una receta que TAPABA sus claves

`section` (cuatro claves resucitadas), `sticky` 0 → 100 %, `button-group` 40 →
100 %, `qr-code` 43 → 100 %, `toggle-group` 33 → 50 % (su techo) y `split-button`
adjudicado en 50 % (el suyo). Cero claves nuevas. Global 65 %, 25 componentes al
100 %, y los de alcance <20 % bajan de 14 a 12.

EL HALLAZGO ESTÁ EN `section`: LA RECETA TAPABA SUS PROPIAS CLAVES PÚBLICAS.
Declaraba los cuatro pasos de relleno en el host
(`[data-box][data-section] { --section-padding-block-sm: … }`), y esa declaración
gana a la que el contrato emite en `:root`: un tema que escribiera
`--section-padding-block-lg` perdía contra la receta. Borrado el bloque —mismos
valores, una sola fuente—, computado idéntico (48px antes y después), y las
cuatro claves alcanzan.

Lo demás es IDENTIDAD: once `100 %` entre las seis recetas y el centinela de 2px
del sticky —el hilo invisible que el observador vigila—, todos con su razón
escrita. Más un respaldo muerto retirado en `toggle-group`, sobre un token
PRESTADO de `toggle`: el velo de un grupo deshabilitado ES el del toggle, y se
tema en su ficha (§5.3-3).

Y UNA CLASE DE ADJUDICACIÓN NUEVA, QUE SE REPETIRÁ: UN PASO POR INSTANCIA.
`section` y `qr-code` mapean su prop de talla a `var(--{c}-{eje}-{k})` y la
escriben INLINE, así que sólo el paso ACTIVO alcanza; los otros leen muertos
hasta que la prop cambia. No es deuda ni ceguera del instrumento: es cómo
funciona una escala resuelta por prop.

Gates: los seis guards en verde · censo 65 % · `component:audit` PASS · suite
eidos con el rojo conocido ajeno · `rtl:check` 0 · `docs:check` 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
alpha-0.1-background
dev 2 months ago
parent 6d199133c7
commit 0c362cedd5

@ -10,9 +10,9 @@
- **Medido**: 2026-08-23 · **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**: **65%** — 2992 de 4618 knobs pasan por un token público del componente
- **Reparto**: público 2992 · privado 320 · global 858 · literal 448 · sistema transversal 509 · excepción firmada 94 _(los dos últimos, fuera del ratio)_
- **Sin token público propio**: 24 · **alcance < 20 %**: 14 · **alcance 100 %**: 22 · **con eje `size`**: 58
- **Alcance global**: **65%** — 2993 de 4608 knobs pasan por un token público del componente
- **Reparto**: público 2993 · privado 319 · global 858 · literal 438 · sistema transversal 509 · excepción firmada 104 _(los dos últimos, fuera del ratio)_
- **Sin token público propio**: 24 · **alcance < 20 %**: 12 · **alcance 100 %**: 25 · **con eje `size`**: 58
## Cómo se lee
@ -113,11 +113,9 @@ La columna «contrato» cuenta las claves **públicas** del bloque del component
| [field-langs](./field-langs.md) | 0% | 39 | 0 | 0 | 31 | 6 | 2 | 0 | – |
| [aspect-ratio](./aspect-ratio.md) | 0% | 3 | 0 | 0 | 1 | 2 | 0 | 0 | – |
| [date-picker](./date-picker.md) | 0% | 2 | 0 | 0 | 0 | 2 | 0 | 0 | – |
| [sticky](./sticky.md) | 0% | 2 | 0 | 1 | 0 | 1 | 0 | 1 | – |
| [text-blur](./text-blur.md) | 0% | 2 | 0 | 0 | 0 | 2 | 0 | 0 | – |
| [cascade](./cascade.md) | 0% | 1 | 0 | 0 | 0 | 1 | 0 | 0 | – |
| [motion](./motion.md) | 0% | 1 | 0 | 0 | 0 | 1 | 0 | 0 | – |
| [section](./section.md) | 0% | 1 | 0 | 0 | 0 | 1 | 0 | 4 | – |
| [chronos](./chronos.md) | 1% | 244 | 3 | 21 | 202 | 11 | 7 | 9 | y |
| [time-picker](./time-picker.md) | 18% | 59 | 10 | 12 | 24 | 11 | 2 | 16 | y |
| [proof-of-human](./proof-of-human.md) | 19% | 79 | 14 | 17 | 29 | 15 | 4 | 20 | – |
@ -126,18 +124,16 @@ La columna «contrato» cuenta las claves **públicas** del bloque del component
| [palabras](./palabras.md) | 22% | 468 | 103 | 1 | 272 | 87 | 5 | 0 | – |
| [surface](./surface.md) | 25% | 4 | 1 | 3 | 0 | 0 | 0 | 25 | – |
| [scroll-frames](./scroll-frames.md) | 33% | 9 | 3 | 1 | 1 | 4 | 0 | 4 | – |
| [toggle-group](./toggle-group.md) | 33% | 3 | 1 | 0 | 1 | 1 | 0 | 1 | – |
| [link](./link.md) | 36% | 14 | 4 | 4 | 1 | 2 | 3 | 5 | – |
| [audio-player](./audio-player.md) | 37% | 19 | 7 | 0 | 8 | 4 | 0 | 5 | – |
| [menu-dial](./menu-dial.md) | 38% | 8 | 3 | 0 | 1 | 4 | 0 | 3 | y |
| [mockup](./mockup.md) | 38% | 29 | 11 | 0 | 13 | 5 | 0 | 0 | – |
| [date-range-picker](./date-range-picker.md) | 40% | 53 | 20 | 9 | 19 | 2 | 3 | 22 | y |
| [button-group](./button-group.md) | 40% | 5 | 2 | 0 | 0 | 3 | 0 | 1 | – |
| [qr-code](./qr-code.md) | 43% | 7 | 3 | 0 | 0 | 4 | 0 | 10 | – |
| [navigation-menu](./navigation-menu.md) | 44% | 38 | 15 | 1 | 13 | 5 | 4 | 26 | y |
| [skeleton](./skeleton.md) | 45% | 11 | 5 | 5 | 0 | 1 | 0 | 9 | – |
| [text-scramble](./text-scramble.md) | 50% | 4 | 2 | 0 | 0 | 2 | 0 | 0 | – |
| [split-button](./split-button.md) | 50% | 2 | 1 | 0 | 1 | 0 | 0 | 1 | y |
| [toggle-group](./toggle-group.md) | 50% | 2 | 1 | 0 | 1 | 0 | 0 | 1 | – |
| [waveform](./waveform.md) | 55% | 11 | 6 | 0 | 1 | 4 | 0 | 10 | y |
| [sidebar](./sidebar.md) | 55% | 45 | 23 | 0 | 14 | 5 | 3 | 50 | – |
| [color-field](./color-field.md) | 56% | 16 | 9 | 0 | 2 | 5 | 0 | 22 | y |
@ -255,8 +251,11 @@ La columna «contrato» cuenta las claves **públicas** del bloque del component
| [display](./display.md) | — | 6 | 0 | 0 | 0 | 0 | 6 | 0 | – |
| [text-circular](./text-circular.md) | 100% | 6 | 6 | 0 | 0 | 0 | 0 | 6 | – |
| [barcode](./barcode.md) | 100% | 5 | 5 | 0 | 0 | 0 | 0 | 7 | – |
| [qr-code](./qr-code.md) | 100% | 3 | 3 | 0 | 0 | 0 | 0 | 10 | – |
| [button-group](./button-group.md) | 100% | 2 | 2 | 0 | 0 | 0 | 0 | 1 | – |
| [flex](./flex.md) | 100% | 2 | 2 | 0 | 0 | 0 | 0 | 7 | – |
| [grid](./grid.md) | 100% | 2 | 2 | 0 | 0 | 0 | 0 | 13 | – |
| [sticky](./sticky.md) | 100% | 1 | 1 | 0 | 0 | 0 | 0 | 1 | – |
| [alert-dialog](./alert-dialog.md) | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| [auto-grid](./auto-grid.md) | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| [container](./container.md) | — | 0 | 0 | 0 | 0 | 0 | 0 | 1 | – |
@ -267,6 +266,7 @@ La columna «contrato» cuenta las claves **públicas** del bloque del component
| [icon](./icon.md) | — | 0 | 0 | 0 | 0 | 0 | 0 | 2 | – |
| [mask-field](./mask-field.md) | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| [number-field](./number-field.md) | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| [section](./section.md) | — | 0 | 0 | 0 | 0 | 0 | 0 | 4 | – |
| [stack](./stack.md) | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |
| [wrap](./wrap.md) | — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | – |

@ -5,8 +5,8 @@
> Vista de conjunto: [README](./README.md) · método y protocolo:
> [`PLAN-theming.md`](../../process/PLAN-theming.md) §1, §2, §7.
- **Medido**: 2026-08-23 · **Alcance**: **40%** — 2 de 5 knobs por token público
- **Knobs de apariencia**: 5 — público 2 · privado 0 · global 0 · literal 3 · sistema 0 · excepción 0 _(los dos últimos, fuera del ratio)_
- **Medido**: 2026-08-23 · **Alcance**: **100%** — 2 de 2 knobs por token público
- **Knobs de apariencia**: 2 — público 2 · privado 0 · global 0 · literal 0 · sistema 0 · excepción 3 _(los dos últimos, fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 1 pública(s) — `divider-strength`
- **Eje `size`**: no · **ficheros**: `button-group.css`
@ -20,22 +20,22 @@ _Ninguno._
_Ninguno._
### 1.3 Literales (3)
### 1.3 Literales (0)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `button-group.css:31` | `[data-button-group][data-block]` | `inline-size` | `100%` |
| 2 | `button-group.css:47` | `[data-button-group][data-equal][data-orientation='vertical'] > [data-button]` | `inline-size` | `100%` |
| 3 | `button-group.css:51` | `[data-button-group][data-equal]:not([data-orientation='vertical'])` | `inline-size` | `100%` |
_Ninguno._
### 1.4 Excepciones firmadas (0) — fuera del ratio
### 1.4 Excepciones firmadas (3) — fuera del ratio
Literales que llevan su anotación `/* literal: <razón> */` en la propia
declaración: la válvula de recipe-contract §3, la misma que honra
`component-audit`. **Una desviación firmada no es deuda** — se listan para que la
razón se lea, no para acuñarlas.
_Ninguno._
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `button-group.css:31` | `[data-button-group][data-block]` | `inline-size` | `100%` |
| 2 | `button-group.css:47` | `[data-button-group][data-equal][data-orientation='vertical'] > [data-button]` | `inline-size` | `100%` |
| 3 | `button-group.css:51` | `[data-button-group][data-equal]:not([data-orientation='vertical'])` | `inline-size` | `100%` |
## 2. Sistema transversal (0) — informativo, fuera del ratio
Un tema los alcanza **a nivel de sistema**, por diseño (recipe-contract §2).
@ -77,6 +77,12 @@ no distingue lo que debería — se marca `⚠`.
<!-- veredicto:start -->
_(pendiente — lo escribe el autor; se conserva al regenerar)_
**EJECUTADO 2026-08-23 (cola pequeña) — 40 % → 100 %** sin acuñar nada: los tres
knobs fuera eran `inline-size: 100%` —la variante `block`, y las dos caras de
`equal`—, identidad pura, ahora con su razón escrita.
`divider-strength` leía quieto porque **la costura sólo se dibuja entre botones
ATTACHED de una variante con relleno** (solid/soft/ghost/plain) y la demo monta
`surface`; forzado, alcanza (alfa 0,18 → 0,9).
<!-- veredicto:end -->

@ -5,8 +5,8 @@
> Vista de conjunto: [README](./README.md) · método y protocolo:
> [`PLAN-theming.md`](../../process/PLAN-theming.md) §1, §2, §7.
- **Medido**: 2026-08-23 · **Alcance**: **43%** — 3 de 7 knobs por token público
- **Knobs de apariencia**: 7 — público 3 · privado 0 · global 0 · literal 4 · sistema 0 · excepción 0 _(los dos últimos, fuera del ratio)_
- **Medido**: 2026-08-23 · **Alcance**: **100%** — 3 de 3 knobs por token público
- **Knobs de apariencia**: 3 — público 3 · privado 0 · global 0 · literal 0 · sistema 0 · excepción 4 _(los dos últimos, fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 10 pública(s) — `fg`, `bg`, `size-xs`, `size-sm`, `size-md`, `size-lg`, `size-xl`, `radius`, `overlay-bg`, `overlay-radius`
- **Eje `size`**: no · **ficheros**: `qr-code.css`
@ -20,23 +20,23 @@ _Ninguno._
_Ninguno._
### 1.3 Literales (4)
### 1.3 Literales (0)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `qr-code.css:19` | `[data-qr-code-overlay]` | `width` | `100%` |
| 2 | `qr-code.css:20` | `[data-qr-code-overlay]` | `height` | `100%` |
| 3 | `qr-code.css:33` | `[data-qr-code-overlay] :where(img, svg)` | `width` | `100%` |
| 4 | `qr-code.css:34` | `[data-qr-code-overlay] :where(img, svg)` | `height` | `100%` |
_Ninguno._
### 1.4 Excepciones firmadas (0) — fuera del ratio
### 1.4 Excepciones firmadas (4) — fuera del ratio
Literales que llevan su anotación `/* literal: <razón> */` en la propia
declaración: la válvula de recipe-contract §3, la misma que honra
`component-audit`. **Una desviación firmada no es deuda** — se listan para que la
razón se lea, no para acuñarlas.
_Ninguno._
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `qr-code.css:19` | `[data-qr-code-overlay]` | `width` | `100%` |
| 2 | `qr-code.css:20` | `[data-qr-code-overlay]` | `height` | `100%` |
| 3 | `qr-code.css:33` | `[data-qr-code-overlay] :where(img, svg)` | `width` | `100%` |
| 4 | `qr-code.css:34` | `[data-qr-code-overlay] :where(img, svg)` | `height` | `100%` |
## 2. Sistema transversal (0) — informativo, fuera del ratio
Un tema los alcanza **a nivel de sistema**, por diseño (recipe-contract §2).
@ -77,6 +77,14 @@ no distingue lo que debería — se marca `⚠`.
<!-- veredicto:start -->
_(pendiente — lo escribe el autor; se conserva al regenerar)_
**EJECUTADO 2026-08-23 (cola pequeña) — 43 % → 100 %** sin acuñar nada: los
cuatro knobs fuera eran `100 %` —la superposición y su imagen llenan el módulo—,
identidad con su razón escrita.
Sus seis rojos del guard son de MEDIDA, no de contrato: `fg` y `bg` se pintan
como `fill` del `<path>` y del `<rect>` del SVG —no como `color` y `background`
del nodo raíz—, y las cinco tallas son **un paso por instancia**: el wrapper
escribe `var(--qr-code-size-{k})` inline desde la prop y la demo corre `lg`.
Medidos en el nodo correcto y forzando el paso, todos alcanzan.
<!-- veredicto:end -->

@ -5,8 +5,8 @@
> Vista de conjunto: [README](./README.md) · método y protocolo:
> [`PLAN-theming.md`](../../process/PLAN-theming.md) §1, §2, §7.
- **Medido**: 2026-08-23 · **Alcance**: **0%** — 0 de 1 knobs por token público
- **Knobs de apariencia**: 1 — público 0 · privado 0 · global 0 · literal 1 · sistema 0 · excepción 0 _(los dos últimos, fuera del ratio)_
- **Medido**: 2026-08-23 · **Alcance**: **—** — 0 de 0 knobs por token público
- **Knobs de apariencia**: 0 — público 0 · privado 0 · global 0 · literal 0 · sistema 0 · excepción 1 _(los dos últimos, fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 4 pública(s) — `padding-block-sm`, `padding-block-md`, `padding-block-lg`, `padding-block-xl`
- **Eje `size`**: no · **ficheros**: `section.css`
@ -20,20 +20,20 @@ _Ninguno._
_Ninguno._
### 1.3 Literales (1)
### 1.3 Literales (0)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `section.css:19` | `[data-box][data-section]` | `inline-size` | `100%` |
_Ninguno._
### 1.4 Excepciones firmadas (0) — fuera del ratio
### 1.4 Excepciones firmadas (1) — fuera del ratio
Literales que llevan su anotación `/* literal: <razón> */` en la propia
declaración: la válvula de recipe-contract §3, la misma que honra
`component-audit`. **Una desviación firmada no es deuda** — se listan para que la
razón se lea, no para acuñarlas.
_Ninguno._
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `section.css:18` | `[data-box][data-section]` | `inline-size` | `100%` |
## 2. Sistema transversal (0) — informativo, fuera del ratio
Un tema los alcanza **a nivel de sistema**, por diseño (recipe-contract §2).
@ -46,6 +46,8 @@ _La receta no declara privados propios en su CSS._
## 4. Propuesta de corrección
- **Sin knobs de apariencia**: el visual vive en un componente compuesto o en una capa compartida. «Declarado y nunca pintado» no es deuda por sí solo (architecture/eidos.md, columna `unused`).
### 4.1 Tokens a declarar en `lib/recipes/base.ts` (0)
Valor **verbatim** del CSS de hoy: el default no se mueve, sólo cambia quién
@ -73,6 +75,19 @@ no distingue lo que debería — se marca `⚠`.
<!-- veredicto:start -->
_(pendiente — lo escribe el autor; se conserva al regenerar)_
**EJECUTADO 2026-08-23 (cola pequeña) — sin knobs que medir, y CUATRO claves
resucitadas.** La receta declaraba sus propios pasos de relleno EN EL HOST
(`[data-box][data-section]`), y esa declaración **tapaba las cuatro públicas que
el contrato ya emite en `:root`**: un tema que escribiera `--section-padding-block-lg`
perdía contra la receta. Borrado el bloque —mismos valores, una sola fuente— y el
computado no se mueve (48px antes y después).
El `inline-size: 100%` pasa a excepción por identidad: una sección ES el ancho de
su caja.
Los tres pasos que el guard sigue viendo quietos son **un paso por instancia**:
el wrapper mapea la prop `padding` a `var(--section-padding-block-{k})` y lo
escribe INLINE, así que sólo el activo alcanza. Forzando el paso, alcanza
(96px → 123px).
<!-- veredicto:end -->

@ -77,6 +77,9 @@ no distingue lo que debería — se marca `⚠`.
<!-- veredicto:start -->
_(pendiente — lo escribe el autor; se conserva al regenerar)_
**ADJUDICADO 2026-08-23 (cola pequeña) — el 50 % es su techo.** Compone
ButtonGroup + Button + DropdownMenu y sólo posee el relleno del rabillo `▾`; el
knob que queda fuera lee `--dropdown-menu-content-min-width`, **un público
PRESTADO de su menú**, que se tema en la ficha del dueño. Nada que acuñar.
<!-- veredicto:end -->

@ -5,8 +5,8 @@
> Vista de conjunto: [README](./README.md) · método y protocolo:
> [`PLAN-theming.md`](../../process/PLAN-theming.md) §1, §2, §7.
- **Medido**: 2026-08-23 · **Alcance**: **0%** — 0 de 2 knobs por token público
- **Knobs de apariencia**: 2 — público 0 · privado 1 · global 0 · literal 1 · sistema 0 · excepción 0 _(los dos últimos, fuera del ratio)_
- **Medido**: 2026-08-23 · **Alcance**: **100%** — 1 de 1 knobs por token público
- **Knobs de apariencia**: 1 — público 1 · privado 0 · global 0 · literal 0 · sistema 0 · excepción 1 _(los dos últimos, fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 1 pública(s) — `z-index`
- **Eje `size`**: no · **ficheros**: `sticky.css`
@ -16,26 +16,24 @@
_Ninguno._
### 1.2 A través de un privado (1)
### 1.2 A través de un privado (0)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `sticky.css:44` | `[data-sticky-sentinel]` | `block-size` | `var(--_sticky-sentinel-size)` |
_Ninguno._
### 1.3 Literales (1)
### 1.3 Literales (0)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `sticky.css:45` | `[data-sticky-sentinel]` | `inline-size` | `100%` |
_Ninguno._
### 1.4 Excepciones firmadas (0) — fuera del ratio
### 1.4 Excepciones firmadas (1) — fuera del ratio
Literales que llevan su anotación `/* literal: <razón> */` en la propia
declaración: la válvula de recipe-contract §3, la misma que honra
`component-audit`. **Una desviación firmada no es deuda** — se listan para que la
razón se lea, no para acuñarlas.
_Ninguno._
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `sticky.css:45` | `[data-sticky-sentinel]` | `inline-size` | `100%` |
## 2. Sistema transversal (0) — informativo, fuera del ratio
Un tema los alcanza **a nivel de sistema**, por diseño (recipe-contract §2).
@ -46,13 +44,13 @@ _Ninguno._
| privado | declaraciones | valor(es) | origen | ¿deriva de un público? |
| --- | ---: | --- | --- | :-: |
| `--_sticky-sentinel-size` | 1 | `2px` | literal | no |
| `--_sticky-sentinel-size` | 1 | `2px` | exception | no |
Consumidos y **no declarados en el CSS** (vienen de `base.ts` o de un estilo inline del wrapper): `--_sticky-offset`.
## 4. Propuesta de corrección
### 4.1 Tokens a declarar en `lib/recipes/base.ts` (1)
### 4.1 Tokens a declarar en `lib/recipes/base.ts` (0)
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
@ -62,16 +60,11 @@ no distingue lo que debería — se marca `⚠`.
| token (`--sticky-…`) | scope TSC | valor propuesto | usos |
| --- | --- | --- | ---: |
| `sentinel-height` | `root` | `2px` | 1 |
### 4.2 Sin nombre mecánico (1)
- **⚠ decisión: `100%` es un valor identidad o geometría de layout, no un knob de tema — el perímetro de «knob» es D-TH.2, sin firmar** — 1: `inline-size`.
### 4.3 Avisos sobre los tokens propuestos (1)
- **el privado `--_sticky-sentinel-size` debe pasar a leer este público (o desaparecer)** — `--sticky-sentinel-height`
### 4.4 Lo que hay que comprobar a mano (PLAN-theming §1.3 · §7.4)
- [ ] **Privado que no deriva de un público** — §3 lo marca; el privado debe leer el público o desaparecer.
@ -84,6 +77,9 @@ no distingue lo que debería — se marca `⚠`.
<!-- veredicto:start -->
_(pendiente — lo escribe el autor; se conserva al regenerar)_
**EJECUTADO 2026-08-23 (cola pequeña) — 0 % → 100 %** sin acuñar nada: sus dos
knobs eran identidad. El centinela del sticky (`--_sticky-sentinel-size: 2px`) es
el hilo invisible que el observador vigila —ni se ve ni se tematiza— y el
`inline-size: 100%` es el ancho de su caja. Los dos con su razón escrita.
<!-- veredicto:end -->

@ -5,8 +5,8 @@
> Vista de conjunto: [README](./README.md) · método y protocolo:
> [`PLAN-theming.md`](../../process/PLAN-theming.md) §1, §2, §7.
- **Medido**: 2026-08-23 · **Alcance**: **33%** — 1 de 3 knobs por token público
- **Knobs de apariencia**: 3 — público 1 · privado 0 · global 1 · literal 1 · sistema 0 · excepción 0 _(los dos últimos, fuera del ratio)_
- **Medido**: 2026-08-23 · **Alcance**: **50%** — 1 de 2 knobs por token público
- **Knobs de apariencia**: 2 — público 1 · privado 0 · global 1 · literal 0 · sistema 0 · excepción 1 _(los dos últimos, fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 1 pública(s) — `gap`
- **Eje `size`**: no · **ficheros**: `toggle-group.css`
@ -16,26 +16,26 @@
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `toggle-group.css:101` | `[data-toggle-group][data-disabled]` | `opacity` | `var(--toggle-disabled-opacity, 0.55)` ⤴ prestado de `toggle` |
| 1 | `toggle-group.css:103` | `[data-toggle-group][data-disabled]` | `opacity` | `var(--toggle-disabled-opacity)` ⤴ prestado de `toggle` |
### 1.2 A través de un privado (0)
_Ninguno._
### 1.3 Literales (1)
### 1.3 Literales (0)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `toggle-group.css:32` | `[data-toggle-group][data-block]` | `inline-size` | `100%` |
_Ninguno._
### 1.4 Excepciones firmadas (0) — fuera del ratio
### 1.4 Excepciones firmadas (1) — fuera del ratio
Literales que llevan su anotación `/* literal: <razón> */` en la propia
declaración: la válvula de recipe-contract §3, la misma que honra
`component-audit`. **Una desviación firmada no es deuda** — se listan para que la
razón se lea, no para acuñarlas.
_Ninguno._
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `toggle-group.css:32` | `[data-toggle-group][data-block]` | `inline-size` | `100%` |
## 2. Sistema transversal (0) — informativo, fuera del ratio
Un tema los alcanza **a nivel de sistema**, por diseño (recipe-contract §2).
@ -60,7 +60,7 @@ no distingue lo que debería — se marca `⚠`.
| token (`--toggle-group-…`) | scope TSC | valor propuesto | usos |
| --- | --- | --- | ---: |
| `disabled-opacity` | `root` | `var(--toggle-disabled-opacity, 0.55)` | 1 |
| `disabled-opacity` | `root` | `var(--toggle-disabled-opacity)` | 1 |
### 4.2 Sin nombre mecánico (1)
@ -78,6 +78,17 @@ no distingue lo que debería — se marca `⚠`.
<!-- veredicto:start -->
_(pendiente — lo escribe el autor; se conserva al regenerar)_
**EJECUTADO 2026-08-23 (cola pequeña) — 33 % → 50 %**, que es su techo. Un
literal anotado (`inline-size: 100%` de la variante `block`) y un respaldo muerto
retirado.
El knob que queda fuera es **un token PRESTADO de `toggle`**
(`--toggle-disabled-opacity`): el velo de un grupo de toggles deshabilitado ES el
del toggle, y se tema en SU ficha (recipe-contract §5.3-3). Llevaba además un
respaldo `0.55` que no puede ocurrir —la clave está en el contrato del dueño— y
que sólo servía para envejecer contra él.
`gap` leía quieto porque el grupo de la demo es ATTACHED (segmentado), donde el
hueco colapsa a 0; sin `data-attached` alcanza (4px → 123px).
<!-- veredicto:end -->

@ -455,6 +455,35 @@ de commit. Sin artefacto, el paso no se ha hecho.
## 8. Registro
- 2026-08-23 — **COLA PEQUEÑA: seis componentes, cero claves nuevas.** `section`
(— · cuatro claves RESUCITADAS), `sticky` 0 → **100 %**, `button-group` 40 →
**100 %**, `qr-code` 43 → **100 %**, `toggle-group` 33 → **50 %** (su techo) y
`split-button` ADJUDICADO en 50 % (su techo). Global 65 %; **25 componentes al
100 %** y los de alcance <20 % bajan de 14 a 12.
**El hallazgo está en `section`: la receta TAPABA sus propias claves públicas.**
Declaraba los cuatro pasos de relleno en el host
(`[data-box][data-section] { --section-padding-block-sm: … }`), y esa
declaración gana a la que el contrato emite en `:root`: un tema que escribiera
`--section-padding-block-lg` perdía contra la receta. Borrado el bloque —mismos
valores, una sola fuente—, computado idéntico (48px antes y después), y las
cuatro claves alcanzan.
Lo demás es **identidad**: once `100 %` entre las seis recetas y el centinela
de 2px del sticky —el hilo invisible que el observador vigila—, todos con su
razón escrita. Más un respaldo muerto retirado en `toggle-group`, sobre un
token PRESTADO de `toggle` (el velo de un grupo deshabilitado es el del
toggle, y se tema en su ficha).
**Y una clase de adjudicación nueva, que se repetirá: UN PASO POR INSTANCIA.**
`section` y `qr-code` mapean su prop de talla a `var(--{c}-{eje}-{k})` y la
escriben INLINE, así que sólo el paso ACTIVO alcanza; los otros cuatro leen
muertos hasta que la prop cambia. No es deuda ni ceguera del instrumento: es
cómo funciona una escala resuelta por prop.
Gates: seis guards en verde · censo · audit PASS · suite con el rojo conocido
ajeno · rtl 0 · docs 0.
- 2026-08-23 — **`tooltip` 52 % → 75 %** (3 claves, 4 literales anotados) **y el
plano `overlay` ANULA SUS TRES VARIANTES**. Global 65 %.

@ -632,6 +632,29 @@ export const SENTINEL_EXCEPTIONS: Record<string, Record<string, string>> = {
'outline-arrow-bg': 'the arrow is painted through a SIBLING selector that needs the content carrying data-variant=outline AND the panel open; measured with the demo switch -> reaches (oklch(0.9911 0 0) -> rgb(1,2,3))',
'outline-arrow-border': 'same sibling rule; measured -> reaches (oklch(0.7921 0 0) -> rgb(1,2,3))'
},
section: {
// ONE STEP PER INSTANCE: the wrapper maps the `padding` prop to
// var(--section-padding-block-{k}) and writes it INLINE, so only the active
// step is reachable at a time. Forcing another step reaches (2026-08-23).
'padding-block-sm': 'one step per instance; the demo runs md. Forced the step inline -> reaches',
'padding-block-lg': 'same, one step per instance',
'padding-block-xl': 'same; forced -> reaches (96px -> 123px)'
},
'qr-code': {
'size-xs': 'one step per instance: the wrapper writes var(--qr-code-size-{k}) inline from the size prop and the demo runs lg. Forced -> reaches (96px -> 123px)',
'size-sm': 'same, one step per instance',
'size-md': 'same, one step per instance',
'size-xl': 'same, one step per instance',
'fg': 'painted as the SVG fill of the module path, not as color on the root; read on the path -> reaches (rgb(24,24,27) -> rgb(1,2,3))',
'bg': 'painted as the fill of the backing rect, not as background on the root; read on the rect -> reaches (white -> rgb(1,2,3))'
},
'toggle-group': {
gap: 'the demo group is ATTACHED (segmented), where the gap collapses to 0; removed data-attached -> reaches (4px -> 123px)'
},
'button-group': {
'divider-strength':
'the seam only draws between ATTACHED buttons of a filled variant (solid/soft/ghost/plain) and the demo runs surface; forced -> reaches (0.18 -> 0.9 alpha)'
},
button: {
// Measured one by one 2026-08-23. The icon scale needs a real <svg> child,
// the radius scale is an OPT-IN prop the demo does not set, and the four

@ -28,7 +28,7 @@
}
[data-button-group][data-block] {
inline-size: 100%;
inline-size: 100%; /* literal: fills its box — identity, not a knob */
}
/* `equal` — every button the same width. Pure CSS can only equalise on the
@ -44,11 +44,11 @@
}
[data-button-group][data-equal][data-orientation='vertical'] > [data-button] {
inline-size: 100%;
inline-size: 100%; /* literal: fills its box — identity, not a knob */
}
[data-button-group][data-equal]:not([data-orientation='vertical']) {
inline-size: 100%;
inline-size: 100%; /* literal: fills its box — identity, not a knob */
}
[data-button-group][data-equal]:not([data-orientation='vertical']) > [data-button] {

@ -16,8 +16,8 @@
* for HTML inside a viewBox-scaled foreignObject. */
[data-qr-code-overlay] {
box-sizing: border-box;
width: 100%;
height: 100%;
width: 100%; /* literal: fills its box — identity, not a knob */
height: 100%; /* literal: fills its box — identity, not a knob */
display: flex;
align-items: center;
justify-content: center;
@ -30,7 +30,7 @@
}
[data-qr-code-overlay] :where(img, svg) {
width: 100%;
height: 100%;
width: 100%; /* literal: fills its box — identity, not a knob */
height: 100%; /* literal: fills its box — identity, not a knob */
object-fit: contain;
}

@ -11,10 +11,9 @@
*/
[data-box][data-section] {
--section-padding-block-sm: var(--space-8);
--section-padding-block-md: var(--space-12);
--section-padding-block-lg: var(--space-16);
--section-padding-block-xl: calc(var(--space-16) * 1.5);
inline-size: 100%;
/* The four steps live in the CONTRACT (recipes.section.padding-block-*) and are
emitted at :root. Re-declaring them here shadowed them: a theme writing the
public token lost against this block, so the four keys were dead (measured
2026-08-23). Same values, one source. */
inline-size: 100%; /* literal: fills its box — identity, not a knob */
}

@ -40,9 +40,9 @@
* box because both derive from the same soma offset.
*/
[data-sticky-sentinel] {
--_sticky-sentinel-size: 2px;
--_sticky-sentinel-size: 2px; /* literal: the invisible tripwire the observer watches — identity, not a knob */
block-size: var(--_sticky-sentinel-size);
inline-size: 100%;
inline-size: 100%; /* literal: fills its box — identity, not a knob */
pointer-events: none;
visibility: hidden;
}

@ -29,7 +29,7 @@
[data-toggle-group][data-block] {
display: flex;
inline-size: 100%;
inline-size: 100%; /* literal: fills its box — identity, not a knob */
}
[data-toggle-group][data-block] [data-toggle-group-item] {
@ -98,7 +98,9 @@
* onto each item, so Toggle's `[data-toggle][data-disabled]` rule paints
* each one; this rule disables interaction at the container. */
[data-toggle-group][data-disabled] {
opacity: var(--toggle-disabled-opacity, 0.55);
/* Borrowed on purpose: the disabled veil of a group of toggles IS the
toggle’s, and it is themed in ITS sheet (recipe-contract 5.3-3). */
opacity: var(--toggle-disabled-opacity);
pointer-events: none;
}

Loading…
Cancel
Save

Powered by TurnKey Linux.