# float-panel — alcance de tema: análisis y propuesta > Generado por `node --import tsx/esm scripts/theming-census.ts --report`. > Lo **medido** y la **propuesta** se regeneran; el **Veredicto** (§5) se conserva. > Vista de conjunto: [README](./README.md) · método y protocolo: > [`PLAN-theming.md`](../../process/PLAN-theming.md) §1, §2, §7. - **Medido**: 2026-08-24 · **Alcance**: **84%** — 41 de 49 knobs por token público - **Knobs de apariencia**: 53 — público 41 · privado 3 · global 0 · literal 5 · sistema 4 · excepción 0 _(los dos últimos, fuera del ratio)_ - **Contrato hoy** (`lib/recipes/base.ts`): 40 pública(s) — `outline-bg`, `outline-border`, `ghost-bg`, `ghost-border`, `ghost-shadow`, `action-radius`, `action-fg`, `action-font-size`, `hover-action-bg`, `hover-action-fg`, `resize-grip-fg`, `resize-grip-radius`, `bg`, `border`, `border-width`, `radius`, `shadow`, `shadow-topmost`, `active-shadow`, `fg`, `font-family`, `font-size`, `line-height`, `header-bg`, `header-padding-inline`, `header-padding-block`, `header-gap`, `controls-gap`, `title-font-size`, `title-font-weight`, `title-fg`, `body-padding-inline`, `body-padding-block`, `resize-size`, `resize-edge`, `button-size`, `grip-size`, `accent`, `transition-duration`, `transition-ease` · 1 privada(s) forward — `_palette-border` - **Eje `size`**: no · **ficheros**: `float-panel.css` ## 1. Knobs fuera de alcance ### 1.1 Directo a primitivo global (0) _Ninguno._ ### 1.2 A través de un privado (3) | # | fichero:línea | selector | propiedad | valor | | ---: | --- | --- | --- | --- | | 1 | `float-panel.css:107` | `[data-float-panel-header]` | `border-block-end` | `var(--border-width-medium) solid var(--_float-panel-accent)` | | 2 | `float-panel.css:184` | `[data-float-panel-action]:focus-visible, [data-float-panel-stage-trigger]:focus-visible` | `box-shadow` | `0 0 0 2px var(--_float-panel-accent)` | | 3 | `float-panel.css:255` | `[data-float-panel-resize-grip]:hover, [data-float-panel-resize-grip][data-grabbed]` | `color` | `var(--_float-panel-accent)` | ### 1.3 Literales (5) | # | fichero:línea | selector | propiedad | valor | | ---: | --- | --- | --- | --- | | 1 | `float-panel.css:173` | `[data-float-panel-action], [data-float-panel-stage-trigger]` | `line-height` | `1` | | 2 | `float-panel.css:231` | `[data-float-panel-resize-grip]` | `background-image` | `repeating-linear-gradient( -45deg, transparent 0 3px, currentColor 3px 4.5px )` | | 3 | `float-panel.css:248` | `[data-float-panel-resize-grip]:dir(rtl)` | `background-image` | `repeating-linear-gradient(45deg, transparent 0 3px, currentColor 3px 4.5px)` | | 4 | `float-panel.css:254` | `[data-float-panel-resize-grip]:hover, [data-float-panel-resize-grip][data-grabbed]` | `opacity` | `1` | | 5 | `float-panel.css:259` | `[data-float-panel-resize-grip]:focus-visible` | `opacity` | `1` | ### 1.4 Excepciones firmadas (0) — fuera del ratio Literales que llevan su anotación `/* literal: */` en la propia declaración: la válvula de recipe-contract §3, la misma que honra `component-audit`. **Una desviación firmada no es deuda** — se listan para que la razón se lea, no para acuñarlas. _Ninguno._ ## 2. Sistema transversal (4) — 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 | `float-panel.css:121` | `[data-float-panel-header]:focus-visible` | `box-shadow` | `inset 0 0 0 var(--ring-inset-width) var(--ring-inset-color, currentColor)` | | 2 | `float-panel.css:125` | `[data-float-panel-header][data-grabbed]` | `box-shadow` | `inset 0 0 0 var(--ring-inset-width) var(--ring-inset-color, currentColor)` | | 3 | `float-panel.css:239` | `[data-float-panel-resize-grip]` | `opacity` | `var(--opacity-muted)` | | 4 | `float-panel.css:261` | `[data-float-panel-resize-grip]:focus-visible` | `box-shadow` | `inset 0 0 0 var(--ring-inset-width) var(--ring-inset-color, currentColor)` | ## 3. Privados de la receta — ¿de dónde sale su valor? | privado | declaraciones | valor(es) | origen | ¿deriva de un público? | | --- | ---: | --- | --- | :-: | | `--_float-panel-accent` | 2 | `var(--float-panel-accent)`, `var(--_float-panel-palette-border)` | private, public | no | Consumidos y **no declarados en el CSS** (vienen de `base.ts` o de un estilo inline del wrapper): `--_float-panel-palette-border`. ## 4. Propuesta de corrección ### 4.1 Tokens a declarar en `lib/recipes/base.ts` (3) Valor **verbatim** del CSS de hoy: el default no se mueve, sólo cambia quién puede moverlo. Nombres derivados de recipe-contract §1 (ejes lógicos, talla al final) y theming §6.7 (slots de color, modificador delante). Un token con DOS valores distintos es una colisión de nombre: son dos knobs, o el nombre no distingue lo que debería — se marca `⚠`. | token (`--float-panel-…`) | scope TSC | valor propuesto | usos | | --- | --- | --- | ---: | | `action-shadow` | `root` | ⚠ `var(--float-panel-accent)` / `var(--_float-panel-palette-border)` | 2 | | `resize-grip-bg-image` | `root` | ⚠ `repeating-linear-gradient( -45deg, transparent 0 3px, currentColor 3px 4.5px )` / `repeating-linear-gradient(45deg, transparent 0 3px, currentColor 3px 4.5px)` | 2 | | `hover-resize-grip-fg` | `root` | ⚠ `var(--float-panel-accent)` / `var(--_float-panel-palette-border)` | 2 | ### 4.2 Sin nombre mecánico (4) - **⚠ decisión: `border-block-end` es shorthand o eje físico — hay que partirlo en ejes lógicos antes de nombrarlo (recipe-contract §1, R-4.4)** — 1: `border-block-end`. - **⚠ 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** — 3: `line-height`, `opacity`. ### 4.3 Avisos sobre los tokens propuestos (1) - **el privado `--_float-panel-accent` debe pasar a leer este público (o desaparecer)** — `--float-panel-action-shadow`, `--float-panel-hover-resize-grip-fg` ### 4.4 Lo que hay que comprobar a mano (PLAN-theming §1.3 · §7.4) - [ ] **Privado que no deriva de un público** — §3 lo marca; el privado debe leer el público o desaparecer. - [ ] **Velo o acento en el nodo equivocado** (`archetype: 'item'` en un envoltorio, un `background` en shorthand que mata la capa de estado) — se mide desde el píxel hacia arriba. - [ ] **Doble animación** al mover un sello a una superficie con animación propia — registro de `animationstart`/`animationend`. - [ ] **Diff de computed = 0** en reposo · hover · abierto · disabled · foco, por talla, antes y después. - [ ] **Centinela por token nuevo**: valor imposible en el root → el nodo lo sigue. Si no, el token miente. ## 5. Veredicto **EJECUTADO 2026-08-22 — 59 % → 84 %**, 12 claves. `global` a CERO. Lo cosido: las dos VARIANTES que ajustan desde el default elevado (`outline-*` / `ghost-*`) y los botones de su barra (`action-*` + `hover-action-*`), más el asa de redimensionado. **Y de paso muere uno de los nueve fantasmas del barrido**: el asa leía `var(--color-content-tertiary, var(--color-content-secondary))` — ese primitivo NO EXISTE, así que pintaba el respaldo. El contrato guarda lo que de verdad pintaba (`--color-content-secondary`), con lo que la referencia muerta desaparece sin mover un píxel. **Once adjudicaciones medidas**: la demo monta la variante por DEFECTO (no outline ni ghost) y no tiene botón de acción genérico —sólo el control de cierre—, así que se forzaron el atributo de variante y se montó el botón donde el CSS lo selecciona; el radio del asa sólo pinta bajo `:focus-visible`. Las once alcanzan. **La ANOMALÍA que registré aquí quedó RESUELTA el mismo día**, en la revisión adversarial del bloque: `resize-grip-fg` movía aislado y leía muerto en una corrida completa porque **la pasada de hover deja el puntero sobre el último nodo que tocó**, y el asa re-apunta su color al acento bajo `:hover`. Con el puntero aparcado tras esa pasada, el token vive y el guard delató su propia excepción como STALE. **Lo rojo que queda es ANTERIOR**: 12 tokens del bloque viejo, y seis de ellos —`bg`, `border`, `border-width`, `shadow`, `font-family`, `line-height`— son el mismo conjunto que en `popover`: el PLANO de profundidad gana sobre el panel (§12.9, pendiente de firma). Sonda antes/después: **0 diffs en 1.890 valores** (9 nodos × 7 estados). --- **ADJUDICADO 2026-08-23 — los DOCE, uno a uno.** Quedaron escritos como «rojo que queda» el 22 y nadie los midió: el guard R-5.4 **fallaba** en este componente y el handoff daba por cerrado el bloque viejo. Ninguna regresión —la bisección de las nueve versiones del guard desde `cd6a9f4e3` da siempre 17/40 con estos mismos doce—, sólo un rojo sin medir. Ahora el guard pasa: **17/40 con 23 adjudicaciones, cero STALE**. **SEIS son el plano, y la predicción del 22 era exacta.** El contenido lleva `data-depth='overlay'`, y `[data-depth='overlay']` declara `background`, `border`, `box-shadow`, `font-family` y `line-height` a la MISMA especificidad (0,1,0) y más tarde en la cascada. Verificado moviendo los tokens del propio plano sobre este nodo (superficie, borde, fuente y leading lo repintan) y enumerando las reglas que casan: la del plano está ahí, después de la de la receta. `border-width` cae con `border` porque el plano escribe el shorthand. Misma adjudicación que `tooltip`, misma decisión abierta. ⚠ **Y una corrección a mi propio diagnóstico**: primero atribuí la sombra a `[data-topmost]` (el panel de la demo arranca frontal). Falso: quitando el atributo, `--float-panel-shadow` sigue muerto. Es el plano también. Lo que sí depende de `topmost` es su compañero **`shadow-topmost`, que SÍ alcanza** (→ `rgb(4,5,6)`) porque su regla es (0,2,0) y le gana al plano — la prueba de que la cascada es la explicación y no un token roto. **Tres son estados o partes que la demo no monta**: `active-shadow` (forzado `data-dragging` → alcanza), `resize-size` (lo consumen las asas de ESQUINA `se/ne/sw/nw` y la demo monta sólo las de borde, `e` y `s`) y `button-size` (lo consumen `[data-float-panel-action]` y `[data-float-panel-stage-trigger]`, ninguno montado — el único control es el cierre, que es un `