uix(grid-list): temable — 0 % → 85 %, y una selección que nunca pintó

38 claves nuevas en el bloque `grid-list` de `recipes/base.ts` (que ya existía
con los dos forwards de paleta: se FUSIONA, no se añade un segundo). Alcance
del censo 0 % → 85 % (22 de 26 puntuables); global 45 % → 46 %, sin contrato
49 → 48.

El eje `size` se estampa en el PROVIDER —el wrapper de eidos no tiene parte
`root`—, así que los tres nombres resueltos van SIN `parts`: molde `command`,
no `table`/`tree-grid`. Fuera los cuatro bloques `[data-size]` del CSS; los
emite el TSC. Altura de fila 1:1 con el bundle, `row-padding-inline` con ritmo
propio, y la tipografía un paso por debajo de md en adelante — verbatim, como
table y tree-grid.

LO QUE LA MEDICIÓN CAMBIÓ (la §4 generada tenía un error de doctrina y dos
defectos que sólo aparecieron midiendo; el §5 Veredicto se escribió con la
sonda delante, porque estaba vacío):

1. La selección de fila NUNCA HA PINTADO. La receta seleccionaba
   `[data-grid-list-row][data-selected]`, un atributo que ni el morfo declara
   ni soma estampa (estampa `data-state='selected'` + `aria-selected`): la
   regla no casaba jamás y su acento `--_grid-list-palette-element` no llegó
   nunca al píxel — una fila seleccionada muestra el velo NEUTRO del
   arquetipo, indistinguible del hover (capturado). Retirada la declaración
   muerta y su forward huérfano: 0 diffs, que es la prueba. Repararlo mueve
   píxel y choca con §12.5 (el arquetipo fija también `color`) → §13, espera
   firma. El acento del checkbox (`_palette-solid`) sí vive y se conserva.

2. NO se acuña `hover-row-bg` aunque la propuesta lo pedía. La fila es
   `archetype: 'item'`: el velo del sistema la pinta a especificidad plena y
   el plano de la receta pierde — medido, el hover pinta DOS VECES sobre el
   MISMO nodo (a diferencia de table/tree-grid, donde velo y plano caen en
   nodos distintos). Sería un token que no puede ganar; mismo criterio que
   listbox.highlighted → §12.7. Y fila Y celda llevan ambas `item`, así que
   bajo el puntero se apilan tres capas.

3. «Consume la capa compartida `menu-indicator`» es FALSO. Sale de
   `SHARED_LAYERS` (`theming-census.ts:432`), pero esa capa no tiene un solo
   selector de grid-list, la receta no la importa y soma no renderiza
   indicator: el checkbox ESPEJA el visual pintándolo por su cuenta. Entrada
   desviada del censo → §13 (listbox / menubar / navigation-menu están en la
   misma entrada y tampoco aparecen en el fichero).

Nombres, sobre la propuesta: `selection-checkbox-{width,height}` son UN knob →
`selection-checkbox-size`; `on-selection-checkbox-bg` → `checked-selection-checkbox-fg`
(es la tinta del glifo, y `on-` es prefijo reservado); `border` partido en
`border-width` + color (molde `command.input-border*`), que el checkbox
comparte; `padding` uniforme se queda `padding` (precedente
`command.viewport-padding`); `max-block` entra al contrato (molde
`command.viewport-max-block`).

Artefactos y gates:
- Sonda antes/después = 0 diffs en 6.090 valores × 7 estados. Determinismo
  verificado: dos corridas del mismo código, 0 diffs.
- R-5.4: 37/38 tokens mueven un computed. `invalid-border` adjudicado EN EL
  LEDGER con su razón medida (la demo arranca con su control en off; activado
  a mano alcanza: oklch(0.9555 0.0207 13.86) → rgb(1,2,3)).
- censo --only 85 % · component:audit PASS (R-5.3 incluida) · eidos-lint
  invalid 0, class-hooks 0 · suite eidos sin rojos nuevos (el único es el
  conocido skin-media-player) · rtl:check 0 · docs:check 0 · check por fichero
  limpio (72 globales, ninguno mío).
- README con su «Talla y tema» y tab `Tokens` en la demo (38 claves, leídas en
  vivo). El tab `Recipe` documentaba la regla retirada: corregido.

Las fichas de calendar / carousel / command / month-grid / range-calendar /
table / year-grid entran porque `--report` regenera el informe entero: sólo
actualizan NÚMEROS DE LÍNEA de commits ya hechos que no lo regeneraron. Cero
cambios de alcance. Las otras 161, que sólo cambiaban la fecha, se revirtieron.

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

@ -8,11 +8,11 @@
> [`theming-audit.md`](../theming-audit.md); ésta es la deuda de adopción que
> aquélla dejó apuntada en su §5.3-3.
- **Medido**: 2026-08-20 · **162 recetas** con CSS + **8 componentes sin receta** = 170 fichas, el árbol entero de `eidos/components/`
- **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**: **43%** — 2103 de 4856 knobs pasan por un token público del componente
- **Reparto**: público 2103 · privado 699 · global 1469 · literal 585 · sistema transversal 335 _(fuera del ratio)_
- **Sin token público propio**: 49 · **alcance < 20 %**: 43 · **alcance 100 %**: 7 · **con eje `size`**: 72
- **Alcance global**: **46%** — 2130 de 4680 knobs pasan por un token público del componente
- **Reparto**: público 2130 · privado 675 · global 1291 · literal 584 · sistema transversal 509 _(fuera del ratio)_
- **Sin token público propio**: 48 · **alcance < 20 %**: 42 · **alcance 100 %**: 7 · **con eje `size`**: 67
## Cómo se lee
@ -72,31 +72,31 @@ theming §6.7 lo pone delante (`hover-bg`). Las fichas proponen la forma
| ---: | --- | ---: | ---: | ---: | ---: |
| 1 | [palabras](./palabras.md) | 363 | 22% | 471 | 0 |
| 2 | [chronos](./chronos.md) | 236 | 1% | 245 | 9 |
| 3 | [range-calendar](./range-calendar.md) | 95 | 0% | 98 | 0 |
| 4 | [chart](./chart.md) | 80 | 9% | 92 | 17 |
| 5 | [proof-of-human](./proof-of-human.md) | 78 | 14% | 95 | 20 |
| 6 | [prose](./prose.md) | 73 | 1% | 74 | 2 |
| 7 | [time-range-picker](./time-range-picker.md) | 68 | 19% | 88 | 22 |
| 8 | [month-grid](./month-grid.md) | 58 | 0% | 61 | 0 |
| 9 | [year-grid](./year-grid.md) | 58 | 0% | 61 | 0 |
| 10 | [time-picker](./time-picker.md) | 47 | 18% | 59 | 16 |
| 11 | [color-picker](./color-picker.md) | 43 | 46% | 82 | 54 |
| 12 | [field-langs](./field-langs.md) | 39 | 0% | 41 | 0 |
| 13 | [field](./field.md) | 33 | 51% | 73 | 60 |
| 14 | [date-range-picker](./date-range-picker.md) | 30 | 40% | 53 | 22 |
| 15 | [natural-time-picker](./natural-time-picker.md) | 30 | 61% | 77 | 62 |
| 16 | [popover](./popover.md) | 28 | 42% | 51 | 36 |
| 17 | [radio-cards](./radio-cards.md) | 28 | 60% | 72 | 39 |
| 18 | [drawer](./drawer.md) | 27 | 55% | 62 | 44 |
| 19 | [grid-list](./grid-list.md) | 27 | 0% | 32 | 0 |
| 20 | [code-block](./code-block.md) | 26 | 0% | 26 | 0 |
| 3 | [chart](./chart.md) | 80 | 9% | 92 | 17 |
| 4 | [proof-of-human](./proof-of-human.md) | 78 | 14% | 95 | 20 |
| 5 | [prose](./prose.md) | 73 | 1% | 74 | 2 |
| 6 | [time-range-picker](./time-range-picker.md) | 68 | 19% | 88 | 22 |
| 7 | [time-picker](./time-picker.md) | 47 | 18% | 59 | 16 |
| 8 | [color-picker](./color-picker.md) | 43 | 46% | 82 | 54 |
| 9 | [field-langs](./field-langs.md) | 39 | 0% | 41 | 0 |
| 10 | [field](./field.md) | 33 | 51% | 73 | 60 |
| 11 | [date-range-picker](./date-range-picker.md) | 30 | 40% | 53 | 22 |
| 12 | [natural-time-picker](./natural-time-picker.md) | 30 | 61% | 77 | 62 |
| 13 | [popover](./popover.md) | 28 | 42% | 51 | 36 |
| 14 | [radio-cards](./radio-cards.md) | 28 | 60% | 72 | 39 |
| 15 | [drawer](./drawer.md) | 27 | 55% | 62 | 44 |
| 16 | [code-block](./code-block.md) | 26 | 0% | 26 | 0 |
| 17 | [tree-view](./tree-view.md) | 26 | 0% | 28 | 0 |
| 18 | [mockup](./mockup.md) | 25 | 14% | 29 | 0 |
| 19 | [textarea](./textarea.md) | 25 | 0% | 28 | 0 |
| 20 | [aura](./aura.md) | 24 | 31% | 35 | 24 |
## Sin ningún token público propio (49)
## Sin ningún token público propio (48)
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) · [`card-group`](./card-group.md) · [`cascade`](./cascade.md) · [`clipboard`](./clipboard.md) · [`code`](./code.md) · [`code-block`](./code-block.md) · [`css-field`](./css-field.md) · [`date-picker`](./date-picker.md) · [`display`](./display.md) · [`field-langs`](./field-langs.md) · [`grid-list`](./grid-list.md) · [`heading`](./heading.md) · [`highlight`](./highlight.md) · [`kbd`](./kbd.md) · [`knob`](./knob.md) · [`label`](./label.md) · [`link`](./link.md) · [`link-preview`](./link-preview.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) · [`picker-shell`](./picker-shell.md) · [`range-calendar`](./range-calendar.md) · [`scroll-frames`](./scroll-frames.md) · [`skeleton`](./skeleton.md) · [`skin-media-player`](./skin-media-player.md) · [`spinner`](./spinner.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) · [`textarea`](./textarea.md) · [`tree-view`](./tree-view.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) · [`card-group`](./card-group.md) · [`cascade`](./cascade.md) · [`clipboard`](./clipboard.md) · [`code`](./code.md) · [`code-block`](./code-block.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) · [`kbd`](./kbd.md) · [`knob`](./knob.md) · [`label`](./label.md) · [`link`](./link.md) · [`link-preview`](./link-preview.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) · [`picker-shell`](./picker-shell.md) · [`range-calendar`](./range-calendar.md) · [`scroll-frames`](./scroll-frames.md) · [`skeleton`](./skeleton.md) · [`skin-media-player`](./skin-media-player.md) · [`spinner`](./spinner.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) · [`textarea`](./textarea.md) · [`tree-view`](./tree-view.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)
@ -104,11 +104,10 @@ La columna «contrato» cuenta las claves **públicas** del bloque del component
| componente | alcance | knobs | público | privado | global | literal | sistema | contrato | size |
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | :-: |
| [range-calendar](./range-calendar.md) | 0% | 98 | 0 | 7 | 84 | 4 | 3 | 0 | y |
| [month-grid](./month-grid.md) | 0% | 61 | 0 | 13 | 40 | 5 | 3 | 0 | y |
| [year-grid](./year-grid.md) | 0% | 61 | 0 | 13 | 40 | 5 | 3 | 0 | y |
| [range-calendar](./range-calendar.md) | 0% | 98 | 0 | 7 | 2 | 4 | 85 | 0 | – |
| [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 | – |
| [grid-list](./grid-list.md) | 0% | 32 | 0 | 8 | 17 | 2 | 5 | 0 | y |
| [textarea](./textarea.md) | 0% | 28 | 0 | 16 | 5 | 4 | 3 | 0 | y |
| [tree-view](./tree-view.md) | 0% | 28 | 0 | 8 | 14 | 4 | 2 | 0 | y |
| [code-block](./code-block.md) | 0% | 26 | 0 | 2 | 24 | 0 | 0 | 0 | – |
@ -221,18 +220,19 @@ La columna «contrato» cuenta las claves **públicas** del bloque del component
| [skip-link](./skip-link.md) | 71% | 8 | 5 | 0 | 0 | 2 | 1 | 8 | – |
| [date-field](./date-field.md) | 73% | 11 | 8 | 3 | 0 | 0 | 0 | 21 | y |
| [time-field](./time-field.md) | 73% | 11 | 8 | 3 | 0 | 0 | 0 | 21 | y |
| [calendar](./calendar.md) | 75% | 71 | 51 | 13 | 0 | 4 | 3 | 76 | y |
| [combobox](./combobox.md) | 76% | 97 | 68 | 12 | 3 | 6 | 8 | 106 | – |
| [accordion](./accordion.md) | 77% | 65 | 49 | 10 | 0 | 5 | 1 | 76 | y |
| [carousel](./carousel.md) | 77% | 35 | 23 | 2 | 0 | 5 | 5 | 27 | – |
| [radio-group](./radio-group.md) | 77% | 75 | 56 | 10 | 7 | 0 | 2 | 61 | y |
| [chat-log](./chat-log.md) | 77% | 42 | 30 | 0 | 4 | 5 | 3 | 31 | – |
| [carousel](./carousel.md) | 77% | 36 | 24 | 2 | 0 | 5 | 5 | 30 | – |
| [chat-composer](./chat-composer.md) | 79% | 44 | 31 | 0 | 3 | 5 | 5 | 31 | – |
| [gradient-builder](./gradient-builder.md) | 80% | 60 | 45 | 7 | 1 | 3 | 4 | 72 | – |
| [chat-message](./chat-message.md) | 82% | 84 | 63 | 2 | 6 | 6 | 7 | 75 | y |
| [fab](./fab.md) | 82% | 17 | 14 | 0 | 2 | 1 | 0 | 10 | – |
| [toast](./toast.md) | 83% | 42 | 35 | 6 | 0 | 1 | 0 | 85 | – |
| [text-circular](./text-circular.md) | 83% | 6 | 5 | 0 | 1 | 0 | 0 | 0 | – |
| [calendar](./calendar.md) | 84% | 71 | 57 | 7 | 0 | 4 | 3 | 74 | – |
| [grid-list](./grid-list.md) | 85% | 31 | 22 | 2 | 1 | 1 | 5 | 38 | – |
| [table](./table.md) | 86% | 46 | 37 | 3 | 0 | 3 | 3 | 45 | – |
| [media-player](./media-player.md) | 88% | 49 | 43 | 0 | 0 | 6 | 0 | 61 | – |
| [tabs](./tabs.md) | 88% | 76 | 66 | 6 | 2 | 1 | 1 | 79 | y |

@ -5,10 +5,10 @@
> 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**: **75%** — 51 de 68 knobs por token público
- **Knobs de apariencia**: 71 — público 51 · privado 13 · global 0 · literal 4 · sistema 3 _(fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 76 pública(s) — `gap`, `padding-xs`, `padding-sm`, `padding-md`, `padding-lg`, `radius`, `border-width`, `border`, `invalid-border`, `bg`, `outline-bg`, `outline-shadow`, `fg`, `shadow`, `font-family`, `font-size-xs`, `font-size-sm`, `font-size-md`, `font-size-lg`, `line-height`, `header-gap`, `heading-fg`, `heading-font-size`, `heading-font-weight`, `control-size-xs`, `control-size-sm`, `control-size-md`, `control-size-lg`, `control-radius`, `control-border-width`, `control-border`, `hover-control-border`, `control-bg`, `control-fg`, `hover-control-fg`, `select-min-width`, `select-padding-inline`, `months-gap`, `grid-gap`, `weekday-size`, `weekday-fg`, `weekday-font-size`, `weekday-font-weight`, `day-size-xs`, `day-size-sm`, `day-size-md`, `day-size-lg`, `day-radius`, `day-border-width`, `day-border`, `hover-day-border`, `day-bg`, `day-fg`, `hover-day-fg`, `day-font-weight`, `day-selected-fg`, `range-start-solid`, `range-start-solid-hover`, `range-start-border`, `range-start-text`, `range-end-solid`, `range-end-solid-hover`, `range-end-border`, `range-end-text`, `day-today-border`, `day-today-fg`, `day-today-font-weight`, `day-outside-fg`, `day-outside-opacity`, `day-weekend-fg`, `day-holiday-shadow`, `day-unavailable-decoration`, `event-shadow`, `transition-duration`, `transition-ease`, `disabled-opacity` · 5 privada(s) forward — `_palette-solid`, `_palette-solid-hover`, `_palette-track`, `_palette-border`, `_palette-text`
- **Eje `size`**: sí · **ficheros**: `calendar-select.css`, `calendar.css`
- **Medido**: 2026-08-21 · **Alcance**: **84%** — 57 de 68 knobs por token público
- **Knobs de apariencia**: 71 — público 57 · privado 7 · global 0 · literal 4 · sistema 3 _(fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 74 pública(s) — `gap`, `padding-xs`, `padding-sm`, `padding-md`, `padding-lg`, `radius`, `border-width`, `border`, `invalid-border`, `bg`, `outline-bg`, `outline-shadow`, `fg`, `shadow`, `font-family`, `font-size-xs`, `font-size-sm`, `font-size-md`, `font-size-lg`, `line-height`, `header-gap`, `heading-fg`, `heading-font-size`, `heading-font-weight`, `control-size-xs`, `control-size-sm`, `control-size-md`, `control-size-lg`, `control-radius`, `control-border-width`, `control-border`, `hover-control-border`, `control-bg`, `control-fg`, `hover-control-fg`, `select-min-width`, `select-padding-inline`, `months-gap`, `grid-gap`, `weekday-size`, `weekday-fg`, `weekday-font-size`, `weekday-font-weight`, `day-size-xs`, `day-size-sm`, `day-size-md`, `day-size-lg`, `day-radius`, `day-border-width`, `day-border`, `hover-day-border`, `day-bg`, `day-fg`, `hover-day-fg`, `day-font-weight`, `day-selected-fg`, `range-start-solid`, `range-start-solid-hover`, `range-start-border`, `range-start-text`, `range-end-solid`, `range-end-solid-hover`, `range-end-border`, `range-end-text`, `day-today-border`, `day-today-fg`, `day-today-font-weight`, `day-outside-fg`, `day-outside-opacity`, `day-weekend-fg`, `day-unavailable-decoration`, `transition-duration`, `transition-ease`, `disabled-opacity` · 5 privada(s) forward — `_palette-solid`, `_palette-solid-hover`, `_palette-track`, `_palette-border`, `_palette-text`
- **Eje `size`**: no · **ficheros**: `calendar-select.css`, `calendar.css`
## 1. Knobs fuera de alcance
@ -16,32 +16,26 @@
_Ninguno._
### 1.2 A través de un privado (13)
### 1.2 A través de un privado (7)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `calendar.css:12` | `[data-calendar]` | `padding` | `var(--_calendar-padding)` |
| 2 | `calendar.css:19` | `[data-calendar]` | `font-size` | `var(--_calendar-font-size)` |
| 3 | `calendar.css:140` | `[data-calendar-head-cell]` | `inline-size` | `var(--_calendar-day-size)` |
| 4 | `calendar.css:154` | `[data-calendar-week-number][scope='row']` | `block-size` | `var(--_calendar-day-size)` |
| 5 | `calendar.css:169` | `[data-calendar-day]` | `inline-size` | `var(--_calendar-day-size)` |
| 6 | `calendar.css:170` | `[data-calendar-day]` | `block-size` | `var(--_calendar-day-size)` |
| 7 | `calendar.css:194` | `[data-calendar-day][data-selected]` | `border-color` | `var(--_calendar-palette-solid)` |
| 8 | `calendar.css:195` | `[data-calendar-day][data-selected]` | `background` | `var(--_calendar-palette-solid)` |
| 9 | `calendar.css:200` | `[data-calendar-day][data-selected]:hover` | `border-color` | `var(--_calendar-palette-solid-hover)` |
| 10 | `calendar.css:201` | `[data-calendar-day][data-selected]:hover` | `background` | `var(--_calendar-palette-solid-hover)` |
| 11 | `calendar.css:205` | `[data-calendar-day][data-focused]:not([data-selected])` | `border-color` | `var(--_calendar-palette-border)` |
| 12 | `calendar.css:206` | `[data-calendar-day][data-focused]:not([data-selected])` | `background` | `var(--_calendar-palette-track)` |
| 13 | `calendar.css:207` | `[data-calendar-day][data-focused]:not([data-selected])` | `color` | `var(--_calendar-palette-text)` |
| 1 | `calendar.css:175` | `[data-calendar-day][data-selected]` | `border-color` | `var(--_calendar-palette-solid)` |
| 2 | `calendar.css:176` | `[data-calendar-day][data-selected]` | `background` | `var(--_calendar-palette-solid)` |
| 3 | `calendar.css:181` | `[data-calendar-day][data-selected]:hover` | `border-color` | `var(--_calendar-palette-solid-hover)` |
| 4 | `calendar.css:182` | `[data-calendar-day][data-selected]:hover` | `background` | `var(--_calendar-palette-solid-hover)` |
| 5 | `calendar.css:186` | `[data-calendar-day][data-focused]:not([data-selected])` | `border-color` | `var(--_calendar-palette-border)` |
| 6 | `calendar.css:187` | `[data-calendar-day][data-focused]:not([data-selected])` | `background` | `var(--_calendar-palette-track)` |
| 7 | `calendar.css:188` | `[data-calendar-day][data-focused]:not([data-selected])` | `color` | `var(--_calendar-palette-text)` |
### 1.3 Literales (4)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `calendar.css:10` | `[data-calendar]` | `inline-size` | `max-content` |
| 2 | `calendar.css:66` | `[data-calendar-header]` | `inline-size` | `100%` |
| 3 | `calendar.css:107` | `[data-calendar-month-panel]` | `min-inline-size` | `max-content` |
| 4 | `calendar.css:131` | `[data-calendar-grid]` | `inline-size` | `max-content` |
| 1 | `calendar.css:12` | `[data-calendar]` | `inline-size` | `max-content` |
| 2 | `calendar.css:47` | `[data-calendar-header]` | `inline-size` | `100%` |
| 3 | `calendar.css:88` | `[data-calendar-month-panel]` | `min-inline-size` | `max-content` |
| 4 | `calendar.css:112` | `[data-calendar-grid]` | `inline-size` | `max-content` |
## 2. Sistema transversal (3) — informativo, fuera del ratio
@ -49,24 +43,21 @@ Un tema los alcanza **a nivel de sistema**, por diseño (recipe-contract §2).
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `calendar-select.css:27` | `[data-calendar-select-chevron]` | `opacity` | `var(--opacity-muted)` |
| 2 | `calendar.css:121` | `[data-calendar-day]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
| 3 | `calendar.css:189` | `[data-calendar-day]:hover` | `background-image` | `linear-gradient(var(--state-hover), var(--state-hover))` |
| 1 | `calendar-select.css:31` | `[data-calendar-select-chevron]` | `opacity` | `var(--opacity-muted)` |
| 2 | `calendar.css:102` | `[data-calendar-day]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
| 3 | `calendar.css:170` | `[data-calendar-day]:hover` | `background-image` | `linear-gradient(var(--state-hover), var(--state-hover))` |
## 3. Privados de la receta — ¿de dónde sale su valor?
| privado | declaraciones | valor(es) | origen | ¿deriva de un público? |
| --- | ---: | --- | --- | :-: |
| `--_calendar-padding` | 4 | `var(--calendar-padding-md)`, `var(--calendar-padding-xs)`, `var(--calendar-padding-sm)`, `var(--calendar-padding-lg)` | public | **sí** |
| `--_calendar-control-size` | 4 | `var(--calendar-control-size-md)`, `var(--calendar-control-size-xs)`, `var(--calendar-control-size-sm)`, `var(--calendar-control-size-lg)` | public | **sí** |
| `--_calendar-day-size` | 4 | `var(--calendar-day-size-md)`, `var(--calendar-day-size-xs)`, `var(--calendar-day-size-sm)`, `var(--calendar-day-size-lg)` | public | **sí** |
| `--_calendar-font-size` | 4 | `var(--calendar-font-size-md)`, `var(--calendar-font-size-xs)`, `var(--calendar-font-size-sm)`, `var(--calendar-font-size-lg)` | public | **sí** |
| `--_calendar-surface-accent` | 1 | `var(--_calendar-palette-border)` | private | no |
Consumidos y **no declarados en el CSS** (vienen de `base.ts` o de un estilo inline del wrapper): `--_calendar-palette-border`, `--_calendar-palette-solid`, `--_calendar-palette-solid-hover`, `--_calendar-palette-text`, `--_calendar-palette-track`.
## 4. Propuesta de corrección
- **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).
- **Consume la capa compartida `calendar-surface`.** Un eje que la capa posee se consume como `var(--_x, var(--x))`; el consumidor **no acuña** `--calendar-{eje}` para él — sería un vocabulario paralelo (README de `eidos/components`, «Capas compartidas» regla 2).
### 4.1 Tokens a declarar en `lib/recipes/base.ts` (3)
@ -82,12 +73,9 @@ no distingue lo que debería — se marca `⚠`.
| `month-panel-width` | `root` | `max-content` | 1 |
| `grid-width` | `root` | `max-content` | 1 |
### 4.2 Sin nombre mecánico (14)
### 4.2 Sin nombre mecánico (8)
- **⚠ decisión: `padding` es shorthand o eje físico — hay que partirlo en ejes lógicos antes de nombrarlo (recipe-contract §1, R-4.4)** — 1: `padding`.
- **ya alcanzable: el privado `--_calendar-font-size` deriva de un público (`var(--calendar-font-size-md)`, `var(--calendar-font-size-xs)`, `var(--calendar-font-size-sm)`, `var(--calendar-font-size-lg)`) — sin acción; el censo lo cuenta como no alcanzable por el límite del regex** — 1: `font-size`.
- **⚠ 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`.
- **ya alcanzable: el privado `--_calendar-day-size` deriva de un público (`var(--calendar-day-size-md)`, `var(--calendar-day-size-xs)`, `var(--calendar-day-size-sm)`, `var(--calendar-day-size-lg)`) — sin acción; el censo lo cuenta como no alcanzable por el límite del regex** — 4: `inline-size`, `block-size`.
- **⚠ 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** — 7: `border-color`, `background`, `color`.
### 4.4 Lo que hay que comprobar a mano (PLAN-theming §1.3 · §7.4)

@ -5,9 +5,9 @@
> 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**: **77%** — 24 de 31 knobs por token público
- **Knobs de apariencia**: 36 — público 24 · privado 2 · global 0 · literal 5 · sistema 5 _(fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 30 pública(s) — `indicator-size-xs`, `indicator-size-sm`, `indicator-size-md`, `indicator-size-lg`, `indicator-size-xl`, `indicator-gap-xs`, `indicator-gap-sm`, `indicator-gap-md`, `indicator-gap-lg`, `indicator-gap-xl`, `item-gap-xs`, `item-gap-md`, `indicator-size`, `indicator-gap`, `item-gap`, `gap`, `radius`, `font-family`, `fg`, `inner-radius-inset`, `trigger-offset`, `frame-padding`, `border-width`, `border`, `surface-bg`, `vertical-block-size`, `indicator-group-padding-block`, `indicator-bg`, `indicator-radius`, `active-indicator-scale` · 2 privada(s) forward — `_palette-solid`, `_palette-element`
- **Medido**: 2026-08-21 · **Alcance**: **77%** — 23 de 30 knobs por token público
- **Knobs de apariencia**: 35 — público 23 · privado 2 · global 0 · literal 5 · sistema 5 _(fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 27 pública(s) — `indicator-size-xs`, `indicator-size-sm`, `indicator-size-md`, `indicator-size-lg`, `indicator-size-xl`, `indicator-gap-xs`, `indicator-gap-sm`, `indicator-gap-md`, `indicator-gap-lg`, `indicator-gap-xl`, `indicator-size`, `indicator-gap`, `gap`, `radius`, `font-family`, `fg`, `inner-radius-inset`, `trigger-offset`, `frame-padding`, `border-width`, `border`, `surface-bg`, `vertical-block-size`, `indicator-group-padding-block`, `indicator-bg`, `indicator-radius`, `active-indicator-scale` · 2 privada(s) forward — `_palette-solid`, `_palette-element`
- **Eje `size`**: no · **ficheros**: `carousel.css`
## 1. Knobs fuera de alcance
@ -20,8 +20,8 @@ _Ninguno._
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `carousel.css:288` | `[data-carousel-indicator]:hover:not([data-disabled])` | `background` | `var(--_carousel-palette-element)` |
| 2 | `carousel.css:292` | `[data-carousel-indicator][data-state='active']` | `background` | `var(--_carousel-palette-solid)` |
| 1 | `carousel.css:287` | `[data-carousel-indicator]:hover:not([data-disabled])` | `background` | `var(--_carousel-palette-element)` |
| 2 | `carousel.css:291` | `[data-carousel-indicator][data-state='active']` | `background` | `var(--_carousel-palette-solid)` |
### 1.3 Literales (5)
@ -31,7 +31,7 @@ _Ninguno._
| 2 | `carousel.css:73` | `[data-carousel][data-orientation='vertical'] [data-carousel-item-group]` | `block-size` | `100%` |
| 3 | `carousel.css:80` | `[data-carousel][data-orientation='vertical'] [data-carousel-item]` | `block-size` | `100%` |
| 4 | `carousel.css:86` | `[data-carousel][data-orientation='vertical'] [data-carousel-indicator-group]` | `inline-size` | `max-content` |
| 5 | `carousel.css:92` | `[data-carousel][data-block]` | `inline-size` | `100%` |
| 5 | `carousel.css:91` | `[data-carousel][data-block]` | `inline-size` | `100%` |
## 2. Sistema transversal (5) — informativo, fuera del ratio
@ -39,11 +39,11 @@ Un tema los alcanza **a nivel de sistema**, por diseño (recipe-contract §2).
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `carousel.css:119` | `[data-carousel][data-disabled]` | `opacity` | `var(--opacity-disabled)` |
| 2 | `carousel.css:139` | `[data-carousel]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
| 3 | `carousel.css:231` | `[data-carousel-prev-trigger][data-disabled], [data-carousel-next-trigger][data-disabled]` | `opacity` | `var(--opacity-disabled)` |
| 4 | `carousel.css:304` | `[data-carousel-indicator]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
| 5 | `carousel.css:309` | `[data-carousel-indicator][data-disabled]` | `opacity` | `var(--opacity-disabled)` |
| 1 | `carousel.css:118` | `[data-carousel][data-disabled]` | `opacity` | `var(--opacity-disabled)` |
| 2 | `carousel.css:138` | `[data-carousel]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
| 3 | `carousel.css:230` | `[data-carousel-prev-trigger][data-disabled], [data-carousel-next-trigger][data-disabled]` | `opacity` | `var(--opacity-disabled)` |
| 4 | `carousel.css:303` | `[data-carousel-indicator]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
| 5 | `carousel.css:308` | `[data-carousel-indicator][data-disabled]` | `opacity` | `var(--opacity-disabled)` |
## 3. Privados de la receta — ¿de dónde sale su valor?

@ -5,7 +5,7 @@
> 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**: **98%** — 49 de 50 knobs por token público
- **Medido**: 2026-08-21 · **Alcance**: **98%** — 49 de 50 knobs por token público
- **Knobs de apariencia**: 52 — público 49 · privado 0 · global 0 · literal 1 · sistema 2 _(fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 57 pública(s) — `input-padding-inline-sm`, `input-padding-inline-md`, `input-padding-inline-lg`, `input-font-size-sm`, `input-font-size-md`, `input-font-size-lg`, `item-padding-inline-sm`, `item-padding-inline-md`, `item-padding-inline-lg`, `item-font-size-sm`, `item-font-size-md`, `item-font-size-lg`, `input-padding-inline`, `input-font-size`, `item-padding-inline`, `item-font-size`, `radius`, `font-family`, `line-height`, `fg`, `input-padding-block`, `input-margin`, `input-border-width`, `input-border`, `input-radius`, `input-bg`, `input-fg`, `input-placeholder-fg`, `focus-input-border`, `loading-height`, `loading-fg`, `viewport-max-block`, `viewport-gap`, `viewport-padding-inline`, `viewport-padding-block-end`, `scrollbar-inset`, `viewport-padding`, `group-gap`, `group-heading-padding-block`, `group-heading-fg`, `group-heading-font-size`, `group-heading-font-weight`, `group-heading-tracking`, `group-items-gap`, `item-gap`, `item-fg`, `item-icon-size`, `item-icon-fg`, `item-shortcut-gap`, `item-shortcut-fg`, `item-shortcut-font-size`, `selected-item-sub-fg`, `empty-padding-block`, `empty-fg`, `separator-height`, `separator-margin-block`, `separator-bg`
- **Eje `size`**: no · **ficheros**: `command.css`
@ -24,7 +24,7 @@ _Ninguno._
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `command.css:41` | `[data-command]` | `inline-size` | `100%` |
| 1 | `command.css:42` | `[data-command]` | `inline-size` | `100%` |
## 2. Sistema transversal (2) — informativo, fuera del ratio
@ -32,8 +32,8 @@ Un tema los alcanza **a nivel de sistema**, por diseño (recipe-contract §2).
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `command.css:128` | `[data-command-input]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
| 2 | `command.css:239` | `[data-command-item][data-disabled], [data-command-link-item][data-disabled]` | `opacity` | `var(--opacity-disabled)` |
| 1 | `command.css:132` | `[data-command-input]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
| 2 | `command.css:243` | `[data-command-item][data-disabled], [data-command-link-item][data-disabled]` | `opacity` | `var(--opacity-disabled)` |
## 3. Privados de la receta — ¿de dónde sale su valor?

@ -5,54 +5,31 @@
> 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 27 knobs por token público
- **Knobs de apariencia**: 32 — público 0 · privado 8 · global 17 · literal 2 · sistema 5 _(fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 0 pública(s) · 2 privada(s) forward — `_palette-solid`, `_palette-element`
- **Eje `size`**: sí · **ficheros**: `grid-list.css`
- **Medido**: 2026-08-21 · **Alcance**: **85%** — 22 de 26 knobs por token público
- **Knobs de apariencia**: 31 — público 22 · privado 2 · global 1 · literal 1 · sistema 5 _(fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 38 pública(s) — `row-height-xs`, `row-height-sm`, `row-height-md`, `row-height-lg`, `row-height-xl`, `row-padding-inline-xs`, `row-padding-inline-sm`, `row-padding-inline-md`, `row-padding-inline-lg`, `row-padding-inline-xl`, `font-size-xs`, `font-size-sm`, `font-size-md`, `font-size-lg`, `font-size-xl`, `row-height`, `row-padding-inline`, `font-size`, `gap`, `padding`, `border-width`, `border`, `radius`, `bg`, `fg`, `font-family`, `line-height`, `max-block`, `invalid-border`, `row-gap`, `row-padding-block`, `row-radius`, `row-fg`, `disabled-row-fg`, `selection-checkbox-size`, `selection-checkbox-border`, `selection-checkbox-radius`, `checked-selection-checkbox-fg` · 1 privada(s) forward — `_palette-solid`
- **Eje `size`**: no · **ficheros**: `grid-list.css`
## 1. Knobs fuera de alcance
### 1.1 Directo a primitivo global (17)
### 1.1 Directo a primitivo global (1)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `grid-list.css:32` | `[data-grid-list]` | `padding` | `var(--space-1)` |
| 2 | `grid-list.css:33` | `[data-grid-list]` | `border` | `var(--border-width) solid var(--color-border-default)` |
| 3 | `grid-list.css:34` | `[data-grid-list]` | `border-radius` | `var(--radius-md)` |
| 4 | `grid-list.css:35` | `[data-grid-list]` | `background` | `var(--color-surface-default)` |
| 5 | `grid-list.css:36` | `[data-grid-list]` | `color` | `var(--color-content-primary)` |
| 6 | `grid-list.css:37` | `[data-grid-list]` | `font-family` | `var(--style-label-font-family)` |
| 7 | `grid-list.css:39` | `[data-grid-list]` | `line-height` | `var(--leading-ui)` |
| 8 | `grid-list.css:91` | `[data-grid-list][data-invalid]` | `border-color` | `var(--color-threat-element)` |
| 9 | `grid-list.css:104` | `[data-grid-list-row]` | `gap` | `var(--space-2)` |
| 10 | `grid-list.css:106` | `[data-grid-list-row]` | `padding-block` | `var(--space-1)` |
| 11 | `grid-list.css:108` | `[data-grid-list-row]` | `border-radius` | `var(--radius-sm)` |
| 12 | `grid-list.css:109` | `[data-grid-list-row]` | `color` | `var(--color-content-primary)` |
| 13 | `grid-list.css:119` | `[data-grid-list-row]:hover:not([data-disabled]), [data-grid-list-row][data-highlighted]:not([data-disabled])` | `background` | `var(--color-surface-overlay)` |
| 14 | `grid-list.css:127` | `[data-grid-list-row][data-disabled]` | `color` | `var(--color-content-muted)` |
| 15 | `grid-list.css:170` | `[data-grid-list-selection-checkbox]` | `border` | `var(--border-width) solid var(--color-border-default)` |
| 16 | `grid-list.css:171` | `[data-grid-list-selection-checkbox]` | `border-radius` | `var(--radius-sm)` |
| 17 | `grid-list.css:189` | `[data-grid-list-selection-checkbox][data-state='checked']::after` | `background-color` | `var(--color-content-on-solid)` |
### 1.2 A través de un privado (8)
| 1 | `grid-list.css:105` | `[data-grid-list-row]:hover:not([data-disabled]), [data-grid-list-row][data-highlighted]:not([data-disabled])` | `background` | `var(--color-surface-overlay)` |
### 1.2 A través de un privado (2)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `grid-list.css:38` | `[data-grid-list]` | `font-size` | `var(--_grid-list-font-size)` |
| 2 | `grid-list.css:105` | `[data-grid-list-row]` | `min-block-size` | `var(--_grid-list-row-height)` |
| 3 | `grid-list.css:107` | `[data-grid-list-row]` | `padding-inline` | `var(--_grid-list-row-padding-inline)` |
| 4 | `grid-list.css:123` | `[data-grid-list-row][data-selected]:not([data-disabled])` | `background` | `var(--_grid-list-palette-element)` |
| 5 | `grid-list.css:168` | `[data-grid-list-selection-checkbox]` | `inline-size` | `var(--_grid-list-checkbox-size)` |
| 6 | `grid-list.css:169` | `[data-grid-list-selection-checkbox]` | `block-size` | `var(--_grid-list-checkbox-size)` |
| 7 | `grid-list.css:181` | `[data-grid-list-selection-checkbox][data-state='checked']` | `border-color` | `var(--_grid-list-palette-solid)` |
| 8 | `grid-list.css:182` | `[data-grid-list-selection-checkbox][data-state='checked']` | `background` | `var(--_grid-list-palette-solid)` |
| 1 | `grid-list.css:171` | `[data-grid-list-selection-checkbox][data-state='checked']` | `border-color` | `var(--_grid-list-palette-solid)` |
| 2 | `grid-list.css:172` | `[data-grid-list-selection-checkbox][data-state='checked']` | `background` | `var(--_grid-list-palette-solid)` |
### 1.3 Literales (2)
### 1.3 Literales (1)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `grid-list.css:31` | `[data-grid-list]` | `gap` | `1px` |
| 2 | `grid-list.css:47` | `[data-grid-list][data-block]` | `inline-size` | `100%` |
| 1 | `grid-list.css:46` | `[data-grid-list][data-block]` | `inline-size` | `100%` |
## 2. Sistema transversal (5) — informativo, fuera del ratio
@ -60,30 +37,23 @@ Un tema los alcanza **a nivel de sistema**, por diseño (recipe-contract §2).
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `grid-list.css:86` | `[data-grid-list][data-disabled]` | `opacity` | `var(--opacity-disabled)` |
| 2 | `grid-list.css:96` | `[data-grid-list]:focus-visible, [data-grid-list][data-focused]` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
| 3 | `grid-list.css:129` | `[data-grid-list-row][data-disabled]` | `opacity` | `var(--opacity-disabled)` |
| 4 | `grid-list.css:133` | `[data-grid-list-row]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
| 5 | `grid-list.css:201` | `[data-grid-list-selection-checkbox]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
| 1 | `grid-list.css:66` | `[data-grid-list][data-disabled]` | `opacity` | `var(--opacity-disabled)` |
| 2 | `grid-list.css:76` | `[data-grid-list]:focus-visible, [data-grid-list][data-focused]` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
| 3 | `grid-list.css:119` | `[data-grid-list-row][data-disabled]` | `opacity` | `var(--opacity-disabled)` |
| 4 | `grid-list.css:123` | `[data-grid-list-row]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
| 5 | `grid-list.css:191` | `[data-grid-list-selection-checkbox]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
## 3. Privados de la receta — ¿de dónde sale su valor?
| privado | declaraciones | valor(es) | origen | ¿deriva de un público? |
| --- | ---: | --- | --- | :-: |
| `--_grid-list-row-height` | 5 | `var(--control-height-md)`, `var(--control-height-xs)`, `var(--control-height-sm)`, `var(--control-height-lg)`, `var(--control-height-xl)` | global | no |
| `--_grid-list-row-padding-inline` | 5 | `var(--space-3)`, `var(--space-2)`, `var(--space-4)`, `var(--space-5)` | global | no |
| `--_grid-list-font-size` | 5 | `var(--font-size-sm)`, `var(--font-size-xs)`, `var(--font-size-md)`, `var(--font-size-lg)` | global | no |
| `--_grid-list-max-height` | 1 | `min(60vh, 22rem)` | literal | no |
| `--_grid-list-checkbox-size` | 1 | `0.875rem` | literal | no |
_La receta no declara privados propios en su CSS._
Consumidos y **no declarados en el CSS** (vienen de `base.ts` o de un estilo inline del wrapper): `--_grid-list-palette-element`, `--_grid-list-palette-solid`.
Consumidos y **no declarados en el CSS** (vienen de `base.ts` o de un estilo inline del wrapper): `--_grid-list-palette-solid`.
## 4. Propuesta de corrección
- **Consume la capa compartida `menu-indicator`.** Un eje que la capa posee se consume como `var(--_x, var(--x))`; el consumidor **no acuña** `--grid-list-{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` (32)
### 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
puede moverlo. Nombres derivados de recipe-contract §1 (ejes lógicos, talla
@ -93,52 +63,12 @@ no distingue lo que debería — se marca `⚠`.
| token (`--grid-list-…`) | scope TSC | valor propuesto | usos |
| --- | --- | --- | ---: |
| `gap` | `root` | `1px` | 1 |
| `radius` | `root` | `var(--radius-md)` | 1 |
| `bg` | `root` | `var(--color-surface-default)` | 1 |
| `fg` | `root` | `var(--color-content-primary)` | 1 |
| `font-family` | `root` | `var(--style-label-font-family)` | 1 |
| `font-size-md` | `size:md` | `var(--font-size-sm)` | 1 |
| `font-size-xs` | `size:xs` | `var(--font-size-xs)` | 1 |
| `font-size-sm` | `size:sm` | `var(--font-size-sm)` | 1 |
| `font-size-lg` | `size:lg` | `var(--font-size-md)` | 1 |
| `font-size-xl` | `size:xl` | `var(--font-size-lg)` | 1 |
| `line-height` | `root` | `var(--leading-ui)` | 1 |
| `invalid-border` | `root` | `var(--color-threat-element)` | 1 |
| `row-gap` | `root` | `var(--space-2)` | 1 |
| `row-height-md` | `size:md` | `var(--control-height-md)` | 1 |
| `row-height-xs` | `size:xs` | `var(--control-height-xs)` | 1 |
| `row-height-sm` | `size:sm` | `var(--control-height-sm)` | 1 |
| `row-height-lg` | `size:lg` | `var(--control-height-lg)` | 1 |
| `row-height-xl` | `size:xl` | `var(--control-height-xl)` | 1 |
| `row-padding-block` | `root` | `var(--space-1)` | 1 |
| `row-padding-inline-md` | `size:md` | `var(--space-3)` | 1 |
| `row-padding-inline-xs` | `size:xs` | `var(--space-2)` | 1 |
| `row-padding-inline-sm` | `size:sm` | `var(--space-2)` | 1 |
| `row-padding-inline-lg` | `size:lg` | `var(--space-4)` | 1 |
| `row-padding-inline-xl` | `size:xl` | `var(--space-5)` | 1 |
| `row-radius` | `root` | `var(--radius-sm)` | 1 |
| `row-fg` | `root` | `var(--color-content-primary)` | 1 |
| `hover-row-bg` | `root` | `var(--color-surface-overlay)` | 1 |
| `disabled-row-fg` | `root` | `var(--color-content-muted)` | 1 |
| `selection-checkbox-width` | `root` | `0.875rem` | 1 |
| `selection-checkbox-height` | `root` | `0.875rem` | 1 |
| `selection-checkbox-radius` | `root` | `var(--radius-sm)` | 1 |
| `on-selection-checkbox-bg` | `root` | `var(--color-content-on-solid)` | 1 |
### 4.2 Sin nombre mecánico (7)
### 4.2 Sin nombre mecánico (3)
- **⚠ decisión: `padding` es shorthand o eje físico — hay que partirlo en ejes lógicos antes de nombrarlo (recipe-contract §1, R-4.4)** — 1: `padding`.
- **⚠ 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)** — 2: `border`.
- **⚠ 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`.
- **⚠ 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** — 3: `background`, `border-color`.
### 4.3 Avisos sobre los tokens propuestos (4)
- **el privado `--_grid-list-font-size` debe pasar a leer este público (o desaparecer)** — `--grid-list-font-size-md`, `--grid-list-font-size-xs`, `--grid-list-font-size-sm`, `--grid-list-font-size-lg`, `--grid-list-font-size-xl`
- **el privado `--_grid-list-row-height` debe pasar a leer este público (o desaparecer)** — `--grid-list-row-height-md`, `--grid-list-row-height-xs`, `--grid-list-row-height-sm`, `--grid-list-row-height-lg`, `--grid-list-row-height-xl`
- **el privado `--_grid-list-row-padding-inline` debe pasar a leer este público (o desaparecer)** — `--grid-list-row-padding-inline-md`, `--grid-list-row-padding-inline-xs`, `--grid-list-row-padding-inline-sm`, `--grid-list-row-padding-inline-lg`, `--grid-list-row-padding-inline-xl`
- **el privado `--_grid-list-checkbox-size` debe pasar a leer este público (o desaparecer)** — `--grid-list-selection-checkbox-width`, `--grid-list-selection-checkbox-height`
- **⚠ 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: `border-color`, `background`.
### 4.4 Lo que hay que comprobar a mano (PLAN-theming §1.3 · §7.4)
@ -152,6 +82,66 @@ no distingue lo que debería — se marca `⚠`.
<!-- veredicto:start -->
_(pendiente — lo escribe el autor; se conserva al regenerar)_
**Medido 2026-08-21** (sonda ×2 sobre el mismo código = 0 diffs en 6.090
valores · 7 estados; dev server 5180). La §4 orienta pero tiene un error de
doctrina y dos defectos que sólo aparecieron midiendo.
1. **La nota «consume la capa `menu-indicator`» NO aplica.** Sale de
`SHARED_LAYERS` (`theming-census.ts:432`), que lista a grid-list como
consumidor — pero `lib/menu-indicator.css` no tiene un solo selector
`grid-list`, la receta no la importa y soma no renderiza indicator. El
checkbox **espeja** el visual con pintura propia (el comentario de la
receta dice «recipe family», no consumo). Entrada desviada del censo —
anotada como incidencia §13.
2. **La selección está MUERTA en la fila.** La receta selecciona
`[data-grid-list-row][data-selected]`, pero ni el morfo declara
`data-selected` ni soma lo estampa (DOM medido: `data-state=selected` +
`aria-selected='true'`). El acento `--_grid-list-palette-element` no pinta
jamás; una fila seleccionada sólo muestra el velo neutro del arquetipo
(rama `[aria-selected='true']`, 0,4,0) — seleccionada ≡ hovered
visualmente. El checkbox SÍ pinta (`_palette-solid` vivo, medido
`oklch(0.5556 0.1829 305.86)` en checked). Decisión del autor: retirar la
regla muerta + el forward huérfano (0 diffs lo prueba; precedente
`table.selected-row-fg`), o dejarla verbatim a la espera de la reparación
firmada (re-apuntar a `[data-state='selected']` mueve píxel).
3. **El hover de la fila pinta DOBLE** — plano de la receta (`background:`
shorthand, 0,3,0 → background-color overlay) + velo del sistema (la regla
de item va a especificidad PLENA a propósito, 0,5,0 → background-image,
gana al `none` del shorthand). Es el caso `listbox.highlighted`: **NO se
acuña `hover-row-bg`** — bendeciría un duplicado condenado. La declaración
queda verbatim (global, excepción de censo). A diferencia de
table/tree-grid (velo en la CELDA, plano en la FILA — nodos distintos),
aquí ambos caen en el MISMO nodo.
4. **Fila Y celda llevan `archetype: 'item'`** — bajo el puntero se apilan
velo de celda + velo de fila (+ plano), medido en la sonda. Mismo par de
portadores que la firma pendiente de table — anotado §12.
5. **Los per-talla van al bundle, no verbatim al primitivo**: `row-height`
1:1 (`--size-{k}-control-height` ES alias de `--control-height-{k}`,
verificado en `generated/base.css:409-445`); `font-size` un paso por
debajo desde md (xs→xs, sm→sm, md→sm, lg→md, xl→lg) — deviación verbatim
como table/tree-grid, NO «corregirla» al 1:1; `row-padding-inline` ritmo
propio en `--space-*` (2/2/3/4/5). Resolved names **sin `parts`** — el
`data-size` se estampa en el provider (= host); molde `command`.
6. **Correcciones de nombre sobre la §4.1**:
`selection-checkbox-width`+`-height` son UN knob → un solo token
`selection-checkbox-size`; `on-selection-checkbox-bg` →
`checked-selection-checkbox-fg` (es la TINTA del glifo ✓; `on-` choca con
el prefijo reservado — precedente `scrim-fg-over-*`); el `padding`
uniforme se queda `padding` (slot admitido: `command.viewport-padding`,
consumido shorthand en `command.css:188`); `border` se parte en
`border-width` + `border` (color), y el checkbox comparte `border-width`
(molde tree-grid: UNA anchura por componente) con su propio
`selection-checkbox-border`; `max-block: min(60vh, 22rem)` entra al
contrato (molde `command.viewport-max-block`) aunque `max-block-size` no
puntúe en el censo.
7. **Privados que mueren**: `--_grid-list-{row-height,row-padding-inline,font-size}`
(los sustituyen los resolved names; fuera los bloques `[data-size]` del
CSS), `--_grid-list-max-height` y `--_grid-list-checkbox-size` (pasan a
público). Quedan los dos forwards THM-2.
8. **Alcance esperado ≈ 81 %** (22/27): fuera el hover condenado (1 global),
el selected muerto (1 privado — 85 % si se retira), los dos lados checked
del checkbox (forwards, privados en todo el catálogo) y el `100%` de
`[data-block]` (identidad, D-TH.2). Los `transparent` de variant son
identidad de variante y no puntúan.
<!-- veredicto:end -->

@ -5,114 +5,108 @@
> 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 58 knobs por token público
- **Knobs de apariencia**: 61 — público 0 · privado 13 · global 40 · literal 5 · sistema 3 _(fuera del ratio)_
- **Medido**: 2026-08-21 · **Alcance**: **0%** — 0 de 12 knobs por token público
- **Knobs de apariencia**: 61 — público 0 · privado 7 · global 0 · literal 5 · sistema 49 _(fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 0 pública(s) · 5 privada(s) forward — `_palette-solid`, `_palette-solid-hover`, `_palette-track`, `_palette-border`, `_palette-text`
- **Eje `size`**: sí · **ficheros**: `month-grid.css`
- **Eje `size`**: no · **ficheros**: `month-grid.css`
## 1. Knobs fuera de alcance
### 1.1 Directo a primitivo global (40)
### 1.1 Directo a primitivo global (0)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `month-grid.css:11` | `[data-month-grid]` | `gap` | `var(--calendar-gap)` ⤴ prestado de `calendar` |
| 2 | `month-grid.css:16` | `[data-month-grid]` | `border` | `var(--calendar-border-width) solid var(--calendar-border)` ⤴ prestado de `calendar` |
| 3 | `month-grid.css:17` | `[data-month-grid]` | `border-radius` | `var(--calendar-radius)` ⤴ prestado de `calendar` |
| 4 | `month-grid.css:18` | `[data-month-grid]` | `background` | `var(--calendar-bg)` ⤴ prestado de `calendar` |
| 5 | `month-grid.css:19` | `[data-month-grid]` | `box-shadow` | `var(--calendar-shadow)` ⤴ prestado de `calendar` |
| 6 | `month-grid.css:20` | `[data-month-grid]` | `color` | `var(--calendar-fg)` ⤴ prestado de `calendar` |
| 7 | `month-grid.css:21` | `[data-month-grid]` | `font-family` | `var(--calendar-font-family)` ⤴ prestado de `calendar` |
| 8 | `month-grid.css:23` | `[data-month-grid]` | `line-height` | `var(--calendar-line-height)` ⤴ prestado de `calendar` |
| 9 | `month-grid.css:48` | `[data-month-grid][data-variant='outline']` | `background` | `var(--calendar-outline-bg)` ⤴ prestado de `calendar` |
| 10 | `month-grid.css:49` | `[data-month-grid][data-variant='outline']` | `box-shadow` | `var(--calendar-outline-shadow)` ⤴ prestado de `calendar` |
| 11 | `month-grid.css:66` | `[data-month-grid-header]` | `gap` | `var(--calendar-header-gap)` ⤴ prestado de `calendar` |
| 12 | `month-grid.css:73` | `[data-month-grid-heading]` | `color` | `var(--calendar-heading-fg)` ⤴ prestado de `calendar` |
| 13 | `month-grid.css:74` | `[data-month-grid-heading]` | `font-size` | `var(--calendar-heading-font-size)` ⤴ prestado de `calendar` |
| 14 | `month-grid.css:75` | `[data-month-grid-heading]` | `font-weight` | `var(--calendar-heading-font-weight)` ⤴ prestado de `calendar` |
| 15 | `month-grid.css:88` | `[data-month-grid-prev-button], [data-month-grid-next-button]` | `border` | `var(--calendar-control-border-width) solid var(--calendar-control-border)` ⤴ prestado de `calendar` |
| 16 | `month-grid.css:89` | `[data-month-grid-prev-button], [data-month-grid-next-button]` | `border-radius` | `var(--calendar-control-radius)` ⤴ prestado de `calendar` |
| 17 | `month-grid.css:90` | `[data-month-grid-prev-button], [data-month-grid-next-button]` | `background` | `var(--calendar-control-bg)` ⤴ prestado de `calendar` |
| 18 | `month-grid.css:91` | `[data-month-grid-prev-button], [data-month-grid-next-button]` | `color` | `var(--calendar-control-fg)` ⤴ prestado de `calendar` |
| 19 | `month-grid.css:102` | `[data-month-grid-prev-button]:hover, [data-month-grid-next-button]:hover` | `border-color` | `var(--calendar-hover-control-border)` ⤴ prestado de `calendar` |
| 20 | `month-grid.css:104` | `[data-month-grid-prev-button]:hover, [data-month-grid-next-button]:hover` | `color` | `var(--calendar-hover-control-fg)` ⤴ prestado de `calendar` |
| 21 | `month-grid.css:117` | `[data-month-grid-prev-button][data-disabled], [data-month-grid-next-button][data-disabled]` | `opacity` | `var(--calendar-disabled-opacity)` ⤴ prestado de `calendar` |
| 22 | `month-grid.css:123` | `[data-month-grid-grid]` | `gap` | `var(--calendar-grid-gap)` ⤴ prestado de `calendar` |
| 23 | `month-grid.css:144` | `[data-month-grid-cell]` | `padding-inline` | `var(--calendar-select-padding-inline, 8px)` ⤴ prestado de `calendar` |
| 24 | `month-grid.css:145` | `[data-month-grid-cell]` | `border` | `var(--calendar-day-border-width) solid var(--calendar-day-border)` ⤴ prestado de `calendar` |
| 25 | `month-grid.css:146` | `[data-month-grid-cell]` | `border-radius` | `var(--calendar-day-radius)` ⤴ prestado de `calendar` |
| 26 | `month-grid.css:147` | `[data-month-grid-cell]` | `background` | `var(--calendar-day-bg)` ⤴ prestado de `calendar` |
| 27 | `month-grid.css:148` | `[data-month-grid-cell]` | `color` | `var(--calendar-day-fg)` ⤴ prestado de `calendar` |
| 28 | `month-grid.css:150` | `[data-month-grid-cell]` | `font-weight` | `var(--calendar-day-font-weight)` ⤴ prestado de `calendar` |
| 29 | `month-grid.css:162` | `[data-month-grid-cell]:hover` | `border-color` | `var(--calendar-hover-day-border)` ⤴ prestado de `calendar` |
| 30 | `month-grid.css:164` | `[data-month-grid-cell]:hover` | `color` | `var(--calendar-hover-day-fg)` ⤴ prestado de `calendar` |
| 31 | `month-grid.css:170` | `[data-month-grid-cell][data-selected]` | `color` | `var(--calendar-day-selected-fg)` ⤴ prestado de `calendar` |
| 32 | `month-grid.css:185` | `[data-month-grid-cell][data-current]:not([data-selected])` | `border-color` | `var(--calendar-day-today-border)` ⤴ prestado de `calendar` |
| 33 | `month-grid.css:186` | `[data-month-grid-cell][data-current]:not([data-selected])` | `color` | `var(--calendar-day-today-fg)` ⤴ prestado de `calendar` |
| 34 | `month-grid.css:187` | `[data-month-grid-cell][data-current]:not([data-selected])` | `font-weight` | `var(--calendar-day-today-font-weight)` ⤴ prestado de `calendar` |
| 35 | `month-grid.css:192` | `[data-month-grid-cell][data-disabled]` | `opacity` | `var(--calendar-disabled-opacity)` ⤴ prestado de `calendar` |
| 36 | `month-grid.css:196` | `[data-month-grid][data-disabled]` | `opacity` | `var(--calendar-disabled-opacity)` ⤴ prestado de `calendar` |
| 37 | `month-grid.css:204` | `[data-month-grid][data-readonly] [data-month-grid-cell]:not([data-selected]):hover` | `background` | `var(--calendar-day-bg)` ⤴ prestado de `calendar` |
| 38 | `month-grid.css:205` | `[data-month-grid][data-readonly] [data-month-grid-cell]:not([data-selected]):hover` | `border-color` | `var(--calendar-day-border)` ⤴ prestado de `calendar` |
| 39 | `month-grid.css:206` | `[data-month-grid][data-readonly] [data-month-grid-cell]:not([data-selected]):hover` | `color` | `var(--calendar-day-fg)` ⤴ prestado de `calendar` |
| 40 | `month-grid.css:217` | `[data-month-grid-cell][data-event]` | `box-shadow` | `var(--calendar-event-shadow)` ⤴ prestado de `calendar` |
### 1.2 A través de un privado (13)
_Ninguno._
### 1.2 A través de un privado (7)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `month-grid.css:15` | `[data-month-grid]` | `padding` | `var(--_month-grid-padding)` |
| 2 | `month-grid.css:22` | `[data-month-grid]` | `font-size` | `var(--_month-grid-font-size)` |
| 3 | `month-grid.css:85` | `[data-month-grid-prev-button], [data-month-grid-next-button]` | `inline-size` | `var(--_month-grid-control-size)` |
| 4 | `month-grid.css:86` | `[data-month-grid-prev-button], [data-month-grid-next-button]` | `block-size` | `var(--_month-grid-control-size)` |
| 5 | `month-grid.css:142` | `[data-month-grid-cell]` | `min-inline-size` | `calc(var(--_month-grid-cell-size) * 2)` |
| 6 | `month-grid.css:143` | `[data-month-grid-cell]` | `block-size` | `var(--_month-grid-cell-size)` |
| 7 | `month-grid.css:168` | `[data-month-grid-cell][data-selected]` | `border-color` | `var(--_month-grid-palette-solid)` |
| 8 | `month-grid.css:169` | `[data-month-grid-cell][data-selected]` | `background` | `var(--_month-grid-palette-solid)` |
| 9 | `month-grid.css:174` | `[data-month-grid-cell][data-selected]:hover` | `border-color` | `var(--_month-grid-palette-solid-hover)` |
| 10 | `month-grid.css:175` | `[data-month-grid-cell][data-selected]:hover` | `background` | `var(--_month-grid-palette-solid-hover)` |
| 11 | `month-grid.css:179` | `[data-month-grid-cell][data-focused]:not([data-selected])` | `border-color` | `var(--_month-grid-palette-border)` |
| 12 | `month-grid.css:180` | `[data-month-grid-cell][data-focused]:not([data-selected])` | `background` | `var(--_month-grid-palette-track)` |
| 13 | `month-grid.css:181` | `[data-month-grid-cell][data-focused]:not([data-selected])` | `color` | `var(--_month-grid-palette-text)` |
| 1 | `month-grid.css:147` | `[data-month-grid-cell][data-selected]` | `border-color` | `var(--_month-grid-palette-solid)` |
| 2 | `month-grid.css:148` | `[data-month-grid-cell][data-selected]` | `background` | `var(--_month-grid-palette-solid)` |
| 3 | `month-grid.css:153` | `[data-month-grid-cell][data-selected]:hover` | `border-color` | `var(--_month-grid-palette-solid-hover)` |
| 4 | `month-grid.css:154` | `[data-month-grid-cell][data-selected]:hover` | `background` | `var(--_month-grid-palette-solid-hover)` |
| 5 | `month-grid.css:158` | `[data-month-grid-cell][data-focused]:not([data-selected])` | `border-color` | `var(--_month-grid-palette-border)` |
| 6 | `month-grid.css:159` | `[data-month-grid-cell][data-focused]:not([data-selected])` | `background` | `var(--_month-grid-palette-track)` |
| 7 | `month-grid.css:160` | `[data-month-grid-cell][data-focused]:not([data-selected])` | `color` | `var(--_month-grid-palette-text)` |
### 1.3 Literales (5)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `month-grid.css:12` | `[data-month-grid]` | `inline-size` | `max-content` |
| 2 | `month-grid.css:67` | `[data-month-grid-header]` | `inline-size` | `100%` |
| 3 | `month-grid.css:124` | `[data-month-grid-grid]` | `inline-size` | `max-content` |
| 4 | `month-grid.css:132` | `[data-popover-content] [data-month-grid]` | `inline-size` | `100%` |
| 5 | `month-grid.css:135` | `[data-popover-content] [data-month-grid-grid]` | `inline-size` | `100%` |
| 2 | `month-grid.css:46` | `[data-month-grid-header]` | `inline-size` | `100%` |
| 3 | `month-grid.css:103` | `[data-month-grid-grid]` | `inline-size` | `max-content` |
| 4 | `month-grid.css:111` | `[data-popover-content] [data-month-grid]` | `inline-size` | `100%` |
| 5 | `month-grid.css:114` | `[data-popover-content] [data-month-grid-grid]` | `inline-size` | `100%` |
## 2. Sistema transversal (3) — informativo, fuera del ratio
## 2. Sistema transversal (49) — informativo, fuera del ratio
Un tema los alcanza **a nivel de sistema**, por diseño (recipe-contract §2).
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `month-grid.css:103` | `[data-month-grid-prev-button]:hover, [data-month-grid-next-button]:hover` | `background-image` | `linear-gradient(var(--state-hover), var(--state-hover))` |
| 2 | `month-grid.css:110` | `[data-month-grid-prev-button]:focus-visible, [data-month-grid-next-button]:focus-visible, [data-month-grid-cell]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
| 3 | `month-grid.css:163` | `[data-month-grid-cell]:hover` | `background-image` | `linear-gradient(var(--state-hover), var(--state-hover))` |
| 1 | `month-grid.css:11` | `[data-month-grid]` | `gap` | `var(--calendar-gap)` ⤴ prestado de `calendar` |
| 2 | `month-grid.css:15` | `[data-month-grid]` | `padding` | `var(--calendar-padding)` |
| 3 | `month-grid.css:16` | `[data-month-grid]` | `border` | `var(--calendar-border-width) solid var(--calendar-border)` ⤴ prestado de `calendar` |
| 4 | `month-grid.css:17` | `[data-month-grid]` | `border-radius` | `var(--calendar-radius)` ⤴ prestado de `calendar` |
| 5 | `month-grid.css:18` | `[data-month-grid]` | `background` | `var(--calendar-bg)` ⤴ prestado de `calendar` |
| 6 | `month-grid.css:19` | `[data-month-grid]` | `box-shadow` | `var(--calendar-shadow)` ⤴ prestado de `calendar` |
| 7 | `month-grid.css:20` | `[data-month-grid]` | `color` | `var(--calendar-fg)` ⤴ prestado de `calendar` |
| 8 | `month-grid.css:21` | `[data-month-grid]` | `font-family` | `var(--calendar-font-family)` ⤴ prestado de `calendar` |
| 9 | `month-grid.css:22` | `[data-month-grid]` | `font-size` | `var(--calendar-font-size)` |
| 10 | `month-grid.css:23` | `[data-month-grid]` | `line-height` | `var(--calendar-line-height)` ⤴ prestado de `calendar` |
| 11 | `month-grid.css:27` | `[data-month-grid][data-variant='outline']` | `background` | `var(--calendar-outline-bg)` ⤴ prestado de `calendar` |
| 12 | `month-grid.css:28` | `[data-month-grid][data-variant='outline']` | `box-shadow` | `var(--calendar-outline-shadow)` ⤴ prestado de `calendar` |
| 13 | `month-grid.css:45` | `[data-month-grid-header]` | `gap` | `var(--calendar-header-gap)` ⤴ prestado de `calendar` |
| 14 | `month-grid.css:52` | `[data-month-grid-heading]` | `color` | `var(--calendar-heading-fg)` ⤴ prestado de `calendar` |
| 15 | `month-grid.css:53` | `[data-month-grid-heading]` | `font-size` | `var(--calendar-heading-font-size)` ⤴ prestado de `calendar` |
| 16 | `month-grid.css:54` | `[data-month-grid-heading]` | `font-weight` | `var(--calendar-heading-font-weight)` ⤴ prestado de `calendar` |
| 17 | `month-grid.css:64` | `[data-month-grid-prev-button], [data-month-grid-next-button]` | `inline-size` | `var(--calendar-control-size)` |
| 18 | `month-grid.css:65` | `[data-month-grid-prev-button], [data-month-grid-next-button]` | `block-size` | `var(--calendar-control-size)` |
| 19 | `month-grid.css:67` | `[data-month-grid-prev-button], [data-month-grid-next-button]` | `border` | `var(--calendar-control-border-width) solid var(--calendar-control-border)` ⤴ prestado de `calendar` |
| 20 | `month-grid.css:68` | `[data-month-grid-prev-button], [data-month-grid-next-button]` | `border-radius` | `var(--calendar-control-radius)` ⤴ prestado de `calendar` |
| 21 | `month-grid.css:69` | `[data-month-grid-prev-button], [data-month-grid-next-button]` | `background` | `var(--calendar-control-bg)` ⤴ prestado de `calendar` |
| 22 | `month-grid.css:70` | `[data-month-grid-prev-button], [data-month-grid-next-button]` | `color` | `var(--calendar-control-fg)` ⤴ prestado de `calendar` |
| 23 | `month-grid.css:81` | `[data-month-grid-prev-button]:hover, [data-month-grid-next-button]:hover` | `border-color` | `var(--calendar-hover-control-border)` ⤴ prestado de `calendar` |
| 24 | `month-grid.css:82` | `[data-month-grid-prev-button]:hover, [data-month-grid-next-button]:hover` | `background-image` | `linear-gradient(var(--state-hover), var(--state-hover))` |
| 25 | `month-grid.css:83` | `[data-month-grid-prev-button]:hover, [data-month-grid-next-button]:hover` | `color` | `var(--calendar-hover-control-fg)` ⤴ prestado de `calendar` |
| 26 | `month-grid.css:89` | `[data-month-grid-prev-button]:focus-visible, [data-month-grid-next-button]:focus-visible, [data-month-grid-cell]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
| 27 | `month-grid.css:96` | `[data-month-grid-prev-button][data-disabled], [data-month-grid-next-button][data-disabled]` | `opacity` | `var(--calendar-disabled-opacity)` ⤴ prestado de `calendar` |
| 28 | `month-grid.css:102` | `[data-month-grid-grid]` | `gap` | `var(--calendar-grid-gap)` ⤴ prestado de `calendar` |
| 29 | `month-grid.css:121` | `[data-month-grid-cell]` | `min-inline-size` | `calc(var(--calendar-day-size) * 2)` |
| 30 | `month-grid.css:122` | `[data-month-grid-cell]` | `block-size` | `var(--calendar-day-size)` |
| 31 | `month-grid.css:123` | `[data-month-grid-cell]` | `padding-inline` | `var(--calendar-select-padding-inline, 8px)` ⤴ prestado de `calendar` |
| 32 | `month-grid.css:124` | `[data-month-grid-cell]` | `border` | `var(--calendar-day-border-width) solid var(--calendar-day-border)` ⤴ prestado de `calendar` |
| 33 | `month-grid.css:125` | `[data-month-grid-cell]` | `border-radius` | `var(--calendar-day-radius)` ⤴ prestado de `calendar` |
| 34 | `month-grid.css:126` | `[data-month-grid-cell]` | `background` | `var(--calendar-day-bg)` ⤴ prestado de `calendar` |
| 35 | `month-grid.css:127` | `[data-month-grid-cell]` | `color` | `var(--calendar-day-fg)` ⤴ prestado de `calendar` |
| 36 | `month-grid.css:129` | `[data-month-grid-cell]` | `font-weight` | `var(--calendar-day-font-weight)` ⤴ prestado de `calendar` |
| 37 | `month-grid.css:141` | `[data-month-grid-cell]:hover` | `border-color` | `var(--calendar-hover-day-border)` ⤴ prestado de `calendar` |
| 38 | `month-grid.css:142` | `[data-month-grid-cell]:hover` | `background-image` | `linear-gradient(var(--state-hover), var(--state-hover))` |
| 39 | `month-grid.css:143` | `[data-month-grid-cell]:hover` | `color` | `var(--calendar-hover-day-fg)` ⤴ prestado de `calendar` |
| 40 | `month-grid.css:149` | `[data-month-grid-cell][data-selected]` | `color` | `var(--calendar-day-selected-fg)` ⤴ prestado de `calendar` |
| 41 | `month-grid.css:164` | `[data-month-grid-cell][data-current]:not([data-selected])` | `border-color` | `var(--calendar-day-today-border)` ⤴ prestado de `calendar` |
| 42 | `month-grid.css:165` | `[data-month-grid-cell][data-current]:not([data-selected])` | `color` | `var(--calendar-day-today-fg)` ⤴ prestado de `calendar` |
| 43 | `month-grid.css:166` | `[data-month-grid-cell][data-current]:not([data-selected])` | `font-weight` | `var(--calendar-day-today-font-weight)` ⤴ prestado de `calendar` |
| 44 | `month-grid.css:171` | `[data-month-grid-cell][data-disabled]` | `opacity` | `var(--calendar-disabled-opacity)` ⤴ prestado de `calendar` |
| 45 | `month-grid.css:175` | `[data-month-grid][data-disabled]` | `opacity` | `var(--calendar-disabled-opacity)` ⤴ prestado de `calendar` |
| 46 | `month-grid.css:183` | `[data-month-grid][data-readonly] [data-month-grid-cell]:not([data-selected]):hover` | `background` | `var(--calendar-day-bg)` ⤴ prestado de `calendar` |
| 47 | `month-grid.css:184` | `[data-month-grid][data-readonly] [data-month-grid-cell]:not([data-selected]):hover` | `border-color` | `var(--calendar-day-border)` ⤴ prestado de `calendar` |
| 48 | `month-grid.css:185` | `[data-month-grid][data-readonly] [data-month-grid-cell]:not([data-selected]):hover` | `color` | `var(--calendar-day-fg)` ⤴ prestado de `calendar` |
| 49 | `month-grid.css:196` | `[data-month-grid-cell][data-event]` | `box-shadow` | `var(--calendar-event-shadow)` |
## 3. Privados de la receta — ¿de dónde sale su valor?
| privado | declaraciones | valor(es) | origen | ¿deriva de un público? |
| --- | ---: | --- | --- | :-: |
| `--_month-grid-padding` | 4 | `var(--calendar-padding-md)`, `var(--calendar-padding-xs)`, `var(--calendar-padding-sm)`, `var(--calendar-padding-lg)` | global | no |
| `--_month-grid-control-size` | 4 | `var(--calendar-control-size-md)`, `var(--calendar-control-size-xs)`, `var(--calendar-control-size-sm)`, `var(--calendar-control-size-lg)` | global | no |
| `--_month-grid-cell-size` | 4 | `var(--calendar-day-size-md)`, `var(--calendar-day-size-xs)`, `var(--calendar-day-size-sm)`, `var(--calendar-day-size-lg)` | global | no |
| `--_month-grid-font-size` | 4 | `var(--calendar-font-size-md)`, `var(--calendar-font-size-xs)`, `var(--calendar-font-size-sm)`, `var(--calendar-font-size-lg)` | global | no |
_La receta no declara privados propios en su CSS._
Consumidos y **no declarados en el CSS** (vienen de `base.ts` o de un estilo inline del wrapper): `--_month-grid-palette-border`, `--_month-grid-palette-solid`, `--_month-grid-palette-solid-hover`, `--_month-grid-palette-text`, `--_month-grid-palette-track`.
## 4. Propuesta de corrección
- **Consume la capa compartida `calendar-surface`.** Un eje que la capa posee se consume como `var(--_x, var(--x))`; el consumidor **no acuña** `--month-grid-{eje}` para él — sería un vocabulario paralelo (README de `eidos/components`, «Capas compartidas» regla 2).
- **Consume la capa compartida `picker-shell`.** Un eje que la capa posee se consume como `var(--_x, var(--x))`; el consumidor **no acuña** `--month-grid-{eje}` para él — sería un vocabulario paralelo (README de `eidos/components`, «Capas compartidas» regla 2).
- **Consume tokens públicos de `calendar`.** Un token prestado importa la semántica de su dueño: la corrección no es duplicarlo con prefijo propio, sino la decisión de familia que la auditoría de fase 1 dejó registrada (`theming-audit.md` §B, familia calendar).
- **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` (55)
### 4.1 Tokens a declarar en `lib/recipes/base.ts` (2)
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
@ -122,74 +116,13 @@ no distingue lo que debería — se marca `⚠`.
| token (`--month-grid-…`) | scope TSC | valor propuesto | usos |
| --- | --- | --- | ---: |
| `cell-fg` | `root` | ⚠ `var(--calendar-day-fg)` / `var(--calendar-day-today-fg)` | 2 |
| `cell-font-weight` | `root` | ⚠ `var(--calendar-day-font-weight)` / `var(--calendar-day-today-font-weight)` | 2 |
| `hover-cell-border` | `root` | ⚠ `var(--calendar-hover-day-border)` / `var(--calendar-day-border)` | 2 |
| `hover-cell-fg` | `root` | ⚠ `var(--calendar-hover-day-fg)` / `var(--calendar-day-fg)` | 2 |
| `gap` | `root` | `var(--calendar-gap)` | 1 |
| `width` | `root` | `max-content` | 1 |
| `radius` | `root` | `var(--calendar-radius)` | 1 |
| `bg` | `root` | `var(--calendar-bg)` | 1 |
| `shadow` | `root` | `var(--calendar-shadow)` | 1 |
| `fg` | `root` | `var(--calendar-fg)` | 1 |
| `font-family` | `root` | `var(--calendar-font-family)` | 1 |
| `font-size-md` | `size:md` | `var(--calendar-font-size-md)` | 1 |
| `font-size-xs` | `size:xs` | `var(--calendar-font-size-xs)` | 1 |
| `font-size-sm` | `size:sm` | `var(--calendar-font-size-sm)` | 1 |
| `font-size-lg` | `size:lg` | `var(--calendar-font-size-lg)` | 1 |
| `line-height` | `root` | `var(--calendar-line-height)` | 1 |
| `outline-bg` | `root` | `var(--calendar-outline-bg)` | 1 |
| `outline-shadow` | `root` | `var(--calendar-outline-shadow)` | 1 |
| `header-gap` | `root` | `var(--calendar-header-gap)` | 1 |
| `heading-fg` | `root` | `var(--calendar-heading-fg)` | 1 |
| `heading-font-size` | `root` | `var(--calendar-heading-font-size)` | 1 |
| `heading-font-weight` | `root` | `var(--calendar-heading-font-weight)` | 1 |
| `prev-button-width-md` | `size:md` | `var(--calendar-control-size-md)` | 1 |
| `prev-button-width-xs` | `size:xs` | `var(--calendar-control-size-xs)` | 1 |
| `prev-button-width-sm` | `size:sm` | `var(--calendar-control-size-sm)` | 1 |
| `prev-button-width-lg` | `size:lg` | `var(--calendar-control-size-lg)` | 1 |
| `prev-button-height-md` | `size:md` | `var(--calendar-control-size-md)` | 1 |
| `prev-button-height-xs` | `size:xs` | `var(--calendar-control-size-xs)` | 1 |
| `prev-button-height-sm` | `size:sm` | `var(--calendar-control-size-sm)` | 1 |
| `prev-button-height-lg` | `size:lg` | `var(--calendar-control-size-lg)` | 1 |
| `prev-button-radius` | `root` | `var(--calendar-control-radius)` | 1 |
| `prev-button-bg` | `root` | `var(--calendar-control-bg)` | 1 |
| `prev-button-fg` | `root` | `var(--calendar-control-fg)` | 1 |
| `hover-prev-button-border` | `root` | `var(--calendar-hover-control-border)` | 1 |
| `hover-prev-button-fg` | `root` | `var(--calendar-hover-control-fg)` | 1 |
| `disabled-prev-button-opacity` | `root` | `var(--calendar-disabled-opacity)` | 1 |
| `grid-gap` | `root` | `var(--calendar-grid-gap)` | 1 |
| `grid-width` | `root` | `max-content` | 1 |
| `cell-width-md` | `size:md` | `var(--calendar-day-size-md)` | 1 |
| `cell-width-xs` | `size:xs` | `var(--calendar-day-size-xs)` | 1 |
| `cell-width-sm` | `size:sm` | `var(--calendar-day-size-sm)` | 1 |
| `cell-width-lg` | `size:lg` | `var(--calendar-day-size-lg)` | 1 |
| `cell-height-md` | `size:md` | `var(--calendar-day-size-md)` | 1 |
| `cell-height-xs` | `size:xs` | `var(--calendar-day-size-xs)` | 1 |
| `cell-height-sm` | `size:sm` | `var(--calendar-day-size-sm)` | 1 |
| `cell-height-lg` | `size:lg` | `var(--calendar-day-size-lg)` | 1 |
| `cell-padding-inline` | `root` | `var(--calendar-select-padding-inline, 8px)` | 1 |
| `cell-radius` | `root` | `var(--calendar-day-radius)` | 1 |
| `cell-bg` | `root` | `var(--calendar-day-bg)` | 1 |
| `selected-cell-fg` | `root` | `var(--calendar-day-selected-fg)` | 1 |
| `cell-border` | `root` | `var(--calendar-day-today-border)` | 1 |
| `disabled-cell-opacity` | `root` | `var(--calendar-disabled-opacity)` | 1 |
| `disabled-opacity` | `root` | `var(--calendar-disabled-opacity)` | 1 |
| `hover-cell-bg` | `root` | `var(--calendar-day-bg)` | 1 |
| `cell-shadow` | `root` | `var(--calendar-event-shadow)` | 1 |
### 4.2 Sin nombre mecánico (14)
- **⚠ decisión: `padding` es shorthand o eje físico — hay que partirlo en ejes lógicos antes de nombrarlo (recipe-contract §1, R-4.4)** — 1: `padding`.
- **⚠ 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)** — 3: `border`.
- **⚠ 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** — 3: `inline-size`.
- **⚠ 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** — 7: `border-color`, `background`, `color`.
### 4.3 Avisos sobre los tokens propuestos (3)
### 4.2 Sin nombre mecánico (10)
- **el privado `--_month-grid-font-size` debe pasar a leer este público (o desaparecer)** — `--month-grid-font-size-md`, `--month-grid-font-size-xs`, `--month-grid-font-size-sm`, `--month-grid-font-size-lg`
- **el privado `--_month-grid-control-size` debe pasar a leer este público (o desaparecer)** — `--month-grid-prev-button-width-md`, `--month-grid-prev-button-width-xs`, `--month-grid-prev-button-width-sm`, `--month-grid-prev-button-width-lg`, `--month-grid-prev-button-height-md`, `--month-grid-prev-button-height-xs`, `--month-grid-prev-button-height-sm`, `--month-grid-prev-button-height-lg`
- **el privado `--_month-grid-cell-size` debe pasar a leer este público (o desaparecer)** — `--month-grid-cell-width-md`, `--month-grid-cell-width-xs`, `--month-grid-cell-width-sm`, `--month-grid-cell-width-lg`, `--month-grid-cell-height-md`, `--month-grid-cell-height-xs`, `--month-grid-cell-height-sm`, `--month-grid-cell-height-lg`
- **⚠ 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** — 3: `inline-size`.
- **⚠ 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** — 7: `border-color`, `background`, `color`.
### 4.4 Lo que hay que comprobar a mano (PLAN-theming §1.3 · §7.4)

@ -5,132 +5,132 @@
> 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 95 knobs por token público
- **Knobs de apariencia**: 98 — público 0 · privado 7 · global 84 · literal 4 · sistema 3 _(fuera del ratio)_
- **Medido**: 2026-08-21 · **Alcance**: **0%** — 0 de 13 knobs por token público
- **Knobs de apariencia**: 98 — público 0 · privado 7 · global 2 · literal 4 · sistema 85 _(fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 0 pública(s) · 5 privada(s) forward — `_palette-solid`, `_palette-solid-hover`, `_palette-track`, `_palette-border`, `_palette-text`
- **Eje `size`**: sí · **ficheros**: `range-calendar.css`
- **Eje `size`**: no · **ficheros**: `range-calendar.css`
## 1. Knobs fuera de alcance
### 1.1 Directo a primitivo global (84)
### 1.1 Directo a primitivo global (2)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `range-calendar.css:51` | `[data-range-calendar]` | `gap` | `var(--calendar-gap)` ⤴ prestado de `calendar` |
| 2 | `range-calendar.css:54` | `[data-range-calendar]` | `padding` | `var(--_calendar-padding)` |
| 3 | `range-calendar.css:55` | `[data-range-calendar]` | `border` | `var(--calendar-border-width) solid var(--calendar-border)` ⤴ prestado de `calendar` |
| 4 | `range-calendar.css:56` | `[data-range-calendar]` | `border-radius` | `var(--calendar-radius)` ⤴ prestado de `calendar` |
| 5 | `range-calendar.css:57` | `[data-range-calendar]` | `background` | `var(--calendar-bg)` ⤴ prestado de `calendar` |
| 6 | `range-calendar.css:58` | `[data-range-calendar]` | `box-shadow` | `var(--calendar-shadow)` ⤴ prestado de `calendar` |
| 7 | `range-calendar.css:59` | `[data-range-calendar]` | `color` | `var(--calendar-fg)` ⤴ prestado de `calendar` |
| 8 | `range-calendar.css:60` | `[data-range-calendar]` | `font-family` | `var(--calendar-font-family)` ⤴ prestado de `calendar` |
| 9 | `range-calendar.css:61` | `[data-range-calendar]` | `font-size` | `var(--_calendar-font-size)` |
| 10 | `range-calendar.css:62` | `[data-range-calendar]` | `line-height` | `var(--calendar-line-height)` ⤴ prestado de `calendar` |
| 11 | `range-calendar.css:87` | `[data-range-calendar][data-variant='outline']` | `background` | `var(--calendar-outline-bg)` ⤴ prestado de `calendar` |
| 12 | `range-calendar.css:88` | `[data-range-calendar][data-variant='outline']` | `box-shadow` | `var(--calendar-outline-shadow)` ⤴ prestado de `calendar` |
| 13 | `range-calendar.css:116` | `[data-range-calendar-header]` | `gap` | `var(--calendar-header-gap)` ⤴ prestado de `calendar` |
| 14 | `range-calendar.css:125` | `[data-range-calendar-header-controls]` | `gap` | `var(--calendar-header-gap)` ⤴ prestado de `calendar` |
| 15 | `range-calendar.css:132` | `[data-range-calendar-heading]` | `color` | `var(--calendar-heading-fg)` ⤴ prestado de `calendar` |
| 16 | `range-calendar.css:133` | `[data-range-calendar-heading]` | `font-weight` | `var(--calendar-heading-font-weight)` ⤴ prestado de `calendar` |
| 17 | `range-calendar.css:150` | `[data-range-calendar-month-select], [data-range-calendar-year-select]` | `block-size` | `var(--_calendar-control-size)` |
| 18 | `range-calendar.css:151` | `[data-range-calendar-month-select], [data-range-calendar-year-select]` | `padding-inline` | `var(--calendar-select-padding-inline)` ⤴ prestado de `calendar` |
| 19 | `range-calendar.css:152` | `[data-range-calendar-month-select], [data-range-calendar-year-select]` | `border` | `var(--calendar-control-border-width) solid var(--calendar-control-border)` ⤴ prestado de `calendar` |
| 20 | `range-calendar.css:153` | `[data-range-calendar-month-select], [data-range-calendar-year-select]` | `border-radius` | `var(--calendar-control-radius)` ⤴ prestado de `calendar` |
| 21 | `range-calendar.css:154` | `[data-range-calendar-month-select], [data-range-calendar-year-select]` | `background` | `var(--calendar-control-bg)` ⤴ prestado de `calendar` |
| 22 | `range-calendar.css:155` | `[data-range-calendar-month-select], [data-range-calendar-year-select]` | `color` | `var(--calendar-control-fg)` ⤴ prestado de `calendar` |
| 23 | `range-calendar.css:166` | `[data-range-calendar-month-select]` | `min-inline-size` | `var(--calendar-select-min-width)` ⤴ prestado de `calendar` |
| 24 | `range-calendar.css:167` | `[data-range-calendar-month-select]` | `inline-size` | `calc(var(--_calendar-day-size) * 2.35)` |
| 25 | `range-calendar.css:171` | `[data-range-calendar-year-select]` | `min-inline-size` | `var(--calendar-select-min-width)` ⤴ prestado de `calendar` |
| 26 | `range-calendar.css:172` | `[data-range-calendar-year-select]` | `inline-size` | `calc(var(--_calendar-day-size) * 2.15)` |
| 27 | `range-calendar.css:177` | `[data-range-calendar-month-select]:hover, [data-range-calendar-year-select]:hover` | `border-color` | `var(--calendar-hover-control-border)` ⤴ prestado de `calendar` |
| 28 | `range-calendar.css:179` | `[data-range-calendar-month-select]:hover, [data-range-calendar-year-select]:hover` | `color` | `var(--calendar-hover-control-fg)` ⤴ prestado de `calendar` |
| 29 | `range-calendar.css:192` | `[data-range-calendar-months]` | `gap` | `var(--calendar-months-gap)` ⤴ prestado de `calendar` |
| 30 | `range-calendar.css:203` | `[data-range-calendar-month-panel]` | `gap` | `var(--calendar-grid-gap)` ⤴ prestado de `calendar` |
| 31 | `range-calendar.css:218` | `[data-range-calendar-head-cell]` | `inline-size` | `var(--_calendar-day-size)` |
| 32 | `range-calendar.css:219` | `[data-range-calendar-head-cell]` | `block-size` | `var(--calendar-weekday-size)` ⤴ prestado de `calendar` |
| 33 | `range-calendar.css:220` | `[data-range-calendar-head-cell]` | `color` | `var(--calendar-weekday-fg)` ⤴ prestado de `calendar` |
| 34 | `range-calendar.css:221` | `[data-range-calendar-head-cell]` | `font-size` | `var(--calendar-weekday-font-size)` ⤴ prestado de `calendar` |
| 35 | `range-calendar.css:222` | `[data-range-calendar-head-cell]` | `font-weight` | `var(--calendar-weekday-font-weight)` ⤴ prestado de `calendar` |
| 36 | `range-calendar.css:227` | `[data-range-calendar-week-number]` | `color` | `var(--calendar-weekday-fg)` ⤴ prestado de `calendar` |
| 37 | `range-calendar.css:232` | `[data-range-calendar-week-number][scope='row']` | `block-size` | `var(--_calendar-day-size)` |
| 38 | `range-calendar.css:233` | `[data-range-calendar-week-number][scope='row']` | `font-size` | `var(--calendar-weekday-font-size)` ⤴ prestado de `calendar` |
| 39 | `range-calendar.css:234` | `[data-range-calendar-week-number][scope='row']` | `font-weight` | `var(--calendar-weekday-font-weight)` ⤴ prestado de `calendar` |
| 40 | `range-calendar.css:239` | `[data-range-calendar-cell]` | `border-radius` | `var(--calendar-day-radius)` ⤴ prestado de `calendar` |
| 41 | `range-calendar.css:254` | `[data-range-calendar-day]` | `inline-size` | `var(--_calendar-day-size)` |
| 42 | `range-calendar.css:255` | `[data-range-calendar-day]` | `block-size` | `var(--_calendar-day-size)` |
| 43 | `range-calendar.css:256` | `[data-range-calendar-day]` | `border` | `var(--calendar-day-border-width) solid var(--calendar-day-border)` ⤴ prestado de `calendar` |
| 44 | `range-calendar.css:257` | `[data-range-calendar-day]` | `border-radius` | `var(--calendar-day-radius)` ⤴ prestado de `calendar` |
| 45 | `range-calendar.css:258` | `[data-range-calendar-day]` | `background` | `var(--calendar-day-bg)` ⤴ prestado de `calendar` |
| 46 | `range-calendar.css:259` | `[data-range-calendar-day]` | `color` | `var(--calendar-day-fg)` ⤴ prestado de `calendar` |
| 47 | `range-calendar.css:261` | `[data-range-calendar-day]` | `font-weight` | `var(--calendar-day-font-weight)` ⤴ prestado de `calendar` |
| 48 | `range-calendar.css:273` | `[data-range-calendar-day]:hover` | `border-color` | `var(--calendar-hover-day-border)` ⤴ prestado de `calendar` |
| 49 | `range-calendar.css:275` | `[data-range-calendar-day]:hover` | `color` | `var(--calendar-hover-day-fg)` ⤴ prestado de `calendar` |
| 50 | `range-calendar.css:279` | `[data-range-calendar-day][data-selected]` | `border-color` | `var(--calendar-range-end-border)` ⤴ prestado de `calendar` |
| 51 | `range-calendar.css:280` | `[data-range-calendar-day][data-selected]` | `background` | `var(--calendar-range-end-solid)` ⤴ prestado de `calendar` |
| 52 | `range-calendar.css:281` | `[data-range-calendar-day][data-selected]` | `color` | `var(--calendar-day-selected-fg)` ⤴ prestado de `calendar` |
| 53 | `range-calendar.css:287` | `[data-range-calendar-day][data-range-start]` | `border-color` | `var(--calendar-range-start-border)` ⤴ prestado de `calendar` |
| 54 | `range-calendar.css:288` | `[data-range-calendar-day][data-range-start]` | `background` | `var(--calendar-range-start-solid)` ⤴ prestado de `calendar` |
| 55 | `range-calendar.css:289` | `[data-range-calendar-day][data-range-start]` | `color` | `var(--calendar-range-start-text)` ⤴ prestado de `calendar` |
| 56 | `range-calendar.css:290` | `[data-range-calendar-day][data-range-start]` | `box-shadow` | `inset 2px 0 0 0 var(--calendar-range-start-border)` ⤴ prestado de `calendar` |
| 57 | `range-calendar.css:291` | `[data-range-calendar-day][data-range-start]` | `font-weight` | `var(--font-weight-semibold)` |
| 58 | `range-calendar.css:296` | `[data-range-calendar-day][data-range-end]` | `border-color` | `var(--calendar-range-end-border)` ⤴ prestado de `calendar` |
| 59 | `range-calendar.css:297` | `[data-range-calendar-day][data-range-end]` | `background` | `var(--calendar-range-end-solid)` ⤴ prestado de `calendar` |
| 60 | `range-calendar.css:298` | `[data-range-calendar-day][data-range-end]` | `color` | `var(--calendar-range-end-text)` ⤴ prestado de `calendar` |
| 61 | `range-calendar.css:299` | `[data-range-calendar-day][data-range-end]` | `box-shadow` | `inset -2px 0 0 0 var(--calendar-range-end-border)` ⤴ prestado de `calendar` |
| 62 | `range-calendar.css:300` | `[data-range-calendar-day][data-range-end]` | `font-weight` | `var(--font-weight-semibold)` |
| 63 | `range-calendar.css:304` | `[data-range-calendar-day][data-range-start][data-range-end]` | `border-color` | `var(--calendar-range-end-border)` ⤴ prestado de `calendar` |
| 64 | `range-calendar.css:305` | `[data-range-calendar-day][data-range-start][data-range-end]` | `background` | `linear-gradient( 135deg, var(--calendar-range-start-solid) 0 50%, var(--calendar-range-end-solid) 50% 100% )` ⤴ prestado de `calendar` |
| 65 | `range-calendar.css:310` | `[data-range-calendar-day][data-range-start][data-range-end]` | `color` | `var(--calendar-day-selected-fg)` ⤴ prestado de `calendar` |
| 66 | `range-calendar.css:314` | `[data-range-calendar-day][data-selected]:hover` | `border-color` | `var(--calendar-range-end-solid-hover)` ⤴ prestado de `calendar` |
| 67 | `range-calendar.css:315` | `[data-range-calendar-day][data-selected]:hover` | `background` | `var(--calendar-range-end-solid-hover)` ⤴ prestado de `calendar` |
| 68 | `range-calendar.css:319` | `[data-range-calendar-day][data-range-start]:hover` | `border-color` | `var(--calendar-range-start-solid-hover)` ⤴ prestado de `calendar` |
| 69 | `range-calendar.css:320` | `[data-range-calendar-day][data-range-start]:hover` | `background` | `var(--calendar-range-start-solid-hover)` ⤴ prestado de `calendar` |
| 70 | `range-calendar.css:324` | `[data-range-calendar-day][data-range-end]:hover` | `border-color` | `var(--calendar-range-end-solid-hover)` ⤴ prestado de `calendar` |
| 71 | `range-calendar.css:325` | `[data-range-calendar-day][data-range-end]:hover` | `background` | `var(--calendar-range-end-solid-hover)` ⤴ prestado de `calendar` |
| 72 | `range-calendar.css:329` | `[data-range-calendar-day][data-range-start][data-range-end]:hover` | `background` | `linear-gradient( 135deg, var(--calendar-range-start-solid-hover) 0 50%, var(--calendar-range-end-solid-hover) 50% 100% )` ⤴ prestado de `calendar` |
| 73 | `range-calendar.css:357` | `[data-range-calendar-day][data-today]:not([data-selected])` | `border-color` | `var(--calendar-day-today-border)` ⤴ prestado de `calendar` |
| 74 | `range-calendar.css:358` | `[data-range-calendar-day][data-today]:not([data-selected])` | `color` | `var(--calendar-day-today-fg)` ⤴ prestado de `calendar` |
| 75 | `range-calendar.css:359` | `[data-range-calendar-day][data-today]:not([data-selected])` | `font-weight` | `var(--calendar-day-today-font-weight)` ⤴ prestado de `calendar` |
| 76 | `range-calendar.css:363` | `[data-range-calendar-day][data-outside-month]` | `color` | `var(--calendar-day-outside-fg)` ⤴ prestado de `calendar` |
| 77 | `range-calendar.css:364` | `[data-range-calendar-day][data-outside-month]` | `opacity` | `var(--calendar-day-outside-opacity)` ⤴ prestado de `calendar` |
| 78 | `range-calendar.css:368` | `[data-range-calendar-day][data-weekend]:not([data-selected])` | `color` | `var(--calendar-day-weekend-fg)` ⤴ prestado de `calendar` |
| 79 | `range-calendar.css:372` | `[data-range-calendar-day][data-holiday]:not([data-selected])` | `box-shadow` | `var(--calendar-day-holiday-shadow)` ⤴ prestado de `calendar` |
| 80 | `range-calendar.css:377` | `[data-range-calendar-day][data-unavailable]` | `text-decoration-color` | `var(--calendar-day-unavailable-decoration)` ⤴ prestado de `calendar` |
| 81 | `range-calendar.css:383` | `[data-range-calendar-day][data-disabled], [data-range-calendar-day][data-unavailable]` | `opacity` | `var(--calendar-disabled-opacity)` ⤴ prestado de `calendar` |
| 82 | `range-calendar.css:387` | `[data-range-calendar][data-invalid]` | `border-color` | `var(--calendar-invalid-border)` ⤴ prestado de `calendar` |
| 83 | `range-calendar.css:391` | `[data-range-calendar][data-disabled]` | `opacity` | `var(--calendar-disabled-opacity)` ⤴ prestado de `calendar` |
| 84 | `range-calendar.css:407` | `[data-range-calendar-day][data-event], [data-range-calendar-prev-button][data-event], [data-range-calendar-next-button][data-event], [data-range-calendar-month-select][data-event], [data-range-calendar-year-select][data-event]` | `box-shadow` | `var(--calendar-event-shadow)` ⤴ prestado de `calendar` |
| 1 | `range-calendar.css:269` | `[data-range-calendar-day][data-range-start]` | `font-weight` | `var(--font-weight-semibold)` |
| 2 | `range-calendar.css:278` | `[data-range-calendar-day][data-range-end]` | `font-weight` | `var(--font-weight-semibold)` |
### 1.2 A través de un privado (7)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `range-calendar.css:246` | `[data-range-calendar-cell][data-in-range], [data-range-calendar-cell][data-in-preview-range]` | `background` | `var(--_range-calendar-palette-track)` |
| 2 | `range-calendar.css:339` | `[data-range-calendar-day][data-in-range]:not([data-selected])` | `color` | `var(--_range-calendar-palette-text)` |
| 3 | `range-calendar.css:345` | `[data-range-calendar-day][data-preview-start], [data-range-calendar-day][data-preview-end], [data-range-calendar-day][data-in-preview-range]:not([data-selected])` | `border-color` | `var(--_range-calendar-palette-border)` |
| 4 | `range-calendar.css:347` | `[data-range-calendar-day][data-preview-start], [data-range-calendar-day][data-preview-end], [data-range-calendar-day][data-in-preview-range]:not([data-selected])` | `color` | `var(--_range-calendar-palette-text)` |
| 5 | `range-calendar.css:351` | `[data-range-calendar-day][data-focused]:not([data-selected])` | `border-color` | `var(--_range-calendar-palette-border)` |
| 6 | `range-calendar.css:352` | `[data-range-calendar-day][data-focused]:not([data-selected])` | `background` | `var(--_range-calendar-palette-track)` |
| 7 | `range-calendar.css:353` | `[data-range-calendar-day][data-focused]:not([data-selected])` | `color` | `var(--_range-calendar-palette-text)` |
| 1 | `range-calendar.css:224` | `[data-range-calendar-cell][data-in-range], [data-range-calendar-cell][data-in-preview-range]` | `background` | `var(--_range-calendar-palette-track)` |
| 2 | `range-calendar.css:317` | `[data-range-calendar-day][data-in-range]:not([data-selected])` | `color` | `var(--_range-calendar-palette-text)` |
| 3 | `range-calendar.css:323` | `[data-range-calendar-day][data-preview-start], [data-range-calendar-day][data-preview-end], [data-range-calendar-day][data-in-preview-range]:not([data-selected])` | `border-color` | `var(--_range-calendar-palette-border)` |
| 4 | `range-calendar.css:325` | `[data-range-calendar-day][data-preview-start], [data-range-calendar-day][data-preview-end], [data-range-calendar-day][data-in-preview-range]:not([data-selected])` | `color` | `var(--_range-calendar-palette-text)` |
| 5 | `range-calendar.css:329` | `[data-range-calendar-day][data-focused]:not([data-selected])` | `border-color` | `var(--_range-calendar-palette-border)` |
| 6 | `range-calendar.css:330` | `[data-range-calendar-day][data-focused]:not([data-selected])` | `background` | `var(--_range-calendar-palette-track)` |
| 7 | `range-calendar.css:331` | `[data-range-calendar-day][data-focused]:not([data-selected])` | `color` | `var(--_range-calendar-palette-text)` |
### 1.3 Literales (4)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `range-calendar.css:52` | `[data-range-calendar]` | `inline-size` | `max-content` |
| 2 | `range-calendar.css:117` | `[data-range-calendar-header]` | `inline-size` | `100%` |
| 3 | `range-calendar.css:204` | `[data-range-calendar-month-panel]` | `min-inline-size` | `max-content` |
| 4 | `range-calendar.css:209` | `[data-range-calendar-grid]` | `inline-size` | `max-content` |
| 1 | `range-calendar.css:51` | `[data-range-calendar]` | `inline-size` | `max-content` |
| 2 | `range-calendar.css:95` | `[data-range-calendar-header]` | `inline-size` | `100%` |
| 3 | `range-calendar.css:182` | `[data-range-calendar-month-panel]` | `min-inline-size` | `max-content` |
| 4 | `range-calendar.css:187` | `[data-range-calendar-grid]` | `inline-size` | `max-content` |
## 2. Sistema transversal (3) — informativo, fuera del ratio
## 2. Sistema transversal (85) — informativo, fuera del ratio
Un tema los alcanza **a nivel de sistema**, por diseño (recipe-contract §2).
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `range-calendar.css:143` | `[data-range-calendar-day]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
| 2 | `range-calendar.css:178` | `[data-range-calendar-month-select]:hover, [data-range-calendar-year-select]:hover` | `background-image` | `linear-gradient(var(--state-hover), var(--state-hover))` |
| 3 | `range-calendar.css:274` | `[data-range-calendar-day]:hover` | `background-image` | `linear-gradient(var(--state-hover), var(--state-hover))` |
| 1 | `range-calendar.css:50` | `[data-range-calendar]` | `gap` | `var(--calendar-gap)` ⤴ prestado de `calendar` |
| 2 | `range-calendar.css:53` | `[data-range-calendar]` | `padding` | `var(--calendar-padding)` |
| 3 | `range-calendar.css:54` | `[data-range-calendar]` | `border` | `var(--calendar-border-width) solid var(--calendar-border)` ⤴ prestado de `calendar` |
| 4 | `range-calendar.css:55` | `[data-range-calendar]` | `border-radius` | `var(--calendar-radius)` ⤴ prestado de `calendar` |
| 5 | `range-calendar.css:56` | `[data-range-calendar]` | `background` | `var(--calendar-bg)` ⤴ prestado de `calendar` |
| 6 | `range-calendar.css:57` | `[data-range-calendar]` | `box-shadow` | `var(--calendar-shadow)` ⤴ prestado de `calendar` |
| 7 | `range-calendar.css:58` | `[data-range-calendar]` | `color` | `var(--calendar-fg)` ⤴ prestado de `calendar` |
| 8 | `range-calendar.css:59` | `[data-range-calendar]` | `font-family` | `var(--calendar-font-family)` ⤴ prestado de `calendar` |
| 9 | `range-calendar.css:60` | `[data-range-calendar]` | `font-size` | `var(--calendar-font-size)` |
| 10 | `range-calendar.css:61` | `[data-range-calendar]` | `line-height` | `var(--calendar-line-height)` ⤴ prestado de `calendar` |
| 11 | `range-calendar.css:65` | `[data-range-calendar][data-variant='outline']` | `background` | `var(--calendar-outline-bg)` ⤴ prestado de `calendar` |
| 12 | `range-calendar.css:66` | `[data-range-calendar][data-variant='outline']` | `box-shadow` | `var(--calendar-outline-shadow)` ⤴ prestado de `calendar` |
| 13 | `range-calendar.css:94` | `[data-range-calendar-header]` | `gap` | `var(--calendar-header-gap)` ⤴ prestado de `calendar` |
| 14 | `range-calendar.css:103` | `[data-range-calendar-header-controls]` | `gap` | `var(--calendar-header-gap)` ⤴ prestado de `calendar` |
| 15 | `range-calendar.css:110` | `[data-range-calendar-heading]` | `color` | `var(--calendar-heading-fg)` ⤴ prestado de `calendar` |
| 16 | `range-calendar.css:111` | `[data-range-calendar-heading]` | `font-weight` | `var(--calendar-heading-font-weight)` ⤴ prestado de `calendar` |
| 17 | `range-calendar.css:121` | `[data-range-calendar-day]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
| 18 | `range-calendar.css:128` | `[data-range-calendar-month-select], [data-range-calendar-year-select]` | `block-size` | `var(--calendar-control-size)` |
| 19 | `range-calendar.css:129` | `[data-range-calendar-month-select], [data-range-calendar-year-select]` | `padding-inline` | `var(--calendar-select-padding-inline)` ⤴ prestado de `calendar` |
| 20 | `range-calendar.css:130` | `[data-range-calendar-month-select], [data-range-calendar-year-select]` | `border` | `var(--calendar-control-border-width) solid var(--calendar-control-border)` ⤴ prestado de `calendar` |
| 21 | `range-calendar.css:131` | `[data-range-calendar-month-select], [data-range-calendar-year-select]` | `border-radius` | `var(--calendar-control-radius)` ⤴ prestado de `calendar` |
| 22 | `range-calendar.css:132` | `[data-range-calendar-month-select], [data-range-calendar-year-select]` | `background` | `var(--calendar-control-bg)` ⤴ prestado de `calendar` |
| 23 | `range-calendar.css:133` | `[data-range-calendar-month-select], [data-range-calendar-year-select]` | `color` | `var(--calendar-control-fg)` ⤴ prestado de `calendar` |
| 24 | `range-calendar.css:144` | `[data-range-calendar-month-select]` | `min-inline-size` | `var(--calendar-select-min-width)` ⤴ prestado de `calendar` |
| 25 | `range-calendar.css:145` | `[data-range-calendar-month-select]` | `inline-size` | `calc(var(--calendar-day-size) * 2.35)` |
| 26 | `range-calendar.css:149` | `[data-range-calendar-year-select]` | `min-inline-size` | `var(--calendar-select-min-width)` ⤴ prestado de `calendar` |
| 27 | `range-calendar.css:150` | `[data-range-calendar-year-select]` | `inline-size` | `calc(var(--calendar-day-size) * 2.15)` |
| 28 | `range-calendar.css:155` | `[data-range-calendar-month-select]:hover, [data-range-calendar-year-select]:hover` | `border-color` | `var(--calendar-hover-control-border)` ⤴ prestado de `calendar` |
| 29 | `range-calendar.css:156` | `[data-range-calendar-month-select]:hover, [data-range-calendar-year-select]:hover` | `background-image` | `linear-gradient(var(--state-hover), var(--state-hover))` |
| 30 | `range-calendar.css:157` | `[data-range-calendar-month-select]:hover, [data-range-calendar-year-select]:hover` | `color` | `var(--calendar-hover-control-fg)` ⤴ prestado de `calendar` |
| 31 | `range-calendar.css:170` | `[data-range-calendar-months]` | `gap` | `var(--calendar-months-gap)` ⤴ prestado de `calendar` |
| 32 | `range-calendar.css:181` | `[data-range-calendar-month-panel]` | `gap` | `var(--calendar-grid-gap)` ⤴ prestado de `calendar` |
| 33 | `range-calendar.css:196` | `[data-range-calendar-head-cell]` | `inline-size` | `var(--calendar-day-size)` |
| 34 | `range-calendar.css:197` | `[data-range-calendar-head-cell]` | `block-size` | `var(--calendar-weekday-size)` ⤴ prestado de `calendar` |
| 35 | `range-calendar.css:198` | `[data-range-calendar-head-cell]` | `color` | `var(--calendar-weekday-fg)` ⤴ prestado de `calendar` |
| 36 | `range-calendar.css:199` | `[data-range-calendar-head-cell]` | `font-size` | `var(--calendar-weekday-font-size)` ⤴ prestado de `calendar` |
| 37 | `range-calendar.css:200` | `[data-range-calendar-head-cell]` | `font-weight` | `var(--calendar-weekday-font-weight)` ⤴ prestado de `calendar` |
| 38 | `range-calendar.css:205` | `[data-range-calendar-week-number]` | `color` | `var(--calendar-weekday-fg)` ⤴ prestado de `calendar` |
| 39 | `range-calendar.css:210` | `[data-range-calendar-week-number][scope='row']` | `block-size` | `var(--calendar-day-size)` |
| 40 | `range-calendar.css:211` | `[data-range-calendar-week-number][scope='row']` | `font-size` | `var(--calendar-weekday-font-size)` ⤴ prestado de `calendar` |
| 41 | `range-calendar.css:212` | `[data-range-calendar-week-number][scope='row']` | `font-weight` | `var(--calendar-weekday-font-weight)` ⤴ prestado de `calendar` |
| 42 | `range-calendar.css:217` | `[data-range-calendar-cell]` | `border-radius` | `var(--calendar-day-radius)` ⤴ prestado de `calendar` |
| 43 | `range-calendar.css:232` | `[data-range-calendar-day]` | `inline-size` | `var(--calendar-day-size)` |
| 44 | `range-calendar.css:233` | `[data-range-calendar-day]` | `block-size` | `var(--calendar-day-size)` |
| 45 | `range-calendar.css:234` | `[data-range-calendar-day]` | `border` | `var(--calendar-day-border-width) solid var(--calendar-day-border)` ⤴ prestado de `calendar` |
| 46 | `range-calendar.css:235` | `[data-range-calendar-day]` | `border-radius` | `var(--calendar-day-radius)` ⤴ prestado de `calendar` |
| 47 | `range-calendar.css:236` | `[data-range-calendar-day]` | `background` | `var(--calendar-day-bg)` ⤴ prestado de `calendar` |
| 48 | `range-calendar.css:237` | `[data-range-calendar-day]` | `color` | `var(--calendar-day-fg)` ⤴ prestado de `calendar` |
| 49 | `range-calendar.css:239` | `[data-range-calendar-day]` | `font-weight` | `var(--calendar-day-font-weight)` ⤴ prestado de `calendar` |
| 50 | `range-calendar.css:251` | `[data-range-calendar-day]:hover` | `border-color` | `var(--calendar-hover-day-border)` ⤴ prestado de `calendar` |
| 51 | `range-calendar.css:252` | `[data-range-calendar-day]:hover` | `background-image` | `linear-gradient(var(--state-hover), var(--state-hover))` |
| 52 | `range-calendar.css:253` | `[data-range-calendar-day]:hover` | `color` | `var(--calendar-hover-day-fg)` ⤴ prestado de `calendar` |
| 53 | `range-calendar.css:257` | `[data-range-calendar-day][data-selected]` | `border-color` | `var(--calendar-range-end-border)` ⤴ prestado de `calendar` |
| 54 | `range-calendar.css:258` | `[data-range-calendar-day][data-selected]` | `background` | `var(--calendar-range-end-solid)` ⤴ prestado de `calendar` |
| 55 | `range-calendar.css:259` | `[data-range-calendar-day][data-selected]` | `color` | `var(--calendar-day-selected-fg)` ⤴ prestado de `calendar` |
| 56 | `range-calendar.css:265` | `[data-range-calendar-day][data-range-start]` | `border-color` | `var(--calendar-range-start-border)` ⤴ prestado de `calendar` |
| 57 | `range-calendar.css:266` | `[data-range-calendar-day][data-range-start]` | `background` | `var(--calendar-range-start-solid)` ⤴ prestado de `calendar` |
| 58 | `range-calendar.css:267` | `[data-range-calendar-day][data-range-start]` | `color` | `var(--calendar-range-start-text)` ⤴ prestado de `calendar` |
| 59 | `range-calendar.css:268` | `[data-range-calendar-day][data-range-start]` | `box-shadow` | `inset 2px 0 0 0 var(--calendar-range-start-border)` ⤴ prestado de `calendar` |
| 60 | `range-calendar.css:274` | `[data-range-calendar-day][data-range-end]` | `border-color` | `var(--calendar-range-end-border)` ⤴ prestado de `calendar` |
| 61 | `range-calendar.css:275` | `[data-range-calendar-day][data-range-end]` | `background` | `var(--calendar-range-end-solid)` ⤴ prestado de `calendar` |
| 62 | `range-calendar.css:276` | `[data-range-calendar-day][data-range-end]` | `color` | `var(--calendar-range-end-text)` ⤴ prestado de `calendar` |
| 63 | `range-calendar.css:277` | `[data-range-calendar-day][data-range-end]` | `box-shadow` | `inset -2px 0 0 0 var(--calendar-range-end-border)` ⤴ prestado de `calendar` |
| 64 | `range-calendar.css:282` | `[data-range-calendar-day][data-range-start][data-range-end]` | `border-color` | `var(--calendar-range-end-border)` ⤴ prestado de `calendar` |
| 65 | `range-calendar.css:283` | `[data-range-calendar-day][data-range-start][data-range-end]` | `background` | `linear-gradient( 135deg, var(--calendar-range-start-solid) 0 50%, var(--calendar-range-end-solid) 50% 100% )` ⤴ prestado de `calendar` |
| 66 | `range-calendar.css:288` | `[data-range-calendar-day][data-range-start][data-range-end]` | `color` | `var(--calendar-day-selected-fg)` ⤴ prestado de `calendar` |
| 67 | `range-calendar.css:292` | `[data-range-calendar-day][data-selected]:hover` | `border-color` | `var(--calendar-range-end-solid-hover)` ⤴ prestado de `calendar` |
| 68 | `range-calendar.css:293` | `[data-range-calendar-day][data-selected]:hover` | `background` | `var(--calendar-range-end-solid-hover)` ⤴ prestado de `calendar` |
| 69 | `range-calendar.css:297` | `[data-range-calendar-day][data-range-start]:hover` | `border-color` | `var(--calendar-range-start-solid-hover)` ⤴ prestado de `calendar` |
| 70 | `range-calendar.css:298` | `[data-range-calendar-day][data-range-start]:hover` | `background` | `var(--calendar-range-start-solid-hover)` ⤴ prestado de `calendar` |
| 71 | `range-calendar.css:302` | `[data-range-calendar-day][data-range-end]:hover` | `border-color` | `var(--calendar-range-end-solid-hover)` ⤴ prestado de `calendar` |
| 72 | `range-calendar.css:303` | `[data-range-calendar-day][data-range-end]:hover` | `background` | `var(--calendar-range-end-solid-hover)` ⤴ prestado de `calendar` |
| 73 | `range-calendar.css:307` | `[data-range-calendar-day][data-range-start][data-range-end]:hover` | `background` | `linear-gradient( 135deg, var(--calendar-range-start-solid-hover) 0 50%, var(--calendar-range-end-solid-hover) 50% 100% )` ⤴ prestado de `calendar` |
| 74 | `range-calendar.css:335` | `[data-range-calendar-day][data-today]:not([data-selected])` | `border-color` | `var(--calendar-day-today-border)` ⤴ prestado de `calendar` |
| 75 | `range-calendar.css:336` | `[data-range-calendar-day][data-today]:not([data-selected])` | `color` | `var(--calendar-day-today-fg)` ⤴ prestado de `calendar` |
| 76 | `range-calendar.css:337` | `[data-range-calendar-day][data-today]:not([data-selected])` | `font-weight` | `var(--calendar-day-today-font-weight)` ⤴ prestado de `calendar` |
| 77 | `range-calendar.css:341` | `[data-range-calendar-day][data-outside-month]` | `color` | `var(--calendar-day-outside-fg)` ⤴ prestado de `calendar` |
| 78 | `range-calendar.css:342` | `[data-range-calendar-day][data-outside-month]` | `opacity` | `var(--calendar-day-outside-opacity)` ⤴ prestado de `calendar` |
| 79 | `range-calendar.css:346` | `[data-range-calendar-day][data-weekend]:not([data-selected])` | `color` | `var(--calendar-day-weekend-fg)` ⤴ prestado de `calendar` |
| 80 | `range-calendar.css:350` | `[data-range-calendar-day][data-holiday]:not([data-selected])` | `box-shadow` | `var(--calendar-day-holiday-shadow)` |
| 81 | `range-calendar.css:355` | `[data-range-calendar-day][data-unavailable]` | `text-decoration-color` | `var(--calendar-day-unavailable-decoration)` ⤴ prestado de `calendar` |
| 82 | `range-calendar.css:361` | `[data-range-calendar-day][data-disabled], [data-range-calendar-day][data-unavailable]` | `opacity` | `var(--calendar-disabled-opacity)` ⤴ prestado de `calendar` |
| 83 | `range-calendar.css:365` | `[data-range-calendar][data-invalid]` | `border-color` | `var(--calendar-invalid-border)` ⤴ prestado de `calendar` |
| 84 | `range-calendar.css:369` | `[data-range-calendar][data-disabled]` | `opacity` | `var(--calendar-disabled-opacity)` ⤴ prestado de `calendar` |
| 85 | `range-calendar.css:385` | `[data-range-calendar-day][data-event], [data-range-calendar-prev-button][data-event], [data-range-calendar-next-button][data-event], [data-range-calendar-month-select][data-event], [data-range-calendar-year-select][data-event]` | `box-shadow` | `var(--calendar-event-shadow)` |
## 3. Privados de la receta — ¿de dónde sale su valor?
@ -140,10 +140,10 @@ 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 `calendar-surface`.** Un eje que la capa posee se consume como `var(--_x, var(--x))`; el consumidor **no acuña** `--range-calendar-{eje}` para él — sería un vocabulario paralelo (README de `eidos/components`, «Capas compartidas» regla 2).
- **Consume tokens públicos de `calendar`.** Un token prestado importa la semántica de su dueño: la corrección no es duplicarlo con prefijo propio, sino la decisión de familia que la auditoría de fase 1 dejó registrada (`theming-audit.md` §B, familia calendar).
- **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` (59)
### 4.1 Tokens a declarar en `lib/recipes/base.ts` (4)
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
@ -153,76 +153,15 @@ no distingue lo que debería — se marca `⚠`.
| token (`--range-calendar-…`) | scope TSC | valor propuesto | usos |
| --- | --- | --- | ---: |
| `day-fg` | `root` | ⚠ `var(--calendar-day-fg)` / `var(--calendar-range-start-text)` / `var(--calendar-range-end-text)` / `var(--calendar-day-selected-fg)` / `var(--calendar-day-today-fg)` / `var(--calendar-day-outside-fg)` / `var(--calendar-day-weekend-fg)` | 7 |
| `day-bg` | `root` | ⚠ `var(--calendar-day-bg)` / `var(--calendar-range-start-solid)` / `var(--calendar-range-end-solid)` / `linear-gradient( 135deg, var(--calendar-range-start-solid) 0 50%, var(--calendar-range-end-solid) 50% 100% )` | 4 |
| `day-font-weight` | `root` | ⚠ `var(--calendar-day-font-weight)` / `var(--font-weight-semibold)` / `var(--calendar-day-today-font-weight)` | 4 |
| `day-border` | `root` | ⚠ `var(--calendar-range-start-border)` / `var(--calendar-range-end-border)` / `var(--calendar-day-today-border)` | 4 |
| `day-shadow` | `root` | ⚠ `inset 2px 0 0 0 var(--calendar-range-start-border)` / `inset -2px 0 0 0 var(--calendar-range-end-border)` / `var(--calendar-day-holiday-shadow)` / `var(--calendar-event-shadow)` | 4 |
| `hover-day-border` | `root` | ⚠ `var(--calendar-hover-day-border)` / `var(--calendar-range-start-solid-hover)` / `var(--calendar-range-end-solid-hover)` | 3 |
| `hover-day-bg` | `root` | ⚠ `var(--calendar-range-start-solid-hover)` / `var(--calendar-range-end-solid-hover)` / `linear-gradient( 135deg, var(--calendar-range-start-solid-hover) 0 50%, var(--calendar-range-end-solid-hover) 50% 100% )` | 3 |
| `month-select-width` | `root` | ⚠ `var(--calendar-select-min-width)` / `calc(var(--_calendar-day-size) * 2.35)` | 2 |
| `year-select-width` | `root` | ⚠ `var(--calendar-select-min-width)` / `calc(var(--_calendar-day-size) * 2.15)` | 2 |
| `gap` | `root` | `var(--calendar-gap)` | 1 |
| `day-font-weight` | `root` | `var(--font-weight-semibold)` | 2 |
| `width` | `root` | `max-content` | 1 |
| `radius` | `root` | `var(--calendar-radius)` | 1 |
| `bg` | `root` | `var(--calendar-bg)` | 1 |
| `shadow` | `root` | `var(--calendar-shadow)` | 1 |
| `fg` | `root` | `var(--calendar-fg)` | 1 |
| `font-family` | `root` | `var(--calendar-font-family)` | 1 |
| `font-size` | `root` | `var(--_calendar-font-size)` | 1 |
| `line-height` | `root` | `var(--calendar-line-height)` | 1 |
| `outline-bg` | `root` | `var(--calendar-outline-bg)` | 1 |
| `outline-shadow` | `root` | `var(--calendar-outline-shadow)` | 1 |
| `header-gap` | `root` | `var(--calendar-header-gap)` | 1 |
| `header-controls-gap` | `root` | `var(--calendar-header-gap)` | 1 |
| `heading-fg` | `root` | `var(--calendar-heading-fg)` | 1 |
| `heading-font-weight` | `root` | `var(--calendar-heading-font-weight)` | 1 |
| `month-select-height` | `root` | `var(--_calendar-control-size)` | 1 |
| `month-select-padding-inline` | `root` | `var(--calendar-select-padding-inline)` | 1 |
| `month-select-radius` | `root` | `var(--calendar-control-radius)` | 1 |
| `month-select-bg` | `root` | `var(--calendar-control-bg)` | 1 |
| `month-select-fg` | `root` | `var(--calendar-control-fg)` | 1 |
| `hover-month-select-border` | `root` | `var(--calendar-hover-control-border)` | 1 |
| `hover-month-select-fg` | `root` | `var(--calendar-hover-control-fg)` | 1 |
| `months-gap` | `root` | `var(--calendar-months-gap)` | 1 |
| `month-panel-gap` | `root` | `var(--calendar-grid-gap)` | 1 |
| `month-panel-width` | `root` | `max-content` | 1 |
| `grid-width` | `root` | `max-content` | 1 |
| `head-cell-width` | `root` | `var(--_calendar-day-size)` | 1 |
| `head-cell-height` | `root` | `var(--calendar-weekday-size)` | 1 |
| `head-cell-fg` | `root` | `var(--calendar-weekday-fg)` | 1 |
| `head-cell-font-size` | `root` | `var(--calendar-weekday-font-size)` | 1 |
| `head-cell-font-weight` | `root` | `var(--calendar-weekday-font-weight)` | 1 |
| `week-number-fg` | `root` | `var(--calendar-weekday-fg)` | 1 |
| `week-number-height` | `root` | `var(--_calendar-day-size)` | 1 |
| `week-number-font-size` | `root` | `var(--calendar-weekday-font-size)` | 1 |
| `week-number-font-weight` | `root` | `var(--calendar-weekday-font-weight)` | 1 |
| `cell-radius` | `root` | `var(--calendar-day-radius)` | 1 |
| `day-width` | `root` | `var(--_calendar-day-size)` | 1 |
| `day-height` | `root` | `var(--_calendar-day-size)` | 1 |
| `day-radius` | `root` | `var(--calendar-day-radius)` | 1 |
| `hover-day-fg` | `root` | `var(--calendar-hover-day-fg)` | 1 |
| `selected-day-border` | `root` | `var(--calendar-range-end-border)` | 1 |
| `selected-day-bg` | `root` | `var(--calendar-range-end-solid)` | 1 |
| `selected-day-fg` | `root` | `var(--calendar-day-selected-fg)` | 1 |
| `hover-selected-day-border` | `root` | `var(--calendar-range-end-solid-hover)` | 1 |
| `hover-selected-day-bg` | `root` | `var(--calendar-range-end-solid-hover)` | 1 |
| `day-opacity` | `root` | `var(--calendar-day-outside-opacity)` | 1 |
| `day-underline` | `root` | `var(--calendar-day-unavailable-decoration)` | 1 |
| `disabled-day-opacity` | `root` | `var(--calendar-disabled-opacity)` | 1 |
| `invalid-border` | `root` | `var(--calendar-invalid-border)` | 1 |
| `disabled-opacity` | `root` | `var(--calendar-disabled-opacity)` | 1 |
### 4.2 Sin nombre mecánico (12)
- **⚠ decisión: `padding` es shorthand o eje físico — hay que partirlo en ejes lógicos antes de nombrarlo (recipe-contract §1, R-4.4)** — 1: `padding`.
- **⚠ 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)** — 3: `border`.
- **⚠ 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`.
- **⚠ 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** — 7: `background`, `color`, `border-color`.
### 4.3 Avisos sobre los tokens propuestos (1)
### 4.2 Sin nombre mecánico (8)
- **⚠ decisión: el valor es una expresión — el token puede llevar la expresión entera o sólo su término variable** — `--range-calendar-month-select-width`, `--range-calendar-year-select-width`
- **⚠ 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`.
- **⚠ 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** — 7: `background`, `color`, `border-color`.
### 4.4 Lo que hay que comprobar a mano (PLAN-theming §1.3 · §7.4)

@ -5,7 +5,7 @@
> 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**: **86%** — 37 de 43 knobs por token público
- **Medido**: 2026-08-21 · **Alcance**: **86%** — 37 de 43 knobs por token público
- **Knobs de apariencia**: 46 — público 37 · privado 3 · global 0 · literal 3 · sistema 3 _(fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 45 pública(s) — `row-height-xs`, `row-height-sm`, `row-height-md`, `row-height-lg`, `row-height-xl`, `cell-padding-inline-xs`, `cell-padding-inline-sm`, `cell-padding-inline-md`, `cell-padding-inline-lg`, `cell-padding-inline-xl`, `cell-padding-block-xs`, `cell-padding-block-sm`, `cell-padding-block-md`, `cell-padding-block-lg`, `cell-padding-block-xl`, `font-size-xs`, `font-size-sm`, `font-size-md`, `font-size-lg`, `font-size-xl`, `row-height`, `cell-padding-inline`, `cell-padding-block`, `font-size`, `radius`, `font-family`, `fg`, `line-height`, `border-width`, `divider`, `surface-bg`, `row-bg`, `header-bg`, `header-fg`, `header-font-weight`, `header-z`, `striped-row-bg`, `hover-row-bg`, `row-detail-bg`, `row-detail-fg`, `row-detail-padding-block`, `sort-trigger-gap`, `sort-trigger-radius`, `sort-indicator-size`, `sort-indicator-opacity` · 2 privada(s) forward — `_palette-solid`, `_palette-element`
- **Eje `size`**: no · **ficheros**: `table.css`
@ -21,8 +21,8 @@ _Ninguno._
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `table.css:167` | `[data-table-column-header][data-sorted]` | `color` | `var(--_table-palette-solid)` |
| 2 | `table.css:212` | `[data-table-body] [data-table-row][data-selected]` | `background` | `var(--_table-palette-element)` |
| 3 | `table.css:303` | `[data-table-sort-trigger][data-sorted]` | `color` | `var(--_table-palette-solid)` |
| 2 | `table.css:223` | `[data-table-body] [data-table-row][data-selected]` | `background` | `var(--_table-palette-element)` |
| 3 | `table.css:314` | `[data-table-sort-trigger][data-sorted]` | `color` | `var(--_table-palette-solid)` |
### 1.3 Literales (3)
@ -30,7 +30,7 @@ _Ninguno._
| ---: | --- | --- | --- | --- |
| 1 | `table.css:55` | `[data-table-root][data-block]` | `inline-size` | `100%` |
| 2 | `table.css:121` | `[data-table]` | `inline-size` | `100%` |
| 3 | `table.css:317` | `[data-table-sort-trigger][data-sorted] [data-table-sort-indicator]` | `opacity` | `1` |
| 3 | `table.css:328` | `[data-table-sort-trigger][data-sorted] [data-table-sort-indicator]` | `opacity` | `1` |
## 2. Sistema transversal (3) — informativo, fuera del ratio
@ -39,8 +39,8 @@ Un tema los alcanza **a nivel de sistema**, por diseño (recipe-contract §2).
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `table.css:115` | `[data-table-root][data-disabled]` | `opacity` | `var(--opacity-disabled)` |
| 2 | `table.css:226` | `[data-table-row]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
| 3 | `table.css:298` | `[data-table-sort-trigger]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
| 2 | `table.css:237` | `[data-table-row]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
| 3 | `table.css:309` | `[data-table-sort-trigger]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
## 3. Privados de la receta — ¿de dónde sale su valor?

@ -5,114 +5,108 @@
> 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 58 knobs por token público
- **Knobs de apariencia**: 61 — público 0 · privado 13 · global 40 · literal 5 · sistema 3 _(fuera del ratio)_
- **Medido**: 2026-08-21 · **Alcance**: **0%** — 0 de 12 knobs por token público
- **Knobs de apariencia**: 61 — público 0 · privado 7 · global 0 · literal 5 · sistema 49 _(fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 0 pública(s) · 5 privada(s) forward — `_palette-solid`, `_palette-solid-hover`, `_palette-track`, `_palette-border`, `_palette-text`
- **Eje `size`**: sí · **ficheros**: `year-grid.css`
- **Eje `size`**: no · **ficheros**: `year-grid.css`
## 1. Knobs fuera de alcance
### 1.1 Directo a primitivo global (40)
### 1.1 Directo a primitivo global (0)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `year-grid.css:11` | `[data-year-grid]` | `gap` | `var(--calendar-gap)` ⤴ prestado de `calendar` |
| 2 | `year-grid.css:16` | `[data-year-grid]` | `border` | `var(--calendar-border-width) solid var(--calendar-border)` ⤴ prestado de `calendar` |
| 3 | `year-grid.css:17` | `[data-year-grid]` | `border-radius` | `var(--calendar-radius)` ⤴ prestado de `calendar` |
| 4 | `year-grid.css:18` | `[data-year-grid]` | `background` | `var(--calendar-bg)` ⤴ prestado de `calendar` |
| 5 | `year-grid.css:19` | `[data-year-grid]` | `box-shadow` | `var(--calendar-shadow)` ⤴ prestado de `calendar` |
| 6 | `year-grid.css:20` | `[data-year-grid]` | `color` | `var(--calendar-fg)` ⤴ prestado de `calendar` |
| 7 | `year-grid.css:21` | `[data-year-grid]` | `font-family` | `var(--calendar-font-family)` ⤴ prestado de `calendar` |
| 8 | `year-grid.css:23` | `[data-year-grid]` | `line-height` | `var(--calendar-line-height)` ⤴ prestado de `calendar` |
| 9 | `year-grid.css:48` | `[data-year-grid][data-variant='outline']` | `background` | `var(--calendar-outline-bg)` ⤴ prestado de `calendar` |
| 10 | `year-grid.css:49` | `[data-year-grid][data-variant='outline']` | `box-shadow` | `var(--calendar-outline-shadow)` ⤴ prestado de `calendar` |
| 11 | `year-grid.css:66` | `[data-year-grid-header]` | `gap` | `var(--calendar-header-gap)` ⤴ prestado de `calendar` |
| 12 | `year-grid.css:73` | `[data-year-grid-heading]` | `color` | `var(--calendar-heading-fg)` ⤴ prestado de `calendar` |
| 13 | `year-grid.css:74` | `[data-year-grid-heading]` | `font-size` | `var(--calendar-heading-font-size)` ⤴ prestado de `calendar` |
| 14 | `year-grid.css:75` | `[data-year-grid-heading]` | `font-weight` | `var(--calendar-heading-font-weight)` ⤴ prestado de `calendar` |
| 15 | `year-grid.css:88` | `[data-year-grid-prev-button], [data-year-grid-next-button]` | `border` | `var(--calendar-control-border-width) solid var(--calendar-control-border)` ⤴ prestado de `calendar` |
| 16 | `year-grid.css:89` | `[data-year-grid-prev-button], [data-year-grid-next-button]` | `border-radius` | `var(--calendar-control-radius)` ⤴ prestado de `calendar` |
| 17 | `year-grid.css:90` | `[data-year-grid-prev-button], [data-year-grid-next-button]` | `background` | `var(--calendar-control-bg)` ⤴ prestado de `calendar` |
| 18 | `year-grid.css:91` | `[data-year-grid-prev-button], [data-year-grid-next-button]` | `color` | `var(--calendar-control-fg)` ⤴ prestado de `calendar` |
| 19 | `year-grid.css:102` | `[data-year-grid-prev-button]:hover, [data-year-grid-next-button]:hover` | `border-color` | `var(--calendar-hover-control-border)` ⤴ prestado de `calendar` |
| 20 | `year-grid.css:104` | `[data-year-grid-prev-button]:hover, [data-year-grid-next-button]:hover` | `color` | `var(--calendar-hover-control-fg)` ⤴ prestado de `calendar` |
| 21 | `year-grid.css:117` | `[data-year-grid-prev-button][data-disabled], [data-year-grid-next-button][data-disabled]` | `opacity` | `var(--calendar-disabled-opacity)` ⤴ prestado de `calendar` |
| 22 | `year-grid.css:123` | `[data-year-grid-grid]` | `gap` | `var(--calendar-grid-gap)` ⤴ prestado de `calendar` |
| 23 | `year-grid.css:144` | `[data-year-grid-cell]` | `padding-inline` | `var(--calendar-select-padding-inline, 8px)` ⤴ prestado de `calendar` |
| 24 | `year-grid.css:145` | `[data-year-grid-cell]` | `border` | `var(--calendar-day-border-width) solid var(--calendar-day-border)` ⤴ prestado de `calendar` |
| 25 | `year-grid.css:146` | `[data-year-grid-cell]` | `border-radius` | `var(--calendar-day-radius)` ⤴ prestado de `calendar` |
| 26 | `year-grid.css:147` | `[data-year-grid-cell]` | `background` | `var(--calendar-day-bg)` ⤴ prestado de `calendar` |
| 27 | `year-grid.css:148` | `[data-year-grid-cell]` | `color` | `var(--calendar-day-fg)` ⤴ prestado de `calendar` |
| 28 | `year-grid.css:150` | `[data-year-grid-cell]` | `font-weight` | `var(--calendar-day-font-weight)` ⤴ prestado de `calendar` |
| 29 | `year-grid.css:162` | `[data-year-grid-cell]:hover` | `border-color` | `var(--calendar-hover-day-border)` ⤴ prestado de `calendar` |
| 30 | `year-grid.css:164` | `[data-year-grid-cell]:hover` | `color` | `var(--calendar-hover-day-fg)` ⤴ prestado de `calendar` |
| 31 | `year-grid.css:170` | `[data-year-grid-cell][data-selected]` | `color` | `var(--calendar-day-selected-fg)` ⤴ prestado de `calendar` |
| 32 | `year-grid.css:185` | `[data-year-grid-cell][data-current]:not([data-selected])` | `border-color` | `var(--calendar-day-today-border)` ⤴ prestado de `calendar` |
| 33 | `year-grid.css:186` | `[data-year-grid-cell][data-current]:not([data-selected])` | `color` | `var(--calendar-day-today-fg)` ⤴ prestado de `calendar` |
| 34 | `year-grid.css:187` | `[data-year-grid-cell][data-current]:not([data-selected])` | `font-weight` | `var(--calendar-day-today-font-weight)` ⤴ prestado de `calendar` |
| 35 | `year-grid.css:192` | `[data-year-grid-cell][data-disabled]` | `opacity` | `var(--calendar-disabled-opacity)` ⤴ prestado de `calendar` |
| 36 | `year-grid.css:196` | `[data-year-grid][data-disabled]` | `opacity` | `var(--calendar-disabled-opacity)` ⤴ prestado de `calendar` |
| 37 | `year-grid.css:204` | `[data-year-grid][data-readonly] [data-year-grid-cell]:not([data-selected]):hover` | `background` | `var(--calendar-day-bg)` ⤴ prestado de `calendar` |
| 38 | `year-grid.css:205` | `[data-year-grid][data-readonly] [data-year-grid-cell]:not([data-selected]):hover` | `border-color` | `var(--calendar-day-border)` ⤴ prestado de `calendar` |
| 39 | `year-grid.css:206` | `[data-year-grid][data-readonly] [data-year-grid-cell]:not([data-selected]):hover` | `color` | `var(--calendar-day-fg)` ⤴ prestado de `calendar` |
| 40 | `year-grid.css:215` | `[data-year-grid-cell][data-event]` | `box-shadow` | `var(--calendar-event-shadow)` ⤴ prestado de `calendar` |
### 1.2 A través de un privado (13)
_Ninguno._
### 1.2 A través de un privado (7)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `year-grid.css:15` | `[data-year-grid]` | `padding` | `var(--_year-grid-padding)` |
| 2 | `year-grid.css:22` | `[data-year-grid]` | `font-size` | `var(--_year-grid-font-size)` |
| 3 | `year-grid.css:85` | `[data-year-grid-prev-button], [data-year-grid-next-button]` | `inline-size` | `var(--_year-grid-control-size)` |
| 4 | `year-grid.css:86` | `[data-year-grid-prev-button], [data-year-grid-next-button]` | `block-size` | `var(--_year-grid-control-size)` |
| 5 | `year-grid.css:142` | `[data-year-grid-cell]` | `min-inline-size` | `calc(var(--_year-grid-cell-size) * 1.8)` |
| 6 | `year-grid.css:143` | `[data-year-grid-cell]` | `block-size` | `var(--_year-grid-cell-size)` |
| 7 | `year-grid.css:168` | `[data-year-grid-cell][data-selected]` | `border-color` | `var(--_year-grid-palette-solid)` |
| 8 | `year-grid.css:169` | `[data-year-grid-cell][data-selected]` | `background` | `var(--_year-grid-palette-solid)` |
| 9 | `year-grid.css:174` | `[data-year-grid-cell][data-selected]:hover` | `border-color` | `var(--_year-grid-palette-solid-hover)` |
| 10 | `year-grid.css:175` | `[data-year-grid-cell][data-selected]:hover` | `background` | `var(--_year-grid-palette-solid-hover)` |
| 11 | `year-grid.css:179` | `[data-year-grid-cell][data-focused]:not([data-selected])` | `border-color` | `var(--_year-grid-palette-border)` |
| 12 | `year-grid.css:180` | `[data-year-grid-cell][data-focused]:not([data-selected])` | `background` | `var(--_year-grid-palette-track)` |
| 13 | `year-grid.css:181` | `[data-year-grid-cell][data-focused]:not([data-selected])` | `color` | `var(--_year-grid-palette-text)` |
| 1 | `year-grid.css:147` | `[data-year-grid-cell][data-selected]` | `border-color` | `var(--_year-grid-palette-solid)` |
| 2 | `year-grid.css:148` | `[data-year-grid-cell][data-selected]` | `background` | `var(--_year-grid-palette-solid)` |
| 3 | `year-grid.css:153` | `[data-year-grid-cell][data-selected]:hover` | `border-color` | `var(--_year-grid-palette-solid-hover)` |
| 4 | `year-grid.css:154` | `[data-year-grid-cell][data-selected]:hover` | `background` | `var(--_year-grid-palette-solid-hover)` |
| 5 | `year-grid.css:158` | `[data-year-grid-cell][data-focused]:not([data-selected])` | `border-color` | `var(--_year-grid-palette-border)` |
| 6 | `year-grid.css:159` | `[data-year-grid-cell][data-focused]:not([data-selected])` | `background` | `var(--_year-grid-palette-track)` |
| 7 | `year-grid.css:160` | `[data-year-grid-cell][data-focused]:not([data-selected])` | `color` | `var(--_year-grid-palette-text)` |
### 1.3 Literales (5)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `year-grid.css:12` | `[data-year-grid]` | `inline-size` | `max-content` |
| 2 | `year-grid.css:67` | `[data-year-grid-header]` | `inline-size` | `100%` |
| 3 | `year-grid.css:124` | `[data-year-grid-grid]` | `inline-size` | `max-content` |
| 4 | `year-grid.css:132` | `[data-popover-content] [data-year-grid]` | `inline-size` | `100%` |
| 5 | `year-grid.css:135` | `[data-popover-content] [data-year-grid-grid]` | `inline-size` | `100%` |
| 2 | `year-grid.css:46` | `[data-year-grid-header]` | `inline-size` | `100%` |
| 3 | `year-grid.css:103` | `[data-year-grid-grid]` | `inline-size` | `max-content` |
| 4 | `year-grid.css:111` | `[data-popover-content] [data-year-grid]` | `inline-size` | `100%` |
| 5 | `year-grid.css:114` | `[data-popover-content] [data-year-grid-grid]` | `inline-size` | `100%` |
## 2. Sistema transversal (3) — informativo, fuera del ratio
## 2. Sistema transversal (49) — informativo, fuera del ratio
Un tema los alcanza **a nivel de sistema**, por diseño (recipe-contract §2).
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `year-grid.css:103` | `[data-year-grid-prev-button]:hover, [data-year-grid-next-button]:hover` | `background-image` | `linear-gradient(var(--state-hover), var(--state-hover))` |
| 2 | `year-grid.css:110` | `[data-year-grid-prev-button]:focus-visible, [data-year-grid-next-button]:focus-visible, [data-year-grid-cell]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
| 3 | `year-grid.css:163` | `[data-year-grid-cell]:hover` | `background-image` | `linear-gradient(var(--state-hover), var(--state-hover))` |
| 1 | `year-grid.css:11` | `[data-year-grid]` | `gap` | `var(--calendar-gap)` ⤴ prestado de `calendar` |
| 2 | `year-grid.css:15` | `[data-year-grid]` | `padding` | `var(--calendar-padding)` |
| 3 | `year-grid.css:16` | `[data-year-grid]` | `border` | `var(--calendar-border-width) solid var(--calendar-border)` ⤴ prestado de `calendar` |
| 4 | `year-grid.css:17` | `[data-year-grid]` | `border-radius` | `var(--calendar-radius)` ⤴ prestado de `calendar` |
| 5 | `year-grid.css:18` | `[data-year-grid]` | `background` | `var(--calendar-bg)` ⤴ prestado de `calendar` |
| 6 | `year-grid.css:19` | `[data-year-grid]` | `box-shadow` | `var(--calendar-shadow)` ⤴ prestado de `calendar` |
| 7 | `year-grid.css:20` | `[data-year-grid]` | `color` | `var(--calendar-fg)` ⤴ prestado de `calendar` |
| 8 | `year-grid.css:21` | `[data-year-grid]` | `font-family` | `var(--calendar-font-family)` ⤴ prestado de `calendar` |
| 9 | `year-grid.css:22` | `[data-year-grid]` | `font-size` | `var(--calendar-font-size)` |
| 10 | `year-grid.css:23` | `[data-year-grid]` | `line-height` | `var(--calendar-line-height)` ⤴ prestado de `calendar` |
| 11 | `year-grid.css:27` | `[data-year-grid][data-variant='outline']` | `background` | `var(--calendar-outline-bg)` ⤴ prestado de `calendar` |
| 12 | `year-grid.css:28` | `[data-year-grid][data-variant='outline']` | `box-shadow` | `var(--calendar-outline-shadow)` ⤴ prestado de `calendar` |
| 13 | `year-grid.css:45` | `[data-year-grid-header]` | `gap` | `var(--calendar-header-gap)` ⤴ prestado de `calendar` |
| 14 | `year-grid.css:52` | `[data-year-grid-heading]` | `color` | `var(--calendar-heading-fg)` ⤴ prestado de `calendar` |
| 15 | `year-grid.css:53` | `[data-year-grid-heading]` | `font-size` | `var(--calendar-heading-font-size)` ⤴ prestado de `calendar` |
| 16 | `year-grid.css:54` | `[data-year-grid-heading]` | `font-weight` | `var(--calendar-heading-font-weight)` ⤴ prestado de `calendar` |
| 17 | `year-grid.css:64` | `[data-year-grid-prev-button], [data-year-grid-next-button]` | `inline-size` | `var(--calendar-control-size)` |
| 18 | `year-grid.css:65` | `[data-year-grid-prev-button], [data-year-grid-next-button]` | `block-size` | `var(--calendar-control-size)` |
| 19 | `year-grid.css:67` | `[data-year-grid-prev-button], [data-year-grid-next-button]` | `border` | `var(--calendar-control-border-width) solid var(--calendar-control-border)` ⤴ prestado de `calendar` |
| 20 | `year-grid.css:68` | `[data-year-grid-prev-button], [data-year-grid-next-button]` | `border-radius` | `var(--calendar-control-radius)` ⤴ prestado de `calendar` |
| 21 | `year-grid.css:69` | `[data-year-grid-prev-button], [data-year-grid-next-button]` | `background` | `var(--calendar-control-bg)` ⤴ prestado de `calendar` |
| 22 | `year-grid.css:70` | `[data-year-grid-prev-button], [data-year-grid-next-button]` | `color` | `var(--calendar-control-fg)` ⤴ prestado de `calendar` |
| 23 | `year-grid.css:81` | `[data-year-grid-prev-button]:hover, [data-year-grid-next-button]:hover` | `border-color` | `var(--calendar-hover-control-border)` ⤴ prestado de `calendar` |
| 24 | `year-grid.css:82` | `[data-year-grid-prev-button]:hover, [data-year-grid-next-button]:hover` | `background-image` | `linear-gradient(var(--state-hover), var(--state-hover))` |
| 25 | `year-grid.css:83` | `[data-year-grid-prev-button]:hover, [data-year-grid-next-button]:hover` | `color` | `var(--calendar-hover-control-fg)` ⤴ prestado de `calendar` |
| 26 | `year-grid.css:89` | `[data-year-grid-prev-button]:focus-visible, [data-year-grid-next-button]:focus-visible, [data-year-grid-cell]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
| 27 | `year-grid.css:96` | `[data-year-grid-prev-button][data-disabled], [data-year-grid-next-button][data-disabled]` | `opacity` | `var(--calendar-disabled-opacity)` ⤴ prestado de `calendar` |
| 28 | `year-grid.css:102` | `[data-year-grid-grid]` | `gap` | `var(--calendar-grid-gap)` ⤴ prestado de `calendar` |
| 29 | `year-grid.css:121` | `[data-year-grid-cell]` | `min-inline-size` | `calc(var(--calendar-day-size) * 1.8)` |
| 30 | `year-grid.css:122` | `[data-year-grid-cell]` | `block-size` | `var(--calendar-day-size)` |
| 31 | `year-grid.css:123` | `[data-year-grid-cell]` | `padding-inline` | `var(--calendar-select-padding-inline, 8px)` ⤴ prestado de `calendar` |
| 32 | `year-grid.css:124` | `[data-year-grid-cell]` | `border` | `var(--calendar-day-border-width) solid var(--calendar-day-border)` ⤴ prestado de `calendar` |
| 33 | `year-grid.css:125` | `[data-year-grid-cell]` | `border-radius` | `var(--calendar-day-radius)` ⤴ prestado de `calendar` |
| 34 | `year-grid.css:126` | `[data-year-grid-cell]` | `background` | `var(--calendar-day-bg)` ⤴ prestado de `calendar` |
| 35 | `year-grid.css:127` | `[data-year-grid-cell]` | `color` | `var(--calendar-day-fg)` ⤴ prestado de `calendar` |
| 36 | `year-grid.css:129` | `[data-year-grid-cell]` | `font-weight` | `var(--calendar-day-font-weight)` ⤴ prestado de `calendar` |
| 37 | `year-grid.css:141` | `[data-year-grid-cell]:hover` | `border-color` | `var(--calendar-hover-day-border)` ⤴ prestado de `calendar` |
| 38 | `year-grid.css:142` | `[data-year-grid-cell]:hover` | `background-image` | `linear-gradient(var(--state-hover), var(--state-hover))` |
| 39 | `year-grid.css:143` | `[data-year-grid-cell]:hover` | `color` | `var(--calendar-hover-day-fg)` ⤴ prestado de `calendar` |
| 40 | `year-grid.css:149` | `[data-year-grid-cell][data-selected]` | `color` | `var(--calendar-day-selected-fg)` ⤴ prestado de `calendar` |
| 41 | `year-grid.css:164` | `[data-year-grid-cell][data-current]:not([data-selected])` | `border-color` | `var(--calendar-day-today-border)` ⤴ prestado de `calendar` |
| 42 | `year-grid.css:165` | `[data-year-grid-cell][data-current]:not([data-selected])` | `color` | `var(--calendar-day-today-fg)` ⤴ prestado de `calendar` |
| 43 | `year-grid.css:166` | `[data-year-grid-cell][data-current]:not([data-selected])` | `font-weight` | `var(--calendar-day-today-font-weight)` ⤴ prestado de `calendar` |
| 44 | `year-grid.css:171` | `[data-year-grid-cell][data-disabled]` | `opacity` | `var(--calendar-disabled-opacity)` ⤴ prestado de `calendar` |
| 45 | `year-grid.css:175` | `[data-year-grid][data-disabled]` | `opacity` | `var(--calendar-disabled-opacity)` ⤴ prestado de `calendar` |
| 46 | `year-grid.css:183` | `[data-year-grid][data-readonly] [data-year-grid-cell]:not([data-selected]):hover` | `background` | `var(--calendar-day-bg)` ⤴ prestado de `calendar` |
| 47 | `year-grid.css:184` | `[data-year-grid][data-readonly] [data-year-grid-cell]:not([data-selected]):hover` | `border-color` | `var(--calendar-day-border)` ⤴ prestado de `calendar` |
| 48 | `year-grid.css:185` | `[data-year-grid][data-readonly] [data-year-grid-cell]:not([data-selected]):hover` | `color` | `var(--calendar-day-fg)` ⤴ prestado de `calendar` |
| 49 | `year-grid.css:194` | `[data-year-grid-cell][data-event]` | `box-shadow` | `var(--calendar-event-shadow)` |
## 3. Privados de la receta — ¿de dónde sale su valor?
| privado | declaraciones | valor(es) | origen | ¿deriva de un público? |
| --- | ---: | --- | --- | :-: |
| `--_year-grid-padding` | 4 | `var(--calendar-padding-md)`, `var(--calendar-padding-xs)`, `var(--calendar-padding-sm)`, `var(--calendar-padding-lg)` | global | no |
| `--_year-grid-control-size` | 4 | `var(--calendar-control-size-md)`, `var(--calendar-control-size-xs)`, `var(--calendar-control-size-sm)`, `var(--calendar-control-size-lg)` | global | no |
| `--_year-grid-cell-size` | 4 | `var(--calendar-day-size-md)`, `var(--calendar-day-size-xs)`, `var(--calendar-day-size-sm)`, `var(--calendar-day-size-lg)` | global | no |
| `--_year-grid-font-size` | 4 | `var(--calendar-font-size-md)`, `var(--calendar-font-size-xs)`, `var(--calendar-font-size-sm)`, `var(--calendar-font-size-lg)` | global | no |
_La receta no declara privados propios en su CSS._
Consumidos y **no declarados en el CSS** (vienen de `base.ts` o de un estilo inline del wrapper): `--_year-grid-palette-border`, `--_year-grid-palette-solid`, `--_year-grid-palette-solid-hover`, `--_year-grid-palette-text`, `--_year-grid-palette-track`.
## 4. Propuesta de corrección
- **Consume la capa compartida `calendar-surface`.** Un eje que la capa posee se consume como `var(--_x, var(--x))`; el consumidor **no acuña** `--year-grid-{eje}` para él — sería un vocabulario paralelo (README de `eidos/components`, «Capas compartidas» regla 2).
- **Consume la capa compartida `picker-shell`.** Un eje que la capa posee se consume como `var(--_x, var(--x))`; el consumidor **no acuña** `--year-grid-{eje}` para él — sería un vocabulario paralelo (README de `eidos/components`, «Capas compartidas» regla 2).
- **Consume tokens públicos de `calendar`.** Un token prestado importa la semántica de su dueño: la corrección no es duplicarlo con prefijo propio, sino la decisión de familia que la auditoría de fase 1 dejó registrada (`theming-audit.md` §B, familia calendar).
- **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` (55)
### 4.1 Tokens a declarar en `lib/recipes/base.ts` (2)
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
@ -122,74 +116,13 @@ no distingue lo que debería — se marca `⚠`.
| token (`--year-grid-…`) | scope TSC | valor propuesto | usos |
| --- | --- | --- | ---: |
| `cell-fg` | `root` | ⚠ `var(--calendar-day-fg)` / `var(--calendar-day-today-fg)` | 2 |
| `cell-font-weight` | `root` | ⚠ `var(--calendar-day-font-weight)` / `var(--calendar-day-today-font-weight)` | 2 |
| `hover-cell-border` | `root` | ⚠ `var(--calendar-hover-day-border)` / `var(--calendar-day-border)` | 2 |
| `hover-cell-fg` | `root` | ⚠ `var(--calendar-hover-day-fg)` / `var(--calendar-day-fg)` | 2 |
| `gap` | `root` | `var(--calendar-gap)` | 1 |
| `width` | `root` | `max-content` | 1 |
| `radius` | `root` | `var(--calendar-radius)` | 1 |
| `bg` | `root` | `var(--calendar-bg)` | 1 |
| `shadow` | `root` | `var(--calendar-shadow)` | 1 |
| `fg` | `root` | `var(--calendar-fg)` | 1 |
| `font-family` | `root` | `var(--calendar-font-family)` | 1 |
| `font-size-md` | `size:md` | `var(--calendar-font-size-md)` | 1 |
| `font-size-xs` | `size:xs` | `var(--calendar-font-size-xs)` | 1 |
| `font-size-sm` | `size:sm` | `var(--calendar-font-size-sm)` | 1 |
| `font-size-lg` | `size:lg` | `var(--calendar-font-size-lg)` | 1 |
| `line-height` | `root` | `var(--calendar-line-height)` | 1 |
| `outline-bg` | `root` | `var(--calendar-outline-bg)` | 1 |
| `outline-shadow` | `root` | `var(--calendar-outline-shadow)` | 1 |
| `header-gap` | `root` | `var(--calendar-header-gap)` | 1 |
| `heading-fg` | `root` | `var(--calendar-heading-fg)` | 1 |
| `heading-font-size` | `root` | `var(--calendar-heading-font-size)` | 1 |
| `heading-font-weight` | `root` | `var(--calendar-heading-font-weight)` | 1 |
| `prev-button-width-md` | `size:md` | `var(--calendar-control-size-md)` | 1 |
| `prev-button-width-xs` | `size:xs` | `var(--calendar-control-size-xs)` | 1 |
| `prev-button-width-sm` | `size:sm` | `var(--calendar-control-size-sm)` | 1 |
| `prev-button-width-lg` | `size:lg` | `var(--calendar-control-size-lg)` | 1 |
| `prev-button-height-md` | `size:md` | `var(--calendar-control-size-md)` | 1 |
| `prev-button-height-xs` | `size:xs` | `var(--calendar-control-size-xs)` | 1 |
| `prev-button-height-sm` | `size:sm` | `var(--calendar-control-size-sm)` | 1 |
| `prev-button-height-lg` | `size:lg` | `var(--calendar-control-size-lg)` | 1 |
| `prev-button-radius` | `root` | `var(--calendar-control-radius)` | 1 |
| `prev-button-bg` | `root` | `var(--calendar-control-bg)` | 1 |
| `prev-button-fg` | `root` | `var(--calendar-control-fg)` | 1 |
| `hover-prev-button-border` | `root` | `var(--calendar-hover-control-border)` | 1 |
| `hover-prev-button-fg` | `root` | `var(--calendar-hover-control-fg)` | 1 |
| `disabled-prev-button-opacity` | `root` | `var(--calendar-disabled-opacity)` | 1 |
| `grid-gap` | `root` | `var(--calendar-grid-gap)` | 1 |
| `grid-width` | `root` | `max-content` | 1 |
| `cell-width-md` | `size:md` | `var(--calendar-day-size-md)` | 1 |
| `cell-width-xs` | `size:xs` | `var(--calendar-day-size-xs)` | 1 |
| `cell-width-sm` | `size:sm` | `var(--calendar-day-size-sm)` | 1 |
| `cell-width-lg` | `size:lg` | `var(--calendar-day-size-lg)` | 1 |
| `cell-height-md` | `size:md` | `var(--calendar-day-size-md)` | 1 |
| `cell-height-xs` | `size:xs` | `var(--calendar-day-size-xs)` | 1 |
| `cell-height-sm` | `size:sm` | `var(--calendar-day-size-sm)` | 1 |
| `cell-height-lg` | `size:lg` | `var(--calendar-day-size-lg)` | 1 |
| `cell-padding-inline` | `root` | `var(--calendar-select-padding-inline, 8px)` | 1 |
| `cell-radius` | `root` | `var(--calendar-day-radius)` | 1 |
| `cell-bg` | `root` | `var(--calendar-day-bg)` | 1 |
| `selected-cell-fg` | `root` | `var(--calendar-day-selected-fg)` | 1 |
| `cell-border` | `root` | `var(--calendar-day-today-border)` | 1 |
| `disabled-cell-opacity` | `root` | `var(--calendar-disabled-opacity)` | 1 |
| `disabled-opacity` | `root` | `var(--calendar-disabled-opacity)` | 1 |
| `hover-cell-bg` | `root` | `var(--calendar-day-bg)` | 1 |
| `cell-shadow` | `root` | `var(--calendar-event-shadow)` | 1 |
### 4.2 Sin nombre mecánico (14)
- **⚠ decisión: `padding` es shorthand o eje físico — hay que partirlo en ejes lógicos antes de nombrarlo (recipe-contract §1, R-4.4)** — 1: `padding`.
- **⚠ 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)** — 3: `border`.
- **⚠ 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** — 3: `inline-size`.
- **⚠ 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** — 7: `border-color`, `background`, `color`.
### 4.3 Avisos sobre los tokens propuestos (3)
### 4.2 Sin nombre mecánico (10)
- **el privado `--_year-grid-font-size` debe pasar a leer este público (o desaparecer)** — `--year-grid-font-size-md`, `--year-grid-font-size-xs`, `--year-grid-font-size-sm`, `--year-grid-font-size-lg`
- **el privado `--_year-grid-control-size` debe pasar a leer este público (o desaparecer)** — `--year-grid-prev-button-width-md`, `--year-grid-prev-button-width-xs`, `--year-grid-prev-button-width-sm`, `--year-grid-prev-button-width-lg`, `--year-grid-prev-button-height-md`, `--year-grid-prev-button-height-xs`, `--year-grid-prev-button-height-sm`, `--year-grid-prev-button-height-lg`
- **el privado `--_year-grid-cell-size` debe pasar a leer este público (o desaparecer)** — `--year-grid-cell-width-md`, `--year-grid-cell-width-xs`, `--year-grid-cell-width-sm`, `--year-grid-cell-width-lg`, `--year-grid-cell-height-md`, `--year-grid-cell-height-xs`, `--year-grid-cell-height-sm`, `--year-grid-cell-height-lg`
- **⚠ 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** — 3: `inline-size`.
- **⚠ 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** — 7: `border-color`, `background`, `color`.
### 4.4 Lo que hay que comprobar a mano (PLAN-theming §1.3 · §7.4)

@ -423,6 +423,19 @@ el mismo nodo entra en una relación que nunca se escribió.
puede ejecutarse ahí sin cambiar el arquetipo — decisión de MORFO. Es el
único knob de los seis de la cola que sigue sin resolver.
7. **En `grid-list` el hover pinta DOS VECES sobre el MISMO nodo** (medido
2026-08-21 al tokenizarlo). A diferencia de `table` / `tree-grid` —donde el
velo cae en la celda y el plano en la fila, nodos distintos— aquí el plano
de la receta (`background:` atajo, (0,3,0) → `background-color`) y el velo
del arquetipo ((0,5,0) → `background-image`) caen ambos en
`[data-grid-list-row]`: el atajo fija `background-image: none` pero pierde,
así que se ven los dos. Por eso el commit de `grid-list` **no acuñó**
`hover-row-bg`: sería un token que no puede ganar (mismo criterio que
`listbox.highlighted`). Y como en `table`, **fila Y celda llevan
`archetype: 'item'`** (`morfo/components/grid-list.ts:118` y `:141`), así
que bajo el puntero se apilan velo de celda + velo de fila + plano — tres
capas. El par de portadores del punto 2 aplica igual aquí.
### Qué habría que decidir
En una frase: **quién manda cuando el arquetipo y la receta pintan el mismo
@ -573,5 +586,32 @@ Lo que la revisión adversarial de F2-A añadió (2026-08-21):
(`sentinel-spinner-duration` / `-reduced-duration`). El mismo trato o una
anotación `functional:`, pero no las dos varas.
Lo que `grid-list` añadió (2026-08-21):
- **⚠ PENDIENTE DE FIRMA — la selección de fila de `grid-list` nunca ha
pintado.** La receta seleccionaba `[data-grid-list-row][data-selected]`, un
atributo que ni el morfo declara ni soma estampa: soma estampa
`data-state='selected'` + `aria-selected='true'`. La regla no casaba jamás,
así que su acento (`--_grid-list-palette-element`) no llegó nunca al píxel y
una fila seleccionada muestra sólo el velo NEUTRO del arquetipo —
visualmente indistinguible de una fila con el ratón encima (capturado). La
declaración muerta y su forward huérfano se retiraron en el commit de
tokenización con **0 diffs de computed**, que es la prueba de que estaban
muertos. **Repararlo mueve píxel** (re-apuntar a `[data-state='selected']`
haría aparecer un acento que el componente nunca tuvo), y choca de frente con
§12.5: el arquetipo `item` fija también `color` a (0,4,0) sobre el estado
seleccionado. Es la misma decisión del velo, no una corrección al paso.
El acento del SelectionCheckbox (`_palette-solid`) sí vive y se conservó.
- **`SHARED_LAYERS` declara a `grid-list` consumidor de `menu-indicator`, y no
lo es.** `theming-census.ts:432` lo lista, pero `lib/menu-indicator.css` no
tiene un solo selector que apunte a grid-list (sus 134 líneas targetean
`[data-context-menu-*]` / `[data-dropdown-menu-*]`), la receta no importa la
capa y soma no renderiza indicator: el checkbox **espeja** ese visual
pintándolo por su cuenta. La entrada hace que el informe generado escriba
«Consume la capa compartida `menu-indicator`» en la §4 de la ficha, que es
falso y dirige mal a quien la lea. Revisar las seis entradas de esa lista
contra los selectores reales de cada capa (`listbox`, `menubar` y
`navigation-menu` están en la misma entrada y tampoco aparecen en el fichero).
**Deps**: ninguna. Son mejoras del instrumental del eje, ejecutables cuando
estorben.

@ -115,5 +115,9 @@ export const SENTINEL_EXCEPTIONS: Record<string, Record<string, string>> = {
'the demo mounts heading levels 3–4 only; forced aria-level=6 → reaches (12 → 1234)',
'sentinel-spinner-reduced-duration':
'behind prefers-reduced-motion; emulated reduce context → reaches (4s → 11.5s)'
},
'grid-list': {
'invalid-border':
'consumed only under [data-invalid]; the demo boots with its invalid control off, so the rule never matches at measurement. Toggled by hand 2026-08-21 → reaches (oklch(0.9555 0.0207 13.86) → rgb(1,2,3))'
}
};

@ -72,6 +72,61 @@ Referencias: [React Aria GridList](https://react-spectrum.adobe.com/react-aria/G
compuesto): su estado ES la selección de la fila (derivado, no propio) y
su `aria-checked` la espeja — componer el Checkbox real duplicaría estado.
## Talla y tema
**Guard R-5.4** (`npm run theming:sentinel -- grid-list <url>`): 37/38 tokens
mueven un computed en vivo; el que calla está adjudicado con su porqué medido en
`scripts/theming-sentinel-exceptions.ts`. Corrido 2026-08-21.
Contrato en `lib/recipes/base.ts > grid-list` (38 claves). Alcance del censo:
**85 %** (era 0 % antes del 2026-08-21).
**El eje `size`** (xs..xl) se estampa en el PROVIDER — el wrapper de eidos no
tiene una parte `root` separada —, así que los nombres resueltos NO declaran
`parts`: la cascada cuelga del host. Molde `command`, no `table`/`tree-grid`.
El CSS ya no lleva bloques `[data-size]`: los emite el TSC.
Qué casa con el bundle y qué no, medido: la altura de fila **sí** (1:1 —
`--size-{k}-control-height` es alias de `--control-height-{k}`); el
`row-padding-inline` **no**, lleva ritmo propio; y la tipografía casa
**desplazada**, un paso por debajo de md en adelante
(`font-size-md` = `var(--size-sm-font-size)`), igual que `table` y `tree-grid`
— una lista de filas se lee en bloque, no de un vistazo.
### Dos cosas que NO tienen token, y por qué
1. **El hover de fila no se acuña.** La fila lleva `archetype: 'item'`, así que
el velo del sistema (`archetypes.css`) la pinta a especificidad PLENA — es
deliberado en esa hoja, no un empate. El plano de esta receta pierde, y
medido el 2026-08-21 el hover **pinta dos veces** sobre el mismo nodo: fondo
plano (`background-color`) + velo (`background-image`). Un
`--grid-list-hover-row-bg` sería un token que no puede ganar; se deja el
global verbatim y la incidencia vive en `next-features.md` §12. Mismo
criterio que `listbox.highlighted`.
A diferencia de `table` / `tree-grid`, donde el velo cae en la CELDA y el
plano en la FILA (nodos distintos), aquí los dos caen en el mismo nodo — y
**fila y celda llevan ambas `archetype: 'item'`**, así que bajo el puntero
se apilan velo de celda + velo de fila. Registrado en §12 junto al par de
portadores de `table`.
2. **La selección de fila estaba MUERTA y su regla se retiró.** La receta
seleccionaba `[data-grid-list-row][data-selected]`, un atributo que ni el
morfo declara ni soma estampa (estampa `data-state='selected'` +
`aria-selected='true'`), así que el acento nunca pintó: una fila
seleccionada muestra el velo neutro del arquetipo, indistinguible del hover.
Retirar la regla dio **0 diffs de computed**, que es la prueba de que estaba
muerta; con ella se fue el forward `--_grid-list-palette-element`, que no
alimentaba nada. Repararlo mueve píxel y espera firma (`next-features.md`
§13). El acento del checkbox (`_palette-solid`) **sí** vive y se conserva.
**El SelectionCheckbox no consume `lib/menu-indicator.css`**, lo ESPEJA. Ningún
selector de esa capa apunta a grid-list, la receta no la importa y soma no
renderiza indicator: la caja se pinta aquí. El censo lo lista como consumidor en
`SHARED_LAYERS` — entrada desviada, anotada en §13.
`R-5.1 exception: inline-size: 100%` — `[data-block]` llena su contenedor.
## Gaps
| Gap | Disposición | Detalle |

@ -10,10 +10,14 @@
* Variant `'surface' | 'outline' | 'ghost'` (ControlVariant).
* Color cascades the accent for selected rows + focus ring.
*
* Cross-component reuse: SelectionCheckbox uses the menu-indicator
* recipe family (see `lib/menu-indicator.css`) — the indicator inside
* the soma part will pick up that visual when the row is selected.
* Row hover/highlight tokens mirror Combobox.Item + Listbox.Item.
* Cross-component reuse: SelectionCheckbox MIRRORS the menu-indicator
* visual (empty outlined square → filled accent + glyph). It does not
* consume `lib/menu-indicator.css` — no selector in that partial targets
* grid-list, and this recipe paints the box itself.
*
* Row hover is the SYSTEM state layer (`archetype: 'item'`), not this
* recipe's: the plane below is outweighed by the archetype veil and both
* paint. Measured 2026-08-21 — see next-features §12.
*
* Disambiguation: use GridList for rows with interactive children
* (buttons / links / checkbox). Use Listbox for atomic text options
@ -21,24 +25,19 @@
*/
[data-grid-list] {
--_grid-list-row-height: var(--control-height-md);
--_grid-list-row-padding-inline: var(--space-3);
--_grid-list-font-size: var(--font-size-sm);
--_grid-list-max-height: min(60vh, 22rem);
display: flex;
flex-direction: column;
gap: 1px;
padding: var(--space-1);
border: var(--border-width) solid var(--color-border-default);
border-radius: var(--radius-md);
background: var(--color-surface-default);
color: var(--color-content-primary);
font-family: var(--style-label-font-family);
font-size: var(--_grid-list-font-size);
line-height: var(--leading-ui);
gap: var(--grid-list-gap);
padding: var(--grid-list-padding);
border: var(--grid-list-border-width) solid var(--grid-list-border);
border-radius: var(--grid-list-radius);
background: var(--grid-list-bg);
color: var(--grid-list-fg);
font-family: var(--grid-list-font-family);
font-size: var(--grid-list-font-size);
line-height: var(--grid-list-line-height);
outline: none;
max-block-size: var(--_grid-list-max-height);
max-block-size: var(--grid-list-max-block);
overflow-y: auto;
overscroll-behavior: contain;
}
@ -57,30 +56,11 @@
border-color: transparent;
}
/* ── Sizes ─ */
[data-grid-list][data-size='xs'] {
--_grid-list-row-height: var(--control-height-xs);
--_grid-list-row-padding-inline: var(--space-2);
--_grid-list-font-size: var(--font-size-xs);
}
[data-grid-list][data-size='sm'] {
--_grid-list-row-height: var(--control-height-sm);
--_grid-list-row-padding-inline: var(--space-2);
--_grid-list-font-size: var(--font-size-sm);
}
[data-grid-list][data-size='lg'] {
--_grid-list-row-height: var(--control-height-lg);
--_grid-list-row-padding-inline: var(--space-4);
--_grid-list-font-size: var(--font-size-md);
}
[data-grid-list][data-size='xl'] {
--_grid-list-row-height: var(--control-height-xl);
--_grid-list-row-padding-inline: var(--space-5);
--_grid-list-font-size: var(--font-size-lg);
}
/* ── Sizes ─
* The per-size cascade is EMITTED by the TSC from the recipe contract
* (`row-height` / `row-padding-inline` / `font-size` resolve per
* `data-size` on the host). Keeping the old `[data-size]` blocks here
* would shadow the new tokens with the old values. */
[data-grid-list][data-disabled] {
opacity: var(--opacity-disabled);
@ -88,7 +68,7 @@
}
[data-grid-list][data-invalid] {
border-color: var(--color-threat-element);
border-color: var(--grid-list-invalid-border);
}
[data-grid-list]:focus-visible,
@ -101,12 +81,12 @@
[data-grid-list-row] {
display: flex;
align-items: center;
gap: var(--space-2);
min-block-size: var(--_grid-list-row-height);
padding-block: var(--space-1);
padding-inline: var(--_grid-list-row-padding-inline);
border-radius: var(--radius-sm);
color: var(--color-content-primary);
gap: var(--grid-list-row-gap);
min-block-size: var(--grid-list-row-height);
padding-block: var(--grid-list-row-padding-block);
padding-inline: var(--grid-list-row-padding-inline);
border-radius: var(--grid-list-row-radius);
color: var(--grid-list-row-fg);
cursor: pointer;
user-select: none;
transition:
@ -114,17 +94,27 @@
color var(--duration-fast) var(--ease-default);
}
/* NO token here on purpose. The row is `archetype: 'item'`, so the system
* state layer paints hover / highlighted / selected at full specificity
* (archetypes.css) and this plane loses — measured 2026-08-21, the hover
* paints TWICE on the same node. A `--grid-list-hover-row-bg` would be a
* token that cannot win: it is registered in next-features §12 (the state
* layer cascade contract), not coined here. Same call as listbox. */
[data-grid-list-row]:hover:not([data-disabled]),
[data-grid-list-row][data-highlighted]:not([data-disabled]) {
background: var(--color-surface-overlay);
}
[data-grid-list-row][data-selected]:not([data-disabled]) {
background: var(--_grid-list-palette-element);
}
/* The accent rule for a selected row was RETIRED 2026-08-21. It selected
* `[data-grid-list-row][data-selected]`, an attribute neither the morfo
* declares nor soma stamps (soma stamps `data-state='selected'` +
* `aria-selected='true'`), so it never matched and its accent never
* painted — the archetype's neutral veil is what a selected row shows.
* Re-pointing the selector moves pixels: it awaits signature and is
* registered in next-features §13. */
[data-grid-list-row][data-disabled] {
color: var(--color-content-muted);
color: var(--grid-list-disabled-row-fg);
cursor: default;
opacity: var(--opacity-disabled);
}
@ -157,18 +147,18 @@
/* ── SelectionCheckbox ─
*
* Soma renders a `[data-grid-list-selection-checkbox]` element with
* `data-state="checked"` / "unchecked". Reuse the menu-indicator visual
* pattern: empty outlined square → filled accent + white ✓.
* `data-state="checked"` / "unchecked". MIRRORS the menu-indicator visual
* pattern — empty outlined square → filled accent + glyph — painted here,
* not consumed from that partial (it targets menu items only).
*/
[data-grid-list-selection-checkbox] {
--_grid-list-checkbox-size: 0.875rem;
position: relative;
display: inline-flex;
flex: 0 0 auto;
inline-size: var(--_grid-list-checkbox-size);
block-size: var(--_grid-list-checkbox-size);
border: var(--border-width) solid var(--color-border-default);
border-radius: var(--radius-sm);
inline-size: var(--grid-list-selection-checkbox-size);
block-size: var(--grid-list-selection-checkbox-size);
border: var(--grid-list-border-width) solid var(--grid-list-selection-checkbox-border);
border-radius: var(--grid-list-selection-checkbox-radius);
background: transparent;
cursor: pointer;
outline: none;
@ -186,7 +176,7 @@
content: '';
position: absolute;
inset: 0;
background-color: var(--color-content-on-solid);
background-color: var(--grid-list-checked-selection-checkbox-fg);
mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><path d='M5 12.5L10 17.5L19 7'/></svg>");
-webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><path d='M5 12.5L10 17.5L19 7'/></svg>");
mask-repeat: no-repeat;

@ -1785,6 +1785,41 @@
--feed-sentinel-spinner-gap: var(--space-2);
--feed-sentinel-spinner-duration: 0.7s;
--feed-sentinel-spinner-reduced-duration: 4s;
--grid-list-row-height-xs: var(--size-xs-control-height);
--grid-list-row-height-sm: var(--size-sm-control-height);
--grid-list-row-height-md: var(--size-md-control-height);
--grid-list-row-height-lg: var(--size-lg-control-height);
--grid-list-row-height-xl: var(--size-xl-control-height);
--grid-list-row-padding-inline-xs: var(--space-2);
--grid-list-row-padding-inline-sm: var(--space-2);
--grid-list-row-padding-inline-md: var(--space-3);
--grid-list-row-padding-inline-lg: var(--space-4);
--grid-list-row-padding-inline-xl: var(--space-5);
--grid-list-font-size-xs: var(--size-xs-font-size);
--grid-list-font-size-sm: var(--size-sm-font-size);
--grid-list-font-size-md: var(--size-sm-font-size);
--grid-list-font-size-lg: var(--size-md-font-size);
--grid-list-font-size-xl: var(--size-lg-font-size);
--grid-list-gap: 1px;
--grid-list-padding: var(--space-1);
--grid-list-border-width: var(--border-width);
--grid-list-border: var(--color-border-default);
--grid-list-radius: var(--radius-md);
--grid-list-bg: var(--color-surface-default);
--grid-list-fg: var(--color-content-primary);
--grid-list-font-family: var(--style-label-font-family);
--grid-list-line-height: var(--leading-ui);
--grid-list-max-block: min(60vh, 22rem);
--grid-list-invalid-border: var(--color-threat-element);
--grid-list-row-gap: var(--space-2);
--grid-list-row-padding-block: var(--space-1);
--grid-list-row-radius: var(--radius-sm);
--grid-list-row-fg: var(--color-content-primary);
--grid-list-disabled-row-fg: var(--color-content-muted);
--grid-list-selection-checkbox-size: 0.875rem;
--grid-list-selection-checkbox-border: var(--color-border-default);
--grid-list-selection-checkbox-radius: var(--radius-sm);
--grid-list-checked-selection-checkbox-fg: var(--color-content-on-solid);
--date-range-picker-accent-border: var(--color-primary-border);
--date-range-picker-accent-track: var(--color-primary-track);
--date-range-picker-accent-text: var(--color-primary-text);
@ -5563,12 +5598,37 @@
}
[data-grid-list] {
--grid-list-row-height: var(--grid-list-row-height-md);
--grid-list-row-padding-inline: var(--grid-list-row-padding-inline-md);
--grid-list-font-size: var(--grid-list-font-size-md);
--_grid-list-palette-solid: var(--color-primary-solid);
--_grid-list-palette-element: var(--color-primary-element);
}
[data-grid-list][data-size='xs'] {
--grid-list-row-height: var(--grid-list-row-height-xs);
--grid-list-row-padding-inline: var(--grid-list-row-padding-inline-xs);
--grid-list-font-size: var(--grid-list-font-size-xs);
}
[data-grid-list][data-size='sm'] {
--grid-list-row-height: var(--grid-list-row-height-sm);
--grid-list-row-padding-inline: var(--grid-list-row-padding-inline-sm);
--grid-list-font-size: var(--grid-list-font-size-sm);
}
[data-grid-list][data-size='lg'] {
--grid-list-row-height: var(--grid-list-row-height-lg);
--grid-list-row-padding-inline: var(--grid-list-row-padding-inline-lg);
--grid-list-font-size: var(--grid-list-font-size-lg);
}
[data-grid-list][data-size='xl'] {
--grid-list-row-height: var(--grid-list-row-height-xl);
--grid-list-row-padding-inline: var(--grid-list-row-padding-inline-xl);
--grid-list-font-size: var(--grid-list-font-size-xl);
}
[data-grid-list][data-color], [data-grid-list][data-color-custom] {
--_grid-list-palette-element: var(--palette-element, var(--color-primary-element));
--_grid-list-palette-solid: var(--palette-solid, var(--color-primary-solid));
}

@ -2326,8 +2326,105 @@ export const THEME_BASE_RECIPE_TOKENS = defineRecipes({
}
},
'grid-list': {
'_palette-solid': { value: 'var(--color-primary-solid)', scope: 'host' },
'_palette-element': { value: 'var(--color-primary-element)', scope: 'host' }
// ── Per-size coordinates (xs..xl) ──
// Row height is 1:1 with the bundle (`--size-{k}-control-height` IS
// `--control-height-{k}`, verified in generated/base.css). Padding keeps
// the list's OWN rhythm — spacing sits outside the bundle guard, which
// governs control-height / font-size / icon-size only.
'row-height-xs': 'var(--size-xs-control-height)',
'row-height-sm': 'var(--size-sm-control-height)',
'row-height-md': 'var(--size-md-control-height)',
'row-height-lg': 'var(--size-lg-control-height)',
'row-height-xl': 'var(--size-xl-control-height)',
'row-padding-inline-xs': 'var(--space-2)',
'row-padding-inline-sm': 'var(--space-2)',
'row-padding-inline-md': 'var(--space-3)',
'row-padding-inline-lg': 'var(--space-4)',
'row-padding-inline-xl': 'var(--space-5)',
// Type: like `table` and `tree-grid`, the list reads ONE STEP BELOW the
// 1:1 from md up — a list of rows is read in bulk, not glanced at. The
// coordinate names that step instead of hiding it in a raw primitive.
'font-size-xs': 'var(--size-xs-font-size)',
'font-size-sm': 'var(--size-sm-font-size)',
'font-size-md': 'var(--size-sm-font-size)',
'font-size-lg': 'var(--size-md-font-size)',
'font-size-xl': 'var(--size-lg-font-size)',
// The RESOLVED names — the only ones the recipe reads. `data-size` is
// stamped on the PROVIDER (the eidos wrapper's only root), so the
// per-size cascade hangs from the host and needs no `parts` (precedent:
// command, not table/tree-grid — those stamp on a `root` part).
'row-height': {
declarations: [
{ value: 'var(--grid-list-row-height-md)', scope: 'host' },
{ value: 'var(--grid-list-row-height-xs)', scope: 'size:xs' },
{ value: 'var(--grid-list-row-height-sm)', scope: 'size:sm' },
{ value: 'var(--grid-list-row-height-lg)', scope: 'size:lg' },
{ value: 'var(--grid-list-row-height-xl)', scope: 'size:xl' }
]
},
'row-padding-inline': {
declarations: [
{ value: 'var(--grid-list-row-padding-inline-md)', scope: 'host' },
{ value: 'var(--grid-list-row-padding-inline-xs)', scope: 'size:xs' },
{ value: 'var(--grid-list-row-padding-inline-sm)', scope: 'size:sm' },
{ value: 'var(--grid-list-row-padding-inline-lg)', scope: 'size:lg' },
{ value: 'var(--grid-list-row-padding-inline-xl)', scope: 'size:xl' }
]
},
'font-size': {
declarations: [
{ value: 'var(--grid-list-font-size-md)', scope: 'host' },
{ value: 'var(--grid-list-font-size-xs)', scope: 'size:xs' },
{ value: 'var(--grid-list-font-size-sm)', scope: 'size:sm' },
{ value: 'var(--grid-list-font-size-lg)', scope: 'size:lg' },
{ value: 'var(--grid-list-font-size-xl)', scope: 'size:xl' }
]
},
// ── Chassis ── no `root-` prefix: the wrapper IS the component.
gap: '1px',
padding: 'var(--space-1)',
'border-width': 'var(--border-width)',
border: 'var(--color-border-default)',
radius: 'var(--radius-md)',
bg: 'var(--color-surface-default)',
fg: 'var(--color-content-primary)',
'font-family': 'var(--style-label-font-family)',
'line-height': 'var(--leading-ui)',
'max-block': 'min(60vh, 22rem)',
'invalid-border': 'var(--color-threat-element)',
// ── Row ──
// NO `hover-row-bg`: the row carries `archetype: 'item'`, so the system
// state layer paints it (full specificity, by design) and this recipe's
// own plane loses. Measured 2026-08-21 — the hover paints TWICE on the
// same node. Coining a token here would bless a condemned duplicate
// (same call as listbox.highlighted). Registered in next-features §12.
'row-gap': 'var(--space-2)',
'row-padding-block': 'var(--space-1)',
'row-radius': 'var(--radius-sm)',
'row-fg': 'var(--color-content-primary)',
'disabled-row-fg': 'var(--color-content-muted)',
// ── SelectionCheckbox ──
// It MIRRORS the menu-indicator visual (empty outlined square → filled
// accent + glyph); it does not consume that layer — no selector of
// `lib/menu-indicator.css` targets grid-list. Its border width is the
// chassis' `border-width`: one border weight per component.
'selection-checkbox-size': '0.875rem',
'selection-checkbox-border': 'var(--color-border-default)',
'selection-checkbox-radius': 'var(--radius-sm)',
'checked-selection-checkbox-fg': 'var(--color-content-on-solid)',
// The accent the CHECKED checkbox fills with. The row's own
// `_palette-element` forward was retired 2026-08-21: its only consumer
// selected `[data-grid-list-row][data-selected]`, an attribute neither
// the morfo declares nor soma stamps (it stamps `data-state='selected'`
// + `aria-selected`), so the rule never matched and the forward fed
// nothing. A selected row reads through the archetype's neutral veil;
// repairing that moves pixels and awaits signature (next-features §13).
'_palette-solid': { value: 'var(--color-primary-solid)', scope: 'host' }
},
// ─────────────────────────────────────────────────────────────────────
// DateRangePicker — only the DECADE / MONTH view is its own invention; the

@ -9,10 +9,11 @@
import { compileMorfo } from '$uix/morfo';
import { gridListMorfo } from '@/uix/morfo/components/grid-list';
import { getActiveUix } from '$active-uix';
import TokensPanel from '../../lib/TokensPanel.svelte';
const uix = getActiveUix();
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'a11y';
type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'tokens' | 'a11y';
let tab = $state<Tab>('live');
// ── Live state ────────────────────────────────────────────────────────
@ -215,6 +216,9 @@
<span data-uix-tab-count>{events.length}</span>
</button>
<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>38</span>
</button>
<button data-uix-tab data-active={tab === 'a11y'} onclick={() => (tab = 'a11y')}>A11y</button>
</div>
@ -462,9 +466,8 @@
<tr><td class="name">[data-grid-list]</td><td><span data-uix-tag data-kind="eidos">eidos</span></td><td>Container — flex column, border, padding, scroll cap.</td></tr>
<tr><td class="name">[data-grid-list][data-size]</td><td><span data-uix-tag data-kind="eidos">eidos</span></td><td>Row height + font-size scale.</td></tr>
<tr><td class="name">[data-grid-list][data-variant]</td><td><span data-uix-tag data-kind="eidos">eidos</span></td><td>Container chrome (surface / outline / ghost).</td></tr>
<tr><td class="name">[data-grid-list][data-color]</td><td><span data-uix-tag data-kind="eidos">eidos</span></td><td>Accent palette for selected rows + focus ring.</td></tr>
<tr><td class="name">[data-grid-list-row]</td><td><span data-uix-tag data-kind="soma">morfo</span></td><td>Row — flex layout, padding, hover/highlight states.</td></tr>
<tr><td class="name">[data-grid-list-row][data-selected]</td><td><span data-uix-tag data-kind="soma">morfo</span></td><td>Selected-row accent-soft fill.</td></tr>
<tr><td class="name">[data-grid-list][data-color]</td><td><span data-uix-tag data-kind="eidos">eidos</span></td><td>Accent palette for the checked selection checkbox + focus ring.</td></tr>
<tr><td class="name">[data-grid-list-row]</td><td><span data-uix-tag data-kind="soma">morfo</span></td><td>Row — flex layout, padding. Hover / highlighted / selected are painted by the SYSTEM state layer (archetype <code>item</code>), not here.</td></tr>
<tr><td class="name">[data-grid-list-cell]</td><td><span data-uix-tag data-kind="soma">morfo</span></td><td>Cell slot — first/last child get special flex treatment.</td></tr>
<tr><td class="name">[data-grid-list-selection-checkbox][data-state]</td><td><span data-uix-tag data-kind="eidos">eidos</span></td><td>0.875rem outlined box → filled accent + white ✓.</td></tr>
</tbody>
@ -473,6 +476,10 @@
</section>
{/if}
{#if tab === 'tokens'}
<TokensPanel component="grid-list" 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.