uix(dropdown-menu): temable — 57 % → 96 %, y la tipografía del gemelo se RETIRA

9 claves nuevas (20 → 28) y 18 respaldos muertos retirados. Y una corrección a la
baja, a propósito: `context-menu` 90 % → 87 %.

Es el GEMELO de `context-menu`: mismos nombres, misma doctrina, mismas dos
limpiezas de paso —el `font-family` era respaldo de SÍ MISMO y el peso del
encabezado llevaba un `500` muerto detrás del token que ya vale 500—. Cosidos la
tinta del panel y de la fila, su interlínea, el encabezado entero (tinta, peso y
tracking) y el galón del submenú, que ahora tiene sus dos knobs: trazo y caja.

EL FILO DEL SEPARADOR DEJA DE SER UN `1px` SUELTO y pasa a ser el trazo fino del
sistema (`--border-width`, idéntico hoy): un tema que engorde el hairline lo
engorda también aquí.

Los 18 respaldos: cada `var(--dropdown-menu-x, …)` llevaba detrás un valor que NO
puede ocurrir —las 18 claves están en el contrato— y sólo servía para envejecer
contra él. Ninguno movía un píxel; dos ya discrepaban del contrato en la FUENTE.

⚠ `content-font-family` NO SE ACUÑA, Y LA DEL GEMELO SE RETIRA. El panel lleva
`data-depth='overlay'` y el PLANO declara la familia con la misma especificidad
y más tarde en la cascada: gana él. Quinta prueba de §12.9. `context-menu` la
tenía acuñada desde esta misma sesión CON una adjudicación escrita, y eso era
media doctrina: un público que no alcanza es un token que miente (R-5.4). Las
dos recetas vuelven a su fuente literal con la razón encima.

Un tercer caso —`combobox`, misma clave, mismo plano— queda medido y registrado
en §13; no se toca aquí porque no es el componente en curso, y cuando §12.9 se
firme la decisión será la misma para las cuatro.

Gates: sonda 0 diffs en 928 valores × 8 estados (22 nodos con el panel abierto) ·
centinela 27/28 con la deshabilitada adjudicada · censo 96 % · `component:audit`
PASS · suite eidos con el rojo conocido ajeno · `rtl:check` 0 · `docs:check` 0 ·
captura del panel abierto.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
alpha-0.1-background
dev 2 months ago
parent 257a2e0804
commit e276bb0cce

@ -10,8 +10,8 @@
- **Medido**: 2026-08-23 · **162 recetas** con CSS + **8 componentes sin receta** = 170 fichas, el árbol entero de `eidos/components/`
- **La pregunta**: ¿cuánto de la apariencia de cada componente puede cambiar un tema **sin tocar el sistema ni la receta**?
- **Alcance global**: **64%** — 2942 de 4607 knobs pasan por un token público del componente
- **Reparto**: público 2942 · privado 322 · global 873 · literal 470 · sistema transversal 509 · excepción firmada 75 _(los dos últimos, fuera del ratio)_
- **Alcance global**: **64%** — 2952 de 4607 knobs pasan por un token público del componente
- **Reparto**: público 2952 · privado 322 · global 866 · literal 467 · sistema transversal 509 · excepción firmada 75 _(los dos últimos, fuera del ratio)_
- **Sin token público propio**: 24 · **alcance < 20 %**: 14 · **alcance 100 %**: 18 · **con eje `size`**: 58
## Cómo se lee
@ -147,7 +147,6 @@ La columna «contrato» cuenta las claves **públicas** del bloque del component
| [sidebar](./sidebar.md) | 55% | 45 | 23 | 0 | 14 | 5 | 3 | 50 | – |
| [color-field](./color-field.md) | 56% | 16 | 9 | 0 | 2 | 5 | 0 | 22 | y |
| [background](./background.md) | 57% | 26 | 13 | 1 | 6 | 3 | 3 | 31 | – |
| [dropdown-menu](./dropdown-menu.md) | 57% | 30 | 16 | 0 | 9 | 3 | 2 | 19 | – |
| [s-text-virtual-list](./s-text-virtual-list.md) | 58% | 12 | 7 | 0 | 3 | 2 | 0 | 9 | – |
| [badge](./badge.md) | 59% | 22 | 13 | 6 | 1 | 2 | 0 | 65 | y |
| [checkbox](./checkbox.md) | 59% | 33 | 19 | 8 | 2 | 3 | 1 | 44 | y |
@ -208,6 +207,7 @@ La columna «contrato» cuenta las claves **públicas** del bloque del component
| [combobox](./combobox.md) | 85% | 97 | 76 | 4 | 3 | 6 | 8 | 106 | – |
| [kbd](./kbd.md) | 86% | 14 | 12 | 0 | 1 | 1 | 0 | 13 | – |
| [table](./table.md) | 86% | 46 | 37 | 3 | 0 | 3 | 3 | 45 | – |
| [context-menu](./context-menu.md) | 87% | 33 | 26 | 0 | 1 | 3 | 3 | 28 | – |
| [fab](./fab.md) | 88% | 16 | 14 | 0 | 2 | 0 | 0 | 10 | – |
| [announce](./announce.md) | 88% | 8 | 7 | 0 | 1 | 0 | 0 | 9 | – |
| [media-player](./media-player.md) | 88% | 49 | 43 | 0 | 0 | 6 | 0 | 61 | – |
@ -217,7 +217,6 @@ La columna «contrato» cuenta las claves **públicas** del bloque del component
| [drag-drop](./drag-drop.md) | 89% | 16 | 8 | 1 | 0 | 0 | 7 | 9 | – |
| [chat-typing](./chat-typing.md) | 89% | 9 | 8 | 0 | 1 | 0 | 0 | 7 | – |
| [chart](./chart.md) | 89% | 95 | 81 | 0 | 0 | 10 | 4 | 45 | – |
| [context-menu](./context-menu.md) | 90% | 33 | 27 | 0 | 0 | 3 | 3 | 29 | – |
| [card-group](./card-group.md) | 90% | 22 | 18 | 0 | 0 | 2 | 2 | 20 | – |
| [collapsible](./collapsible.md) | 90% | 12 | 9 | 0 | 1 | 0 | 2 | 11 | – |
| [result](./result.md) | 90% | 21 | 19 | 0 | 0 | 2 | 0 | 27 | – |
@ -232,6 +231,7 @@ La columna «contrato» cuenta las claves **públicas** del bloque del component
| [picker-shell](./picker-shell.md) | 95% | 21 | 20 | 0 | 1 | 0 | 0 | 31 | y |
| [nav-tree](./nav-tree.md) | 96% | 24 | 22 | 0 | 0 | 1 | 1 | 18 | – |
| [tabs](./tabs.md) | 96% | 76 | 72 | 0 | 2 | 1 | 1 | 79 | y |
| [dropdown-menu](./dropdown-menu.md) | 96% | 30 | 27 | 0 | 1 | 0 | 2 | 28 | – |
| [slider](./slider.md) | 97% | 33 | 31 | 0 | 0 | 1 | 1 | 39 | y |
| [toast](./toast.md) | 98% | 42 | 41 | 0 | 0 | 1 | 0 | 85 | – |
| [command](./command.md) | 98% | 52 | 49 | 0 | 0 | 1 | 2 | 57 | – |

@ -5,16 +5,18 @@
> Vista de conjunto: [README](./README.md) · método y protocolo:
> [`PLAN-theming.md`](../../process/PLAN-theming.md) §1, §2, §7.
- **Medido**: 2026-08-23 · **Alcance**: **90%** — 27 de 30 knobs por token público
- **Knobs de apariencia**: 33 — público 27 · privado 0 · global 0 · literal 3 · sistema 3 · excepción 0 _(los dos últimos, fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 29 pública(s) — `content-font-family`, `content-fg`, `item-fg`, `item-line-height`, `chevron-stroke`, `heading-fg`, `heading-font-weight`, `heading-letter-spacing`, `open-trigger-outline-width`, `open-trigger-outline`, `open-trigger-radius`, `content-min-width`, `content-max-height`, `content-padding`, `content-radius`, `content-font-size`, `item-gap`, `item-height`, `item-padding-inline`, `item-padding-block`, `item-radius`, `hover-item-bg`, `hover-item-fg`, `item-disabled-opacity`, `heading-padding-inline`, `heading-padding-block`, `heading-font-size`, `separator-bg`, `separator-margin-block`
- **Medido**: 2026-08-23 · **Alcance**: **87%** — 26 de 30 knobs por token público
- **Knobs de apariencia**: 33 — público 26 · privado 0 · global 1 · literal 3 · sistema 3 · excepción 0 _(los dos últimos, fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 28 pública(s) — `content-fg`, `item-fg`, `item-line-height`, `chevron-stroke`, `heading-fg`, `heading-font-weight`, `heading-letter-spacing`, `open-trigger-outline-width`, `open-trigger-outline`, `open-trigger-radius`, `content-min-width`, `content-max-height`, `content-padding`, `content-radius`, `content-font-size`, `item-gap`, `item-height`, `item-padding-inline`, `item-padding-block`, `item-radius`, `hover-item-bg`, `hover-item-fg`, `item-disabled-opacity`, `heading-padding-inline`, `heading-padding-block`, `heading-font-size`, `separator-bg`, `separator-margin-block`
- **Eje `size`**: no · **ficheros**: `context-menu.css`
## 1. Knobs fuera de alcance
### 1.1 Directo a primitivo global (0)
### 1.1 Directo a primitivo global (1)
_Ninguno._
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `context-menu.css:36` | `[data-context-menu-content], [data-context-menu-sub-content]` | `font-family` | `var(--style-label-font-family)` |
### 1.2 A través de un privado (0)
@ -24,9 +26,9 @@ _Ninguno._
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `context-menu.css:126` | `[data-context-menu-sub-trigger]::after` | `inline-size` | `0.625rem` |
| 2 | `context-menu.css:127` | `[data-context-menu-sub-trigger]::after` | `block-size` | `0.625rem` |
| 3 | `context-menu.css:152` | `[data-context-menu-separator]` | `block-size` | `1px` |
| 1 | `context-menu.css:130` | `[data-context-menu-sub-trigger]::after` | `inline-size` | `0.625rem` |
| 2 | `context-menu.css:131` | `[data-context-menu-sub-trigger]::after` | `block-size` | `0.625rem` |
| 3 | `context-menu.css:156` | `[data-context-menu-separator]` | `block-size` | `1px` |
### 1.4 Excepciones firmadas (0) — fuera del ratio
@ -42,9 +44,9 @@ Un tema los alcanza **a nivel de sistema**, por diseño (recipe-contract §2).
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `context-menu.css:131` | `[data-context-menu-sub-trigger]::after` | `opacity` | `var(--opacity-muted)` |
| 2 | `context-menu.css:193` | `[data-context-menu-arrow] polygon` | `fill` | `var(--depth-overlay-surface)` |
| 3 | `context-menu.css:202` | `[data-context-menu-arrow] path` | `stroke` | `var(--depth-overlay-border)` |
| 1 | `context-menu.css:135` | `[data-context-menu-sub-trigger]::after` | `opacity` | `var(--opacity-muted)` |
| 2 | `context-menu.css:197` | `[data-context-menu-arrow] polygon` | `fill` | `var(--depth-overlay-surface)` |
| 3 | `context-menu.css:206` | `[data-context-menu-arrow] path` | `stroke` | `var(--depth-overlay-border)` |
## 3. Privados de la receta — ¿de dónde sale su valor?
@ -55,7 +57,7 @@ _La receta no declara privados propios en su CSS._
- **Consume la capa compartida `list-surface`.** Un eje que la capa posee se consume como `var(--_x, var(--x))`; el consumidor **no acuña** `--context-menu-{eje}` para él — sería un vocabulario paralelo (README de `eidos/components`, «Capas compartidas» regla 2).
- **Consume la capa compartida `menu-indicator`.** Un eje que la capa posee se consume como `var(--_x, var(--x))`; el consumidor **no acuña** `--context-menu-{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)
### 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
@ -65,6 +67,7 @@ no distingue lo que debería — se marca `⚠`.
| token (`--context-menu-…`) | scope TSC | valor propuesto | usos |
| --- | --- | --- | ---: |
| `content-font-family` | `root` | `var(--style-label-font-family)` | 1 |
| `sub-trigger-width` | `root` | `0.625rem` | 1 |
| `sub-trigger-height` | `root` | `0.625rem` | 1 |
| `separator-height` | `root` | `1px` | 1 |

@ -5,38 +5,26 @@
> Vista de conjunto: [README](./README.md) · método y protocolo:
> [`PLAN-theming.md`](../../process/PLAN-theming.md) §1, §2, §7.
- **Medido**: 2026-08-23 · **Alcance**: **57%** — 16 de 28 knobs por token público
- **Knobs de apariencia**: 30 — público 16 · privado 0 · global 9 · literal 3 · sistema 2 · excepción 0 _(los dos últimos, fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 19 pública(s) — `content-z`, `content-min-width`, `content-max-height`, `content-padding`, `content-radius`, `content-font-size`, `item-gap`, `item-height`, `item-padding-inline`, `item-padding-block`, `item-radius`, `hover-item-bg`, `hover-item-fg`, `item-disabled-opacity`, `heading-padding-inline`, `heading-padding-block`, `heading-font-size`, `separator-bg`, `separator-margin-block`
- **Medido**: 2026-08-23 · **Alcance**: **96%** — 27 de 28 knobs por token público
- **Knobs de apariencia**: 30 — público 27 · privado 0 · global 1 · literal 0 · sistema 2 · excepción 0 _(los dos últimos, fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 28 pública(s) — `content-z`, `content-fg`, `item-fg`, `item-line-height`, `chevron-stroke`, `chevron-size`, `heading-fg`, `heading-font-weight`, `heading-letter-spacing`, `separator-thickness`, `content-min-width`, `content-max-height`, `content-padding`, `content-radius`, `content-font-size`, `item-gap`, `item-height`, `item-padding-inline`, `item-padding-block`, `item-radius`, `hover-item-bg`, `hover-item-fg`, `item-disabled-opacity`, `heading-padding-inline`, `heading-padding-block`, `heading-font-size`, `separator-bg`, `separator-margin-block`
- **Eje `size`**: no · **ficheros**: `dropdown-menu.css`
## 1. Knobs fuera de alcance
### 1.1 Directo a primitivo global (9)
### 1.1 Directo a primitivo global (1)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `dropdown-menu.css:43` | `[data-dropdown-menu-content], [data-dropdown-menu-sub-content]` | `font-family` | `var(--style-label-font-family, var(--style-label-font-family))` |
| 2 | `dropdown-menu.css:45` | `[data-dropdown-menu-content], [data-dropdown-menu-sub-content]` | `color` | `var(--color-content-primary)` |
| 3 | `dropdown-menu.css:98` | `[data-dropdown-menu-item], [data-dropdown-menu-checkbox-item], [data-dropdown-menu-radio-item], [data-dropdown-menu-sub-trigger]` | `color` | `var(--color-content-primary)` |
| 4 | `dropdown-menu.css:99` | `[data-dropdown-menu-item], [data-dropdown-menu-checkbox-item], [data-dropdown-menu-radio-item], [data-dropdown-menu-sub-trigger]` | `line-height` | `var(--leading-ui)` |
| 5 | `dropdown-menu.css:146` | `[data-dropdown-menu-sub-trigger]::after` | `border-inline-end` | `var(--border-width-medium) solid currentColor` |
| 6 | `dropdown-menu.css:147` | `[data-dropdown-menu-sub-trigger]::after` | `border-block-start` | `var(--border-width-medium) solid currentColor` |
| 7 | `dropdown-menu.css:161` | `[data-dropdown-menu-group-heading]` | `color` | `var(--color-content-muted)` |
| 8 | `dropdown-menu.css:163` | `[data-dropdown-menu-group-heading]` | `font-weight` | `var(--style-label-font-weight, 500)` |
| 9 | `dropdown-menu.css:165` | `[data-dropdown-menu-group-heading]` | `letter-spacing` | `var(--tracking-caps)` |
| 1 | `dropdown-menu.css:47` | `[data-dropdown-menu-content], [data-dropdown-menu-sub-content]` | `font-family` | `var(--style-label-font-family)` |
### 1.2 A través de un privado (0)
_Ninguno._
### 1.3 Literales (3)
### 1.3 Literales (0)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `dropdown-menu.css:144` | `[data-dropdown-menu-sub-trigger]::after` | `inline-size` | `0.625rem` |
| 2 | `dropdown-menu.css:145` | `[data-dropdown-menu-sub-trigger]::after` | `block-size` | `0.625rem` |
| 3 | `dropdown-menu.css:170` | `[data-dropdown-menu-separator]` | `block-size` | `1px` |
_Ninguno._
### 1.4 Excepciones firmadas (0) — fuera del ratio
@ -52,8 +40,8 @@ Un tema los alcanza **a nivel de sistema**, por diseño (recipe-contract §2).
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `dropdown-menu.css:149` | `[data-dropdown-menu-sub-trigger]::after` | `opacity` | `var(--opacity-muted)` |
| 2 | `dropdown-menu.css:186` | `[data-dropdown-menu-arrow]` | `fill` | `var(--depth-overlay-surface)` |
| 1 | `dropdown-menu.css:153` | `[data-dropdown-menu-sub-trigger]::after` | `opacity` | `var(--opacity-muted)` |
| 2 | `dropdown-menu.css:190` | `[data-dropdown-menu-arrow]` | `fill` | `var(--depth-overlay-surface)` |
## 3. Privados de la receta — ¿de dónde sale su valor?
@ -64,7 +52,7 @@ _La receta no declara privados propios en su CSS._
- **Consume la capa compartida `list-surface`.** Un eje que la capa posee se consume como `var(--_x, var(--x))`; el consumidor **no acuña** `--dropdown-menu-{eje}` para él — sería un vocabulario paralelo (README de `eidos/components`, «Capas compartidas» regla 2).
- **Consume la capa compartida `menu-indicator`.** Un eje que la capa posee se consume como `var(--_x, var(--x))`; el consumidor **no acuña** `--dropdown-menu-{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` (10)
### 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
@ -74,21 +62,7 @@ no distingue lo que debería — se marca `⚠`.
| token (`--dropdown-menu-…`) | scope TSC | valor propuesto | usos |
| --- | --- | --- | ---: |
| `content-font-family` | `root` | `var(--style-label-font-family, var(--style-label-font-family))` | 1 |
| `content-fg` | `root` | `var(--color-content-primary)` | 1 |
| `item-fg` | `root` | `var(--color-content-primary)` | 1 |
| `item-line-height` | `root` | `var(--leading-ui)` | 1 |
| `sub-trigger-width` | `root` | `0.625rem` | 1 |
| `sub-trigger-height` | `root` | `0.625rem` | 1 |
| `group-heading-fg` | `root` | `var(--color-content-muted)` | 1 |
| `group-heading-font-weight` | `root` | `var(--style-label-font-weight, 500)` | 1 |
| `group-heading-letter-spacing` | `root` | `var(--tracking-caps)` | 1 |
| `separator-height` | `root` | `1px` | 1 |
### 4.2 Sin nombre mecánico (2)
- **⚠ decisión: `border-inline-end` es shorthand o eje físico — hay que partirlo en ejes lógicos antes de nombrarlo (recipe-contract §1, R-4.4)** — 1: `border-inline-end`.
- **⚠ decisión: `border-block-start` es shorthand o eje físico — hay que partirlo en ejes lógicos antes de nombrarlo (recipe-contract §1, R-4.4)** — 1: `border-block-start`.
| `content-font-family` | `root` | `var(--style-label-font-family)` | 1 |
### 4.4 Lo que hay que comprobar a mano (PLAN-theming §1.3 · §7.4)
@ -102,6 +76,41 @@ no distingue lo que debería — se marca `⚠`.
<!-- veredicto:start -->
_(pendiente — lo escribe el autor; se conserva al regenerar)_
**EJECUTADO 2026-08-23 — 57 % → 96 %**, 9 claves nuevas (20 → 28) y **18
respaldos muertos retirados**. Lo único que queda fuera es la familia
tipográfica, y por una razón medida (abajo).
Es el GEMELO de `context-menu`: mismos nombres, misma doctrina, mismas dos
limpiezas de paso —el `font-family` era respaldo de SÍ MISMO
(`var(--style-label-font-family, var(--style-label-font-family))`) y el peso del
encabezado llevaba un `500` muerto detrás del token que ya vale 500—. Cosidos
la tinta del panel y de la fila, su interlínea, el encabezado entero (tinta,
peso y tracking) y el galón del submenú, que ahora tiene sus dos knobs: el trazo
y la caja.
**El filo del separador deja de ser un `1px` suelto** y pasa a ser el trazo fino
del sistema (`--border-width`, idéntico hoy): un tema que engorde el hairline lo
engorda aquí también.
**Los 18 respaldos**: cada `var(--dropdown-menu-x, …)` de este fichero llevaba
un valor detrás que NO puede ocurrir —las 18 claves están en el contrato—, así
que sólo servían para envejecer contra él. Ninguno movía un píxel; dos ya
discrepaban del contrato en la FUENTE (`--font-size-sm` contra el bundle).
**⚠ `content-font-family` NO se acuña, y la del gemelo se RETIRA.** El panel
lleva `data-depth='overlay'` y el PLANO declara la familia con la misma
especificidad y más tarde en la cascada: gana él. Es la quinta prueba de §12.9
(menubar, popover, float-panel, context-menu y ésta). `context-menu` la tenía
acuñada desde esta misma sesión con una adjudicación escrita; **eso era media
doctrina** —un token público que no alcanza es un token que miente, R-5.4—, así
que se retira allí también y las dos recetas vuelven a su fuente literal con la
razón encima. **Un tercer caso, `combobox`, tiene la misma clave y el mismo
plano**: medido igual, no se toca aquí (no es de este eje-componente) y queda
registrado en §13.
Verificación: **diff de computed = 0** sobre 928 valores en 8 estados (22 nodos
con el panel abierto) · centinela **27/28**, la deshabilitada adjudicada · censo
96 % · `component:audit` PASS · suite eidos con el rojo conocido ajeno ·
`rtl:check` 0 · `docs:check` 0 · captura del panel abierto.
<!-- veredicto:end -->

@ -1208,5 +1208,12 @@ Lo que `chart` destapó (2026-08-22):
Mientras tanto el guard lo adjudica con un PATRÓN
(`SENTINEL_PATTERN_EXCEPTIONS`), para que la razón se escriba una vez.
- **⚠ `combobox.content-font-family` es el TERCER token de tipografía que el
plano `overlay` anula** (medido 2026-08-23). Su panel lleva
`data-depth='overlay'`, así que la clave no mueve nada, igual que las de
`menubar`, `dropdown-menu` y `context-menu` —las tres retiradas ya—. No se
toca aquí porque no es el componente en curso: cuando §12.9 se firme, o se
retira o empieza a pintar, y la decisión es la MISMA para las cuatro.
**Deps**: ninguna. Son mejoras del instrumental del eje, ejecutables cuando
estorben.

@ -455,6 +455,32 @@ de commit. Sin artefacto, el paso no se ha hecho.
## 8. Registro
- 2026-08-23 — **`dropdown-menu` 57 % → 96 %** (9 claves, 18 respaldos muertos
retirados) **y la tipografía del gemelo, RETIRADA**: `context-menu` 90 % →
87 %, a la baja y a propósito. Global 64 %.
Cosidos la tinta del panel y de la fila, su interlínea, el encabezado entero y
el galón del submenú (trazo + caja). **El filo del separador deja de ser un
`1px` suelto** y pasa a `--border-width`. Las dos limpiezas del gemelo se
repiten aquí: `font-family` era respaldo de SÍ MISMO y el peso del encabezado
llevaba un `500` muerto.
**Los 18 respaldos**: cada `var(--dropdown-menu-x, …)` llevaba detrás un valor
que no puede ocurrir —las claves están en el contrato— y sólo servía para
envejecer contra él; dos ya discrepaban en la FUENTE.
**⚠ `content-font-family` no se acuña, y la de `context-menu` se RETIRA.** El
panel lleva `data-depth='overlay'` y el plano gana (quinta prueba de §12.9).
Tenerla acuñada con una adjudicación escrita —como quedó ayer en el gemelo—
era media doctrina: **un público que no alcanza es un token que miente**
(R-5.4). Las dos recetas vuelven a la fuente literal con la razón encima.
`combobox` tiene la misma clave sobre el mismo plano: medido, registrado en
§13, no tocado aquí.
Gates: sonda 0 diffs en 928 valores × 8 estados · centinela 27/28 · censo
96 % · audit PASS · suite con el rojo conocido ajeno · rtl 0 · docs 0 ·
captura del panel.
- 2026-08-23 — **`empty-state` 29 % → 100 %** (20 claves, 6 → 26) **y el
centinela 26/26 sin una sola adjudicación**. Global 64 %.

@ -330,7 +330,6 @@ export const SENTINEL_EXCEPTIONS: Record<string, Record<string, string>> = {
// Third measurement of the same finding: the overlay DEPTH PLANE imposes the
// panel font and beats the recipe (section 12.9, pending signature, 20
// components). Writing the token moves nothing while the plane does.
'content-font-family': 'the overlay depth plane wins over the panel font - section 12.9, measured again 2026-08-22',
'item-disabled-opacity': 'no disabled item in the demo; forced data-disabled -> reaches (0.55 -> 0.123)'
},
metrics: {
@ -585,6 +584,10 @@ export const SENTINEL_EXCEPTIONS: Record<string, Record<string, string>> = {
'transition-ease': 'same instrument limit; measured unfrozen -> reaches (cubic-bezier(0.4, 0, 0.2, 1) -> steps(7))',
'disabled-opacity': 'no disabled trigger or eye dropper in the demo; forced data-disabled -> reaches (0.4 -> 0.137)'
},
'dropdown-menu': {
'item-disabled-opacity':
'no disabled item in the demo menu; forced data-disabled 2026-08-23 -> reaches (0.55 -> 0.137)'
},
button: {
// Measured one by one 2026-08-23. The icon scale needs a real <svg> child,
// the radius scale is an OPT-IN prop the demo does not set, and the four

@ -29,7 +29,11 @@
/* Elevation — surface · border · shadow — from the `overlay` plane via
`data-depth` (A1, Decisión 8). Radius / font / color stay the panel's own. */
border-radius: var(--context-menu-content-radius, var(--radius-md));
font-family: var(--context-menu-content-font-family);
/* DEAD here, and left as the literal source on purpose: the panel carries
`data-depth=overlay` and the PLANE declares font-family at the same
specificity, later in the cascade. Whether a depth plane may own the
typography is PLAN-theming §12.9, still unsigned. */
font-family: var(--style-label-font-family);
font-size: var(--context-menu-content-font-size, var(--font-size-sm));
color: var(--context-menu-content-fg);
}

@ -32,17 +32,21 @@
* positioner wrapper. */
z-index: var(--dropdown-menu-content-z);
min-inline-size: var(--dropdown-menu-content-min-width, 12rem);
max-block-size: var(--dropdown-menu-content-max-height, min(60vh, 22rem));
min-inline-size: var(--dropdown-menu-content-min-width);
max-block-size: var(--dropdown-menu-content-max-height);
overflow-y: auto;
overscroll-behavior: contain;
padding: var(--dropdown-menu-content-padding, var(--space-1));
padding: var(--dropdown-menu-content-padding);
/* Elevation — surface · border · shadow — from the `overlay` plane via
`data-depth` (A1, Decisión 8). Radius / font / color stay the panel's own. */
border-radius: var(--dropdown-menu-content-radius, var(--radius-md));
font-family: var(--style-label-font-family, var(--style-label-font-family));
font-size: var(--dropdown-menu-content-font-size, var(--font-size-sm));
color: var(--color-content-primary);
border-radius: var(--dropdown-menu-content-radius);
/* DEAD here, and left as the literal source on purpose: the panel carries
`data-depth=overlay` and the PLANE declares font-family at the same
specificity, later in the cascade. Whether a depth plane may own the
typography is PLAN-theming §12.9, still unsigned. */
font-family: var(--style-label-font-family);
font-size: var(--dropdown-menu-content-font-size);
color: var(--dropdown-menu-content-fg);
}
[data-dropdown-menu-content]:focus-visible,
@ -90,13 +94,13 @@
[data-dropdown-menu-sub-trigger] {
display: flex;
align-items: center;
gap: var(--dropdown-menu-item-gap, var(--space-2));
min-block-size: var(--dropdown-menu-item-height, 2rem);
padding-block: var(--dropdown-menu-item-padding-block, var(--space-1));
padding-inline: var(--dropdown-menu-item-padding-inline, var(--space-2));
border-radius: var(--dropdown-menu-item-radius, var(--radius-sm));
color: var(--color-content-primary);
line-height: var(--leading-ui);
gap: var(--dropdown-menu-item-gap);
min-block-size: var(--dropdown-menu-item-height);
padding-block: var(--dropdown-menu-item-padding-block);
padding-inline: var(--dropdown-menu-item-padding-inline);
border-radius: var(--dropdown-menu-item-radius);
color: var(--dropdown-menu-item-fg);
line-height: var(--dropdown-menu-item-line-height);
cursor: pointer;
user-select: none;
transition: background var(--duration-fast) var(--ease-default),
@ -113,8 +117,8 @@
[data-dropdown-menu-sub-trigger]:focus-visible,
[data-dropdown-menu-sub-trigger][data-state='open'] {
outline: none;
background: var(--dropdown-menu-hover-item-bg, var(--color-primary-element));
color: var(--dropdown-menu-hover-item-fg, var(--color-content-primary));
background: var(--dropdown-menu-hover-item-bg);
color: var(--dropdown-menu-hover-item-fg);
}
[data-dropdown-menu-item][data-disabled],
@ -128,7 +132,7 @@
[data-dropdown-menu-checkbox-item][data-disabled],
[data-dropdown-menu-radio-item][data-disabled],
[data-dropdown-menu-sub-trigger][data-disabled] {
opacity: var(--dropdown-menu-item-disabled-opacity, 0.55);
opacity: var(--dropdown-menu-item-disabled-opacity);
}
/* Indicator slot + ::before painting for checkbox-item / radio-item shared
@ -141,10 +145,10 @@
[data-dropdown-menu-sub-trigger]::after {
content: '';
margin-inline-start: auto;
inline-size: 0.625rem;
block-size: 0.625rem;
border-inline-end: var(--border-width-medium) solid currentColor;
border-block-start: var(--border-width-medium) solid currentColor;
inline-size: var(--dropdown-menu-chevron-size);
block-size: var(--dropdown-menu-chevron-size);
border-inline-end: var(--dropdown-menu-chevron-stroke) solid currentColor;
border-block-start: var(--dropdown-menu-chevron-stroke) solid currentColor;
transform: rotate(45deg);
opacity: var(--opacity-muted);
}
@ -156,19 +160,19 @@
}
[data-dropdown-menu-group-heading] {
padding-block: var(--dropdown-menu-heading-padding-block, var(--space-1));
padding-inline: var(--dropdown-menu-heading-padding-inline, var(--space-2));
color: var(--color-content-muted);
font-size: var(--dropdown-menu-heading-font-size, var(--font-size-xs));
font-weight: var(--style-label-font-weight, 500);
padding-block: var(--dropdown-menu-heading-padding-block);
padding-inline: var(--dropdown-menu-heading-padding-inline);
color: var(--dropdown-menu-heading-fg);
font-size: var(--dropdown-menu-heading-font-size);
font-weight: var(--dropdown-menu-heading-font-weight);
text-transform: uppercase;
letter-spacing: var(--tracking-caps);
letter-spacing: var(--dropdown-menu-heading-letter-spacing);
}
[data-dropdown-menu-separator] {
margin-block: var(--dropdown-menu-separator-margin-block, var(--space-1));
block-size: 1px;
background: var(--dropdown-menu-separator-bg, var(--color-border-subtle));
margin-block: var(--dropdown-menu-separator-margin-block);
block-size: var(--dropdown-menu-separator-thickness);
background: var(--dropdown-menu-separator-bg);
}
/* ── Trigger ──────────────────────────────────────────────────────── */

@ -4648,6 +4648,15 @@
--navigation-menu-active-trigger-fg: var(--color-content-primary);
--navigation-menu-active-trigger-bg: var(--color-primary-element);
--dropdown-menu-content-z: var(--z-index-overlay-floating);
--dropdown-menu-content-fg: var(--color-content-primary);
--dropdown-menu-item-fg: var(--color-content-primary);
--dropdown-menu-item-line-height: var(--leading-ui);
--dropdown-menu-chevron-stroke: var(--border-width-medium);
--dropdown-menu-chevron-size: 0.625rem;
--dropdown-menu-heading-fg: var(--color-content-muted);
--dropdown-menu-heading-font-weight: var(--style-label-font-weight);
--dropdown-menu-heading-letter-spacing: var(--tracking-caps);
--dropdown-menu-separator-thickness: var(--border-width);
--dropdown-menu-content-min-width: 12rem;
--dropdown-menu-content-max-height: min(60vh, 22rem);
--dropdown-menu-content-padding: var(--space-1);
@ -4701,7 +4710,6 @@
--menubar-panel-font-size-sm: var(--size-sm-font-size);
--menubar-panel-font-size-md: var(--size-md-font-size);
--menubar-panel-font-size-lg: var(--size-lg-font-size);
--context-menu-content-font-family: var(--style-label-font-family);
--context-menu-content-fg: var(--color-content-primary);
--context-menu-item-fg: var(--color-content-primary);
--context-menu-item-line-height: var(--leading-ui);

@ -7806,6 +7806,24 @@ export const THEME_BASE_RECIPE_TOKENS = defineRecipes({
// page element with a positive z-index (e.g. a selected
// `[data-toggle-group-item]`, z-index 1) paints THROUGH it.
'content-z': 'var(--z-index-overlay-floating)',
// El cromo del panel y de sus filas, gemelo del de context-menu: mismos
// nombres, misma doctrina. Con dos limpiezas de paso, las mismas que allí:
// el `font-family` era respaldo de SÍ MISMO y el peso del encabezado
// llevaba un `500` muerto detrás del token que ya vale 500.
// NO `content-font-family`: the panel carries `data-depth=overlay` and the
// PLANE declares the family at the same specificity, later in the cascade —
// it wins, so a token here would lie (measured 2026-08-23; PLAN §12.9).
'content-fg': 'var(--color-content-primary)',
'item-fg': 'var(--color-content-primary)',
'item-line-height': 'var(--leading-ui)',
// El galón del submenú: dos bordes girados 45°, un trazo y una caja.
'chevron-stroke': 'var(--border-width-medium)',
'chevron-size': '0.625rem',
'heading-fg': 'var(--color-content-muted)',
'heading-font-weight': 'var(--style-label-font-weight)',
'heading-letter-spacing': 'var(--tracking-caps)',
// El filo del separador es el trazo fino del sistema, no un 1px suelto.
'separator-thickness': 'var(--border-width)',
'content-min-width': '12rem',
'content-max-height': 'min(60vh, 22rem)',
'content-padding': 'var(--space-1)',
@ -7895,7 +7913,9 @@ export const THEME_BASE_RECIPE_TOKENS = defineRecipes({
// `font-family` leía `var(--style-label-font-family, var(--style-label-font-family))`
// —respaldo de sí mismo— y el peso del encabezado llevaba un `500` muerto
// detrás del token que ya vale 500.
'content-font-family': 'var(--style-label-font-family)',
// NO `content-font-family`: the panel carries `data-depth=overlay` and the
// PLANE declares the family at the same specificity, later in the cascade —
// it wins, so a token here would lie (measured 2026-08-23; PLAN §12.9).
'content-fg': 'var(--color-content-primary)',
'item-fg': 'var(--color-content-primary)',
'item-line-height': 'var(--leading-ui)',

Loading…
Cancel
Save

Powered by TurnKey Linux.