uix(drawer): temable — 57 % → 72 %, el disparador gemelo del de popover

10 claves (44 → 54). Censo drawer 57 % → **72 %**; global del eje 61 %.

Mismo cromo que el disparador de `popover` y con la misma corrección:
`min-block-size` lee el BUNDLE de talla (`--size-md-control-height`) y no el
primitivo crudo que el CSS traía. Aquí no hay estado abierto que teñir, así que
el hover lleva una sola clave.

LO QUE QUEDA FUERA ES LA FIRMA DE SALIDA, y no se cose aquí: los cuatro
`border-{side}: var(--border-width-thick) solid transparent` son la base que las
reglas de `data-last-action` tiñen al cerrar. La misma matriz vive en `popover` y
`dialog`, ya ha derivado entre las tres, y es mapeo transversal según la tabla
morfo→eidos → §13.

Los ocho privados son canales de valor (los `*-override` que el wrapper escribe
desde sus props, y el par `panel-width/height` de la variante persistente) y los
cinco literales, identidad.

UNA ADJUDICACIÓN, MEDIDA, Y DE UNA CLASE NUEVA DEL INSTRUMENTO: el guard ABRE el
cajón antes de probar, y entonces el velo tapa el disparador — la pasada de hover
no puede posarse en él (Playwright se niega a hacer hover sobre un nodo que no
recibe puntero). Con el cajón cerrado y hover a mano, `hover-trigger-bg` alcanza
(oklch(0.931 0 0) → rgb(1,2,3)).

Lo rojo que queda son **24 tokens anteriores** a este commit —el asa de arrastre,
el anillo de arrastre, los anchos y altos por talla, los gaps de cabecera y pie—,
todos de partes o estados que la demo no monta. Este commit no los empeora.

Gates: sonda antes/después **0 diffs en 390 valores** (8 estados) · censo --only
72 % · `--names` sin desviadas · component-audit PASS · eidos-lint 0 invalid ·
suite eidos con el único rojo conocido · rtl:check 0 · docs:check 0 · captura del
escenario mirada (disparador con su cromo intacto).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
alpha-0.1-background
dev 2 months ago
parent 5336fd0009
commit 78dfa236fd

@ -10,8 +10,8 @@
- **Medido**: 2026-08-22 · **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**: **61%** — 2813 de 4628 knobs pasan por un token público del componente
- **Reparto**: público 2813 · privado 339 · global 977 · literal 499 · sistema transversal 509 · excepción firmada 54 _(los dos últimos, fuera del ratio)_
- **Alcance global**: **61%** — 2822 de 4628 knobs pasan por un token público del componente
- **Reparto**: público 2822 · privado 339 · global 968 · literal 499 · sistema transversal 509 · excepción firmada 54 _(los dos últimos, fuera del ratio)_
- **Sin token público propio**: 24 · **alcance < 20 %**: 16 · **alcance 100 %**: 17 · **con eje `size`**: 58
## Cómo se lee
@ -81,17 +81,17 @@ theming §6.7 lo pone delante (`hover-bg`). Las fichas proponen la forma
| 6 | [field-langs](./field-langs.md) | 37 | 0% | 39 | 0 |
| 7 | [date-range-picker](./date-range-picker.md) | 30 | 40% | 53 | 22 |
| 8 | [radio-cards](./radio-cards.md) | 27 | 61% | 72 | 40 |
| 9 | [drawer](./drawer.md) | 26 | 57% | 62 | 44 |
| 10 | [natural-time-picker](./natural-time-picker.md) | 25 | 67% | 77 | 62 |
| 11 | [menubar](./menubar.md) | 22 | 15% | 27 | 5 |
| 12 | [nav-tree](./nav-tree.md) | 22 | 4% | 24 | 1 |
| 13 | [color-picker](./color-picker.md) | 20 | 75% | 82 | 71 |
| 14 | [float-panel](./float-panel.md) | 20 | 59% | 53 | 28 |
| 15 | [password-field](./password-field.md) | 20 | 41% | 35 | 23 |
| 16 | [navigation-menu](./navigation-menu.md) | 19 | 44% | 38 | 26 |
| 17 | [sidebar](./sidebar.md) | 19 | 55% | 45 | 50 |
| 18 | [field](./field.md) | 18 | 73% | 72 | 60 |
| 19 | [mockup](./mockup.md) | 18 | 38% | 29 | 0 |
| 9 | [natural-time-picker](./natural-time-picker.md) | 25 | 67% | 77 | 62 |
| 10 | [menubar](./menubar.md) | 22 | 15% | 27 | 5 |
| 11 | [nav-tree](./nav-tree.md) | 22 | 4% | 24 | 1 |
| 12 | [color-picker](./color-picker.md) | 20 | 75% | 82 | 71 |
| 13 | [float-panel](./float-panel.md) | 20 | 59% | 53 | 28 |
| 14 | [password-field](./password-field.md) | 20 | 41% | 35 | 23 |
| 15 | [navigation-menu](./navigation-menu.md) | 19 | 44% | 38 | 26 |
| 16 | [sidebar](./sidebar.md) | 19 | 55% | 45 | 50 |
| 17 | [field](./field.md) | 18 | 73% | 72 | 60 |
| 18 | [mockup](./mockup.md) | 18 | 38% | 29 | 0 |
| 19 | [drawer](./drawer.md) | 17 | 72% | 62 | 54 |
| 20 | [onion-menu](./onion-menu.md) | 17 | 29% | 27 | 12 |
## Sin ningún token público propio (24)
@ -155,7 +155,6 @@ La columna «contrato» cuenta las claves **públicas** del bloque del component
| [button](./button.md) | 55% | 30 | 16 | 6 | 0 | 7 | 1 | 105 | y |
| [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 | – |
| [drawer](./drawer.md) | 57% | 62 | 34 | 8 | 13 | 5 | 2 | 44 | y |
| [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 |
@ -184,6 +183,7 @@ La columna «contrato» cuenta las claves **públicas** del bloque del component
| [form](./form.md) | 71% | 52 | 36 | 8 | 5 | 2 | 1 | 54 | y |
| [image-picker](./image-picker.md) | 71% | 20 | 12 | 0 | 1 | 4 | 3 | 13 | – |
| [skip-link](./skip-link.md) | 71% | 8 | 5 | 0 | 0 | 2 | 1 | 8 | – |
| [drawer](./drawer.md) | 72% | 62 | 43 | 8 | 4 | 5 | 2 | 54 | y |
| [dialog](./dialog.md) | 73% | 33 | 24 | 4 | 4 | 1 | 0 | 37 | y |
| [virtual-grid](./virtual-grid.md) | 73% | 12 | 8 | 0 | 0 | 3 | 1 | 12 | – |
| [field](./field.md) | 73% | 72 | 49 | 1 | 7 | 10 | 5 | 60 | y |

@ -5,30 +5,21 @@
> Vista de conjunto: [README](./README.md) · método y protocolo:
> [`PLAN-theming.md`](../../process/PLAN-theming.md) §1, §2, §7.
- **Medido**: 2026-08-22 · **Alcance**: **57%** — 34 de 60 knobs por token público
- **Knobs de apariencia**: 62 — público 34 · privado 8 · global 13 · literal 5 · sistema 2 · excepción 0 _(los dos últimos, fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 44 pública(s) — `overlay-bg`, `overlay-blur`, `overlay-opacity`, `overlay-z`, `content-z`, `inline-z`, `handle-bg`, `active-handle-bg`, `handle-radius`, `handle-length`, `handle-thickness`, `handle-active-scale`, `handle-transition-duration`, `handle-transition-ease`, `content-fg`, `content-radius`, `content-shadow-dragging`, `content-ring-dragging-width`, `content-ring-dragging-fg`, `content-ring-dragging-offset`, `content-inset`, `content-width-sm`, `content-width-md`, `content-width-lg`, `content-height-sm`, `content-height-md`, `content-height-lg`, `content-padding-sm`, `content-padding-md`, `content-padding-lg`, `content-padding-full`, `stack-gap`, `header-gap`, `footer-gap`, `footer-margin-top`, `title-font-family`, `title-font-size`, `title-font-weight`, `title-line-height`, `title-fg`, `description-font-family`, `description-font-size`, `description-line-height`, `description-fg`
- **Medido**: 2026-08-22 · **Alcance**: **72%** — 43 de 60 knobs por token público
- **Knobs de apariencia**: 62 — público 43 · privado 8 · global 4 · literal 5 · sistema 2 · excepción 0 _(los dos últimos, fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 54 pública(s) — `trigger-gap`, `trigger-min-block-size`, `trigger-padding-inline`, `trigger-border-width`, `trigger-border`, `trigger-radius`, `trigger-bg`, `trigger-fg`, `trigger-font-weight`, `hover-trigger-bg`, `overlay-bg`, `overlay-blur`, `overlay-opacity`, `overlay-z`, `content-z`, `inline-z`, `handle-bg`, `active-handle-bg`, `handle-radius`, `handle-length`, `handle-thickness`, `handle-active-scale`, `handle-transition-duration`, `handle-transition-ease`, `content-fg`, `content-radius`, `content-shadow-dragging`, `content-ring-dragging-width`, `content-ring-dragging-fg`, `content-ring-dragging-offset`, `content-inset`, `content-width-sm`, `content-width-md`, `content-width-lg`, `content-height-sm`, `content-height-md`, `content-height-lg`, `content-padding-sm`, `content-padding-md`, `content-padding-lg`, `content-padding-full`, `stack-gap`, `header-gap`, `footer-gap`, `footer-margin-top`, `title-font-family`, `title-font-size`, `title-font-weight`, `title-line-height`, `title-fg`, `description-font-family`, `description-font-size`, `description-line-height`, `description-fg`
- **Eje `size`**: sí · **ficheros**: `drawer.css`
## 1. Knobs fuera de alcance
### 1.1 Directo a primitivo global (13)
### 1.1 Directo a primitivo global (4)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `drawer.css:7` | `[data-drawer-trigger]` | `gap` | `var(--space-2)` |
| 2 | `drawer.css:8` | `[data-drawer-trigger]` | `min-block-size` | `var(--control-height-md)` |
| 3 | `drawer.css:9` | `[data-drawer-trigger]` | `padding-inline` | `var(--space-3)` |
| 4 | `drawer.css:10` | `[data-drawer-trigger]` | `border` | `var(--border-width) solid var(--color-border-default)` |
| 5 | `drawer.css:11` | `[data-drawer-trigger]` | `border-radius` | `var(--radius-md)` |
| 6 | `drawer.css:12` | `[data-drawer-trigger]` | `background` | `var(--color-surface-raised)` |
| 7 | `drawer.css:13` | `[data-drawer-trigger]` | `color` | `var(--color-content-primary)` |
| 8 | `drawer.css:15` | `[data-drawer-trigger]` | `font-weight` | `var(--font-weight-medium)` |
| 9 | `drawer.css:20` | `[data-drawer-trigger]:hover` | `background` | `var(--color-surface-overlay)` |
| 10 | `drawer.css:110` | `[data-drawer-content][data-side='right']` | `border-left` | `var(--border-width-thick) solid transparent` |
| 11 | `drawer.css:113` | `[data-drawer-content][data-side='left']` | `border-right` | `var(--border-width-thick) solid transparent` |
| 12 | `drawer.css:116` | `[data-drawer-content][data-side='top']` | `border-bottom` | `var(--border-width-thick) solid transparent` |
| 13 | `drawer.css:119` | `[data-drawer-content][data-side='bottom']` | `border-top` | `var(--border-width-thick) solid transparent` |
| 1 | `drawer.css:110` | `[data-drawer-content][data-side='right']` | `border-left` | `var(--border-width-thick) solid transparent` |
| 2 | `drawer.css:113` | `[data-drawer-content][data-side='left']` | `border-right` | `var(--border-width-thick) solid transparent` |
| 3 | `drawer.css:116` | `[data-drawer-content][data-side='top']` | `border-bottom` | `var(--border-width-thick) solid transparent` |
| 4 | `drawer.css:119` | `[data-drawer-content][data-side='bottom']` | `border-top` | `var(--border-width-thick) solid transparent` |
### 1.2 A través de un privado (8)
@ -86,7 +77,7 @@ Consumidos y **no declarados en el CSS** (vienen de `base.ts` o de un estilo inl
- **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` (8)
### 4.1 Tokens a declarar en `lib/recipes/base.ts` (0)
Valor **verbatim** del CSS de hoy: el default no se mueve, sólo cambia quién
puede moverlo. Nombres derivados de recipe-contract §1 (ejes lógicos, talla
@ -96,18 +87,9 @@ no distingue lo que debería — se marca `⚠`.
| token (`--drawer-…`) | scope TSC | valor propuesto | usos |
| --- | --- | --- | ---: |
| `trigger-gap` | `root` | `var(--space-2)` | 1 |
| `trigger-height` | `root` | `var(--control-height-md)` | 1 |
| `trigger-padding-inline` | `root` | `var(--space-3)` | 1 |
| `trigger-radius` | `root` | `var(--radius-md)` | 1 |
| `trigger-bg` | `root` | `var(--color-surface-raised)` | 1 |
| `trigger-fg` | `root` | `var(--color-content-primary)` | 1 |
| `trigger-font-weight` | `root` | `var(--font-weight-medium)` | 1 |
| `hover-trigger-bg` | `root` | `var(--color-surface-overlay)` | 1 |
### 4.2 Sin nombre mecánico (18)
- **⚠ 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)** — 1: `border`.
### 4.2 Sin nombre mecánico (17)
- **⚠ decisión: `1` es un valor identidad o geometría de layout, no un knob de tema — el perímetro de «knob» es D-TH.2, sin firmar** — 1: `opacity`.
- **⚠ 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: `min-inline-size`, `min-block-size`.
- **⚠ decisión: `border-left` es shorthand o eje físico — hay que partirlo en ejes lógicos antes de nombrarlo (recipe-contract §1, R-4.4)** — 1: `border-left`.
@ -130,6 +112,35 @@ no distingue lo que debería — se marca `⚠`.
<!-- veredicto:start -->
_(pendiente — lo escribe el autor; se conserva al regenerar)_
**EJECUTADO 2026-08-22 — 57 % → 72 %**, 10 claves: el DISPARADOR, el mismo
cromo que el de `popover` y con la misma corrección —`min-block-size` lee el
BUNDLE de talla (`--size-md-control-height`) y no el primitivo crudo que el CSS
traía—. Aquí no hay estado abierto que teñir, así que el hover lleva una sola
clave.
**Lo que queda fuera es la FIRMA DE SALIDA y no se cose aquí**: los cuatro
`border-{side}: var(--border-width-thick) solid transparent` son la base que
las reglas de `data-last-action` tiñen al cerrar. La misma matriz vive en
`popover` y `dialog`, ya ha derivado entre las tres, y es mapeo transversal
según la tabla morfo→eidos → next-features §13.
Los ocho privados son canales de valor: los `*-override` que el wrapper
escribe desde sus props y el par `panel-width/height` de la variante
persistente. Los cinco literales son identidad (cuatro `100%` del panel
persistente y el `opacity: 1` que devuelve el velo a opaco en la variante
overlay).
**Una adjudicación, medida, y de una clase nueva del instrumento**: el guard
ABRE el cajón antes de probar, y entonces el velo tapa el disparador — la
pasada de hover no puede posarse en él (Playwright se niega a hacer hover sobre
un nodo que no recibe puntero). Con el cajón cerrado y hover a mano,
`hover-trigger-bg` alcanza (oklch(0.931 0 0) → rgb(1,2,3)).
**Lo rojo que queda es ANTERIOR**: 24 tokens sin adjudicar del bloque que ya
existía —el asa de arrastre, el anillo de arrastre, los anchos y altos por
talla, los gaps de cabecera y pie—, todos de partes o estados que la demo no
monta. Su pase queda anotado; este commit no los empeora.
Sonda antes/después: **0 diffs en 390 valores** (8 estados).
<!-- veredicto:end -->

@ -455,6 +455,21 @@ de commit. Sin artefacto, el paso no se ha hecho.
## 8. Registro
- 2026-08-22 — **`drawer` 57 % → 72 %** (10 claves: el disparador, gemelo del
de popover, con `min-block-size` al bundle). Global 61 %.
Lo que queda fuera es la FIRMA DE SALIDA (los cuatro bordes base que
`data-last-action` tiñe al cerrar) → §13, y ocho privados que son canales de
valor del wrapper.
**Una adjudicación de clase nueva**: el guard ABRE el cajón antes de probar y
el velo tapa el disparador, así que la pasada de hover no puede posarse en él.
Con el cajón cerrado y hover a mano, `hover-trigger-bg` alcanza. Los otros 24
rojos son anteriores (asa, anillo de arrastre, tallas del panel).
Gates: sonda 0 diffs en 390 valores · censo 72 % · `--names` sin desviadas ·
audit PASS · lint 0 · suite con el rojo conocido · rtl 0 · docs 0.
- 2026-08-22 — **`aura` 43 % → 80 %** (13 claves). Global 61 %.
Su texto de estado baja un paso del bundle por talla y las TRES coordenadas

@ -314,6 +314,13 @@ export const SENTINEL_EXCEPTIONS: Record<string, Record<string, string>> = {
'disabled-row-fg':
'no disabled row in the demo (0 mounted, counted); forced data-disabled → reaches (oklch(0.61 0 0) → rgb(1,2,3))'
},
drawer: {
// The guard OPENS the drawer before testing, and the overlay then covers the
// trigger: the hover pass cannot reach it (Playwright refuses to hover a
// node that does not receive the pointer). Hovered by hand with the drawer
// closed 2026-08-22 -> reaches.
'hover-trigger-bg': 'the open drawer overlay covers the trigger, so the hover pass cannot land on it; hovered by hand with the drawer closed -> reaches (oklch(0.931 0 0) -> rgb(1,2,3))'
},
aura: {
// The six state tints and the halo paint the INDICATOR background, and the
// demo runs with the WebGL canvas mounted — the rule

@ -4,20 +4,20 @@
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--space-2);
min-block-size: var(--control-height-md);
padding-inline: var(--space-3);
border: var(--border-width) solid var(--color-border-default);
border-radius: var(--radius-md);
background: var(--color-surface-raised);
color: var(--color-content-primary);
gap: var(--drawer-trigger-gap);
min-block-size: var(--drawer-trigger-min-block-size);
padding-inline: var(--drawer-trigger-padding-inline);
border: var(--drawer-trigger-border-width) solid var(--drawer-trigger-border);
border-radius: var(--drawer-trigger-radius);
background: var(--drawer-trigger-bg);
color: var(--drawer-trigger-fg);
font: inherit;
font-weight: var(--font-weight-medium);
font-weight: var(--drawer-trigger-font-weight);
cursor: pointer;
}
[data-drawer-trigger]:hover {
background: var(--color-surface-overlay);
background: var(--drawer-hover-trigger-bg);
}
[data-drawer-trigger]:focus-visible {

@ -1269,6 +1269,16 @@
--form-transition-duration: var(--duration-fast);
--form-transition-ease: var(--ease-default);
--form-disabled-opacity: var(--opacity-disabled);
--drawer-trigger-gap: var(--space-2);
--drawer-trigger-min-block-size: var(--size-md-control-height);
--drawer-trigger-padding-inline: var(--space-3);
--drawer-trigger-border-width: var(--border-width);
--drawer-trigger-border: var(--color-border-default);
--drawer-trigger-radius: var(--radius-md);
--drawer-trigger-bg: var(--color-surface-raised);
--drawer-trigger-fg: var(--color-content-primary);
--drawer-trigger-font-weight: var(--font-weight-medium);
--drawer-hover-trigger-bg: var(--color-surface-overlay);
--drawer-overlay-bg: var(--color-overlay);
--drawer-overlay-blur: var(--blur-lg);
--drawer-overlay-opacity: 62%;

@ -1295,6 +1295,20 @@ export const THEME_BASE_RECIPE_TOKENS = defineRecipes({
'disabled-opacity': 'var(--opacity-disabled)'
},
drawer: {
// El disparador por defecto — el mismo cromo que el de popover, y como allí
// `min-block-size` lee el BUNDLE de talla y no el primitivo crudo que el CSS
// traía. Su hover comparte valor con el estado abierto de popover; aquí no
// hay estado abierto que teñir, así que una sola clave.
'trigger-gap': 'var(--space-2)',
'trigger-min-block-size': 'var(--size-md-control-height)',
'trigger-padding-inline': 'var(--space-3)',
'trigger-border-width': 'var(--border-width)',
'trigger-border': 'var(--color-border-default)',
'trigger-radius': 'var(--radius-md)',
'trigger-bg': 'var(--color-surface-raised)',
'trigger-fg': 'var(--color-content-primary)',
'trigger-font-weight': 'var(--font-weight-medium)',
'hover-trigger-bg': 'var(--color-surface-overlay)',
'overlay-bg': 'var(--color-overlay)',
'overlay-blur': 'var(--blur-lg)',
'overlay-opacity': '62%',

Loading…
Cancel
Save

Powered by TurnKey Linux.