3 claves en una entrada NUEVA de `recipes/base.ts`. Censo 0 % → 25 %.
**La receta posee sólo el cromo del dial**, como dice su propia cabecera: las
acciones son `Button` compuestos y la colocación flotante es de la capa
`viewport-placement`. Lo suyo son tres cosas: el hueco del abanico, el paso del
escalonado de entrada y el tinte del velo.
**`--_viewport-placement-offset` y `-z` NO se acuñan, y ésa es la decisión del
commit.** Son las RANURAS DE OVERRIDE que la capa compartida publica, y esta
receta escribe en ellas su decisión — 20px en vez de los 16px por defecto de la
capa, y el comentario del CSS deja claro que es decisión y no deriva. Acuñar
`menu-dial-placement-offset` sería el vocabulario paralelo que la regla 2 de
capas compartidas prohíbe: el eje es de la capa, y un tema lo mueve ahí. Es el
mismo techo que `listbox` paga por consumir bien `list-surface` — el grueso del
75 % que no alcanza, y es correcto que no lo haga (§13).
**El escalonado es tema, no motion**: `stagger-step` no es una duración de
animación sino el INTERVALO entre hermanos (multiplica el índice de cada
acción), y es lo que da carácter al abanico. Por eso entra al contrato.
Artefactos y gates:
- Sonda antes/después = 0 diffs en 1.856 valores × 8 estados.
- R-5.4: 1/3, los dos mudos adjudicados con su medición — el velo sólo existe
con el dial abierto (0 nodos en reposo, contados) y el escalonado sólo se
aplica bajo `[data-stagger]` con la lista abierta; forzando ambos atributos
con índice 3, alcanza (0.084s → 2.331s = 3 × 777ms).
- censo --only 25 % · component:audit PASS · suite eidos sin rojos nuevos ·
rtl:check 0 · docs:check 0 · check por fichero limpio.
- README con «Talla y tema» y tab `Tokens` en la demo (3 claves).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| `--_menu-dial-radius` | 1 | `max(var(--_dial-r-clear), var(--_dial-r-spread))` | global | no |
@ -59,7 +57,7 @@ Consumidos y **no declarados en el CSS** (vienen de `base.ts` o de un estilo inl
- **Consume la capa compartida `viewport-placement`.** Un eje que la capa posee se consume como `var(--_x, var(--x))`; el consumidor **no acuña**`--menu-dial-{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` (4)
### 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
@ -72,16 +70,11 @@ no distingue lo que debería — se marca `⚠`.
- **⚠ decisión: el privado que alimenta este knob no se declara en el CSS (viene de `base.ts` o de un estilo inline) — hay que resolverlo antes de nombrarlo** — 2: `gap`.
- **⚠ decisión: `1` 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: `opacity`.
### 4.3 Avisos sobre los tokens propuestos (1)
- **el privado `--_menu-dial-backdrop-bg` debe pasar a leer este público (o desaparecer)** — `--menu-dial-backdrop-bg`
- **ya alcanzable: el privado `--_menu-dial-backdrop-bg` deriva de un público (`var(--menu-dial-backdrop-bg)`) — sin acción; el censo lo cuenta como no alcanzable por el límite del regex** — 1: `background`.
### 4.4 Lo que hay que comprobar a mano (PLAN-theming §1.3 · §7.4)
@ -95,6 +88,24 @@ no distingue lo que debería — se marca `⚠`.
<!-- veredicto:start -->
_(pendiente — lo escribe el autor; se conserva al regenerar)_
**Medido 2026-08-22.** Su propia cabecera acota el encargo: la receta «owns
only the dial-specific chrome». Las acciones son `Button` compuestos y la
colocación flotante es de la capa `viewport-placement`, así que lo suyo son
tres cosas.
1. **Tres knobs propios**: el hueco del abanico, el paso del escalonado de
entrada (`28ms`, que multiplica el índice de cada acción) y el tinte del
velo de fondo.
2. **`--_viewport-placement-offset` y `-z` NO se acuñan.** Son las RANURAS DE
OVERRIDE que la capa compartida publica, y el componente escribe en ellas su
decisión (20px en vez de los 16px por defecto de la capa; el comentario de
la receta deja claro que es decisión, no deriva). Acuñar
`menu-dial-placement-offset` sería el vocabulario paralelo que la regla 2 de
capas compartidas prohíbe: el eje es de la capa, y un tema lo mueve ahí. Es
el mismo techo que `listbox` paga por consumir bien `list-surface`
(`next-features.md` §13).
3. **El escalonado es tema, no motion**: `--_menu-dial-stagger-step` no es una
duración de animación sino el INTERVALO entre hermanos, que define el
'same structural conflict: the hover rule sets `text-decoration-color: currentColor` over the resting mix, and the panel cannot be opened without hovering'
},
'menu-dial':{
'backdrop-bg':
'the backdrop only exists while the dial is open (0 nodes at rest, counted); forced → reaches (rgb(1,2,3))',
'stagger-step':
"consumed only by `[data-stagger] [data-state='open'] .menu-dial-action`, and the demo dial is closed and un-staggered; forced both attrs with index 3 → reaches (0.084s → 2.331s = 3 × 777ms)"
},
'picker-shell':{
'section-gap-xs':
'its size axis is `data-picker-size` on the PORTALED [data-popover-content], not `data-size` on itself, and the guard only cycles `data-size`; the demo popover sits at md. Cycled by hand across the four picker-sizes 2026-08-21 → reaches (section/row/footer spacing follows the picker size)',