31 claves en una entrada NUEVA de `recipes/base.ts`. Censo 0 % → 76 %. Global
48 %, sin contrato 43 → 42.
La cola avisaba «es CAPA de los pickers: mirar antes qué posee y qué presta».
Mirarlo cambió tres cosas del encargo:
1. **NO es una capa compartida: es un COMPONENTE.** A diferencia de
list-surface o calendar-surface no hay hook de capa ni vocabulario prestado
— color-picker, date-picker y chronos importan sus `.svelte` directamente.
No toca `LAYER_VOCABULARY` y el censo ya lo puntuaba bien.
2. **Su eje de talla no es `data-size`, es `data-picker-size` sobre
`[data-popover-content]`**, un ancestro PORTALADO. El TSC no puede emitir esa
cascada (`scope: 'size:xs'` generaría `[data-picker-shell][data-size='xs']`,
que no casa nunca), así que las coordenadas por talla quedan como públicos
PLANOS y la receta las consume desde sus propios selectores. Es el único
componente del eje donde el patrón de nombres resueltos no aplica; la regla
base es el default `sm` y `md` sólo re-declara lo que cambia. La fila de
tiempo lleva un TERCER eje, `data-size` sobre su propia clase.
3. **El instrumental del eje estaba CIEGO aquí, y la sonda genérica devolvió 0
nodos** — el antipatrón de comparar cero valores y pasar. Dos causas a la
vez: `picker-shell` no tiene demo propia (404, se mide dentro de un picker
anfitrión y detrás de su popover), y sus partes se llaman
`data-picker-header` / `-body` / `-footer` en vez de
`data-picker-shell-{parte}` — genéricas A PROPÓSITO, dice la receta, «so
every picker gets the same visual contract for free» —, así que todo filtro
por prefijo del componente ve sólo la raíz.
CÓMO SE VERIFICÓ, ya que el instrumento estándar no servía:
- Sonda DIRIGIDA (abre el popover del date-picker, captura los seis nodos
reales en las cuatro picker-sizes): determinismo comprobado (576 valores, 0
diffs entre dos corridas del mismo código) y **gate antes/después = 0 diffs**.
- Los knobs que la demo no monta —el header entero y la fila de tiempo— se
midieron FORZANDO el nodo: los quince mueven su propiedad, y el font-size del
header se comprobó talla a talla (12/14/16/16px → 102px, cada uno en su
picker-size).
- El guard R-5.4 pasó de **0/31 a 6/31** al ganar un `COMPONENT_OVERRIDES`
(prefijo de atributo + selector de apertura) acotado a este componente.
Verificado que los otros CATORCE con ledger no se mueven ni una cifra.
Adjudicar 31 excepciones sin ese arreglo habría sido escribir una mentira en
el ledger; los 25 que siguen mudos llevan cada uno su razón medida.
Header y footer comparten la regla que los separa del cuerpo → UN par de knobs
(`border-width` + `border`), no dos.
Sin tab `Tokens`: este componente no tiene página donde ponerlo. Registrado en
next-features §13 junto con la pregunta de fondo — si el precio de los nombres
genéricos (un contrato visual común para todos los pickers) compensa la ceguera
del instrumental — y con el eje portalado como caso de prueba si el TSC llega a
ganar un scope configurable.
Gates: censo --only 76 % · suite eidos sin rojos nuevos (el conocido
skin-media-player) · rtl:check 0 · docs:check 0 · check por fichero limpio ·
`component:audit` no lo cubre (eidos-only, sin morfo) y `eidos-lint` tampoco.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- **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).
- **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` (7)
### 4.1 Tokens a declarar en `lib/recipes/base.ts` (1)
Valor **verbatim** del CSS de hoy: el default no se mueve, sólo cambia quién
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
puede moverlo. Nombres derivados de recipe-contract §1 (ejes lógicos, talla
@ -75,24 +59,13 @@ no distingue lo que debería — se marca `⚠`.
- **⚠ decisión: `border-block-end` es shorthand o eje físico — hay que partirlo en ejes lógicos antes de nombrarlo (recipe-contract §1, R-4.4)** — 1: `border-block-end`.
- **ya alcanzable: el privado `--_picker-shell-section-gap` deriva de un público (`var(--picker-shell-section-gap-sm)`, `var(--picker-shell-section-gap-xs)`, `var(--picker-shell-section-gap-md)`, `var(--picker-shell-section-gap-lg)`) — sin acción; el censo lo cuenta como no alcanzable por el límite del regex** — 1: `gap`.
- **⚠ decisión: `border-block-start` es shorthand o eje físico — hay que partirlo en ejes lógicos antes de nombrarlo (recipe-contract §1, R-4.4)** — 2: `border-block-start`.
- **ya alcanzable: el privado `--_picker-shell-row-spacing` deriva de un público (`var(--picker-shell-row-spacing-sm)`, `var(--picker-shell-row-spacing-xs)`, `var(--picker-shell-row-spacing-lg)`) — sin acción; el censo lo cuenta como no alcanzable por el límite del regex** — 2: `padding-block-end`, `padding-block-start`.
- **ya alcanzable: el privado `--_picker-shell-gap` deriva de un público (`var(--picker-shell-footer-gap-sm)`, `var(--picker-shell-footer-gap-xs)`, `var(--picker-shell-footer-gap-lg)`) — sin acción; el censo lo cuenta como no alcanzable por el límite del regex** — 1: `gap`.
### 4.3 Avisos sobre los tokens propuestos (3)
- **el privado `--_picker-shell-section-gap` debe pasar a leer este público (o desaparecer)** — `--picker-shell-gap`
- **el privado `--_picker-shell-row-spacing` debe pasar a leer este público (o desaparecer)** — `--picker-shell-padding-block`
- **el privado `--_picker-shell-gap` debe pasar a leer este público (o desaparecer)** — `--picker-shell-gap`
### 4.4 Lo que hay que comprobar a mano (PLAN-theming §1.3 · §7.4)
### 4.4 Lo que hay que comprobar a mano (PLAN-theming §1.3 · §7.4)
@ -106,6 +79,44 @@ no distingue lo que debería — se marca `⚠`.
<!-- veredicto:start -->
<!-- veredicto:start -->
_(pendiente — lo escribe el autor; se conserva al regenerar)_
**Medido 2026-08-21.** La cola avisaba «es CAPA de los pickers: mirar antes qué
posee y qué presta», y mirarlo cambió tres cosas del encargo.
1. **NO es una capa compartida al estilo `list-surface` / `calendar-surface`:
es un COMPONENTE** cuyos `.svelte` importan directamente color-picker,
date-picker y chronos (`import PickerShellRoot from
'../picker-shell/picker-shell.svelte'`). No hay hook de capa ni vocabulario
prestado, así que **no toca `LAYER_VOCABULARY`** y el censo lo puntúa bien.
Se tokeniza como un componente normal, y sus consumidores lo COMPONEN.
2. **Su eje de talla NO es `data-size`: es `data-picker-size` sobre
`[data-popover-content]`**, un ancestro PORTALADO. Los selectores son
`[data-popover-content][data-picker-size='X'] [data-picker-shell]`, y el
TSC no puede emitirlos (su `scope: 'size:xs'` genera
`[data-picker-shell][data-size='xs']`, que no existe). **Consecuencia: las
coordenadas por talla se declaran como públicos PLANOS y la receta las
consume desde sus propios selectores** — el único componente del eje donde
el patrón de nombres resueltos del TSC no aplica. El default de la regla
base es `sm`, y `md` sólo re-declara lo que cambia.
3. **El instrumento del eje NO SIRVE aquí, y por dos razones a la vez.** La
sonda genérica dio **0 nodos** (el antipatrón registrado: comparar cero
valores y pasar): no sabe abrir el popover de un picker, y filtra por
`data-picker-shell*` cuando las partes se llaman `data-picker-header` /
'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)',
'section-gap-sm':
'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)',
'section-gap-lg':
'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)',
'row-spacing-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)',
'row-spacing-lg':
'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)',
'footer-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)',
'footer-gap-lg':
'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)',
'header-font-size-xs':
'the date-picker demo renders no [data-picker-header] (0 nodes, counted) — picker-shell has no demo route of its own (404) and is measured inside a host picker; forced → reaches (12/14/16/16px → 102px, each in its own picker-size)',
'header-font-size-sm':
'the date-picker demo renders no [data-picker-header] (0 nodes, counted) — picker-shell has no demo route of its own (404) and is measured inside a host picker; forced → reaches (12/14/16/16px → 102px, each in its own picker-size)',
'header-font-size-md':
'the date-picker demo renders no [data-picker-header] (0 nodes, counted) — picker-shell has no demo route of its own (404) and is measured inside a host picker; forced → reaches (12/14/16/16px → 102px, each in its own picker-size)',
'header-font-size-lg':
'the date-picker demo renders no [data-picker-header] (0 nodes, counted) — picker-shell has no demo route of its own (404) and is measured inside a host picker; forced → reaches (12/14/16/16px → 102px, each in its own picker-size)',
'header-gap':
'the date-picker demo renders no [data-picker-header] (0 nodes, counted) — picker-shell has no demo route of its own (404) and is measured inside a host picker; forced → reaches (gap 8→1234px, fg → rgb(4,5,6), weight 500→123, line-height 20→60.32px)',
'header-fg':
'the date-picker demo renders no [data-picker-header] (0 nodes, counted) — picker-shell has no demo route of its own (404) and is measured inside a host picker; forced → reaches (gap 8→1234px, fg → rgb(4,5,6), weight 500→123, line-height 20→60.32px)',
'header-font-weight':
'the date-picker demo renders no [data-picker-header] (0 nodes, counted) — picker-shell has no demo route of its own (404) and is measured inside a host picker; forced → reaches (gap 8→1234px, fg → rgb(4,5,6), weight 500→123, line-height 20→60.32px)',
'header-line-height':
'the date-picker demo renders no [data-picker-header] (0 nodes, counted) — picker-shell has no demo route of its own (404) and is measured inside a host picker; forced → reaches (gap 8→1234px, fg → rgb(4,5,6), weight 500→123, line-height 20→60.32px)',
'time-row-gap':
'the time row is CLASS-hooked (.picker-time-row) and the date-picker demo does not render it; the guard filters nodes by attribute, so it cannot see it either way (§13 class-hook debt). Forced → reaches (gap/padding → 1234px, border 1→9px + rgb(7,8,9), label → rgb(10,11,12) / 99px)',
'time-row-padding-block-start':
'the time row is CLASS-hooked (.picker-time-row) and the date-picker demo does not render it; the guard filters nodes by attribute, so it cannot see it either way (§13 class-hook debt). Forced → reaches (gap/padding → 1234px, border 1→9px + rgb(7,8,9), label → rgb(10,11,12) / 99px)',
'time-row-padding-inline':
'the time row is CLASS-hooked (.picker-time-row) and the date-picker demo does not render it; the guard filters nodes by attribute, so it cannot see it either way (§13 class-hook debt). Forced → reaches (gap/padding → 1234px, border 1→9px + rgb(7,8,9), label → rgb(10,11,12) / 99px)',
'time-row-border-width':
'the time row is CLASS-hooked (.picker-time-row) and the date-picker demo does not render it; the guard filters nodes by attribute, so it cannot see it either way (§13 class-hook debt). Forced → reaches (gap/padding → 1234px, border 1→9px + rgb(7,8,9), label → rgb(10,11,12) / 99px)',
'time-row-border':
'the time row is CLASS-hooked (.picker-time-row) and the date-picker demo does not render it; the guard filters nodes by attribute, so it cannot see it either way (§13 class-hook debt). Forced → reaches (gap/padding → 1234px, border 1→9px + rgb(7,8,9), label → rgb(10,11,12) / 99px)',
'time-label-fg':
'the time row is CLASS-hooked (.picker-time-row) and the date-picker demo does not render it; the guard filters nodes by attribute, so it cannot see it either way (§13 class-hook debt). Forced → reaches (gap/padding → 1234px, border 1→9px + rgb(7,8,9), label → rgb(10,11,12) / 99px)',
'time-label-font-size-xs':
'the time row is CLASS-hooked (.picker-time-row) and the date-picker demo does not render it; the guard filters nodes by attribute, so it cannot see it either way (§13 class-hook debt). Forced → reaches (gap/padding → 1234px, border 1→9px + rgb(7,8,9), label → rgb(10,11,12) / 99px)',
'time-label-font-size-sm':
'the time row is CLASS-hooked (.picker-time-row) and the date-picker demo does not render it; the guard filters nodes by attribute, so it cannot see it either way (§13 class-hook debt). Forced → reaches (gap/padding → 1234px, border 1→9px + rgb(7,8,9), label → rgb(10,11,12) / 99px)',
'time-label-font-size-md':
'the time row is CLASS-hooked (.picker-time-row) and the date-picker demo does not render it; the guard filters nodes by attribute, so it cannot see it either way (§13 class-hook debt). Forced → reaches (gap/padding → 1234px, border 1→9px + rgb(7,8,9), label → rgb(10,11,12) / 99px)',
'time-label-font-size-lg':
'the time row is CLASS-hooked (.picker-time-row) and the date-picker demo does not render it; the guard filters nodes by attribute, so it cannot see it either way (§13 class-hook debt). Forced → reaches (gap/padding → 1234px, border 1→9px + rgb(7,8,9), label → rgb(10,11,12) / 99px)'
},
spinner:{
spinner:{
'bar-radius':
'bar-radius':
"the demo mounts the RING variant only (0 bars, 0 dots, counted); forced variant='bars' → reaches (1234px)",
"the demo mounts the RING variant only (0 bars, 0 dots, counted); forced variant='bars' → reaches (1234px)",
audit-codex-full-1.md P1 #5 — "picker-shell existe pero no esta cerrado como pieza de sistema". Closed as INTERNAL primitive per the audit's own recommendation: *"Si es interna, no deberia salir como demo publica ni Morfo publico incompleto."*
audit-codex-full-1.md P1 #5 — "picker-shell existe pero no esta cerrado como pieza de sistema". Closed as INTERNAL primitive per the audit's own recommendation: *"Si es interna, no deberia salir como demo publica ni Morfo publico incompleto."*
## Talla y tema
**Guard R-5.4** (`npm run theming:sentinel -- picker-shell <urldeunpicker>`):
6/31 tokens mueven un computed en la demo del `date-picker`; los 25 restantes