From 4d3ca2fd81dde2a2de87b113f45c04f1e667103b Mon Sep 17 00:00:00 2001 From: dev Date: Mon, 24 Aug 2026 04:10:37 +0200 Subject: [PATCH] =?UTF-8?q?uix(slider):=20temable=20=E2=80=94=2097=20%=20?= =?UTF-8?q?=E2=86=92=20100=20%,=20y=20con=20un=20solo=20valor=20el=20tick?= =?UTF-8?q?=20ACTIVO=20no=20existe?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CERO claves acuñadas. El componente llegaba con `global` a cero, `private` a cero —sus cuatro privados son la escala por talla, que ya deriva de públicos— y treinta y nueve claves cubriendo su cromo entero; lo único fuera de alcance era UN literal. Este commit lo firma, adjudica sus siete silenciosas y escribe los artefactos que le faltaban. QUÉ ENTRA - **El literal se firma como identidad**: `inline-size: 100%` en `[data-slider][data-orientation='horizontal']` — un rail ocupa su contenedor. El knob de verdad ya existe y es su SUELO: `min-inline-size`. Mismo precedente que `separator.css:26`, cerrado el mismo día también sin acuñar. - **Siete adjudicaciones en el ledger**, todas MEDIDAS sobre los nodos reales, con `transition` congelada salvo donde el token ES la transición: - `min-block-size` — sólo la lee `[data-orientation='vertical']` y el escenario arranca horizontal. Con el chip de orientación: 160px -> 1234px. - `active-thumb-scale` / `active-thumb-shadow` — el pickup vive en `[data-slider-thumb]:active`, que pide el puntero MANTENIDO, y el pase estático del guard lo aparca. Con `mouse.down()` real sobre el thumb real: scale 1.15 -> 4.56, y la sombra sigue al centinela. - `active-tick-bg` — un tick lleva `data-active` sólo mientras su valor cae DENTRO de [min(valores), max(valores)] (soma, `SliderTickProvider.isActive`), así que un pulgar único en 40 con ticks en 0/50/100 deja los tres inactivos: la marca activa no es un estado del tick, es una RELACIÓN CON EL RANGO. Con el chip `multiple` (20–80) el tick 50 se activa y el token alcanza. - `disabled-opacity` — el escenario arranca habilitado; con su interruptor, 0.4 -> 0.123. - `transition-duration` / `transition-ease` — SON la transición que el guard congela; medidos sin congelar, 0.12s -> 11.5s y steps(4). - README con su sección «Talla y tema» (39 claves por grupo, con la distinción `hit-size-{k}` = agarre vs `track-size-{k}` = pintura) y veredicto §5 de la ficha con las cifras. QUÉ NO SE TOCA - **Nada se retira ni se renombra**, y no es prudencia: este contrato lo COMPONEN otros SIETE componentes que re-tiñen o re-dimensionan estas mismas claves sobre su Slider embebido — `media-player`, `skin-media-player`, `waveform` (su playhead ES este thumb), `color-picker`, `gradient-builder`, `image-adjustments` y `time-range-picker`. `min-block-size`, que el centinela leía muerta, es justo la que `media-player` escribe para su volumen vertical. - `base.ts` y `generated/base.css`: sin cambio de contrato no hay nada que regenerar. 39 -> 39 claves. ARTEFACTOS - Censo: 97 % (33 knobs · 31 públicos · 1 literal · 1 sistema) -> **100 %** (32 · 31 · 0 literal · 1 excepción · 1 sistema). Global 68 %, no baja. - Sonda antes/después: **0 diffs sobre 1.376 valores computados**, 7 estados, 7 nodos (raíz · secondary-range · range · thumb · 3 ticks). - Captura 2× antes y después: idénticas. - Centinela R-5.4: **32/39**, las 7 restantes adjudicadas -> verde. - component-audit PASS · eidos-lint 25 morfo-backed / 5 eidos-only / **0 invalid** · `vitest run src/uix/eidos` 35/36 ficheros (el rojo conocido es `skin-media-player`) · rtl:check 0 · docs:check 0. - `npm run check`: UN error en `web/routes/uix/components/slider/+page.svelte` (`Property 'keyboard' does not exist`, línea del `sliderMorfo.parts[2]`), **PREEXISTENTE** desde `1857c7854` y ajeno a este commit; ningún otro error en ficheros de slider. - Prettier: los hunks de deriva del fichero son los MISMOS que en HEAD (2 en el README, 0 en el CSS); mi bloque no añade ninguno. NOTA DE CONCURRENCIA: la pestaña `Tokens` de su demo (paso 11 del protocolo) la escribí yo, pero entró en HEAD por `9fc38b19a` + `c88b2f861` de otra sesión, que la arrastró con un `git add` derivado y lo declaró. No se re-commitea aquí. Co-Authored-By: Claude Opus 5 --- docs/audit/theming/slider.md | 69 +++++++++++++++++++--- scripts/theming-sentinel-exceptions.ts | 20 +++++++ src/uix/eidos/components/slider/README.md | 45 ++++++++++++++ src/uix/eidos/components/slider/slider.css | 2 +- 4 files changed, 126 insertions(+), 10 deletions(-) diff --git a/docs/audit/theming/slider.md b/docs/audit/theming/slider.md index 7fc1dd1a0..4f1af240a 100644 --- a/docs/audit/theming/slider.md +++ b/docs/audit/theming/slider.md @@ -5,8 +5,8 @@ > 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**: **97%** — 31 de 32 knobs por token público -- **Knobs de apariencia**: 33 — público 31 · privado 0 · global 0 · literal 1 · sistema 1 · excepción 0 _(los dos últimos, fuera del ratio)_ +- **Medido**: 2026-08-24 · **Alcance**: **100%** — 31 de 31 knobs por token público +- **Knobs de apariencia**: 32 — público 31 · privado 0 · global 0 · literal 0 · sistema 1 · excepción 1 _(los dos últimos, fuera del ratio)_ - **Contrato hoy** (`lib/recipes/base.ts`): 39 pública(s) — `track-size-xs`, `track-size-sm`, `track-size-md`, `track-size-lg`, `track-size-xl`, `thumb-size-xs`, `thumb-size-sm`, `thumb-size-md`, `thumb-size-lg`, `thumb-size-xl`, `hit-size-xs`, `hit-size-sm`, `hit-size-md`, `hit-size-lg`, `hit-size-xl`, `tick-size-xs`, `tick-size-sm`, `tick-size-md`, `tick-size-lg`, `tick-size-xl`, `min-inline-size`, `min-block-size`, `track-radius`, `track-bg`, `range-bg`, `secondary-bg`, `thumb-radius`, `thumb-border-width`, `thumb-border`, `thumb-bg`, `thumb-shadow`, `active-thumb-scale`, `active-thumb-shadow`, `tick-radius`, `tick-bg`, `active-tick-bg`, `transition-duration`, `transition-ease`, `disabled-opacity` - **Eje `size`**: sí · **ficheros**: `slider.css` @@ -20,20 +20,20 @@ _Ninguno._ _Ninguno._ -### 1.3 Literales (1) +### 1.3 Literales (0) -| # | fichero:línea | selector | propiedad | valor | -| ---: | --- | --- | --- | --- | -| 1 | `slider.css:70` | `[data-slider][data-orientation='horizontal']` | `inline-size` | `100%` | +_Ninguno._ -### 1.4 Excepciones firmadas (0) — fuera del ratio +### 1.4 Excepciones firmadas (1) — 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._ +| # | fichero:línea | selector | propiedad | valor | +| ---: | --- | --- | --- | --- | +| 1 | `slider.css:70` | `[data-slider][data-orientation='horizontal']` | `inline-size` | `100%` | ## 2. Sistema transversal (1) — informativo, fuera del ratio Un tema los alcanza **a nivel de sistema**, por diseño (recipe-contract §2). @@ -82,6 +82,57 @@ no distingue lo que debería — se marca `⚠`. -_(pendiente — lo escribe el autor; se conserva al regenerar)_ +**Medido y CERRADO al 100 % el 2026-08-24** (97 % → 100 %, contrato **39 → 39** +claves —ninguna acuñada—, centinela **32/39** con 7 adjudicadas, diff de computed +**0 sobre 1.376 valores** en 7 estados, 7 nodos). + +**No entra una sola clave.** El componente llegaba con `global` a cero, +`private` a cero —sus cuatro privados son la escala resuelta por talla, que ya +deriva de públicos— y treinta y nueve claves cubriendo su cromo entero. Lo único +fuera de alcance era **un literal**: el `inline-size: 100%` del rail horizontal. +Es identidad —un rail ocupa su contenedor— y va firmado; el knob de verdad +existe y se llama `min-inline-size`, que es su SUELO. Precedente literal: +`separator.css:26`, cerrado el mismo día con la misma frase y también sin acuñar +nada. + +**Las siete silenciosas del centinela están VIVAS.** Ninguna miente: el +escenario sólo lleva puesta una de sus caras. Medidas una a una sobre los nodos +reales, con `transition` congelada salvo donde el token ES la transición: + +- **`min-block-size`** — sólo `[data-slider][data-orientation='vertical']` la + lee. Con el chip de orientación de la demo: `160px → 1234px` en `block-size` y + `min-block-size`. +- **`active-thumb-scale` / `active-thumb-shadow`** — el pickup vive en + `[data-slider-thumb]:active` y pide el puntero MANTENIDO; el pase estático del + guard lo aparca. Con un `mouse.down()` real sobre el thumb real: + `scale 1.15 → 4.56` y la sombra sigue al centinela. +- **`active-tick-bg`** — un tick lleva `data-active` sólo mientras su valor cae + DENTRO de `[min(valores), max(valores)]` (soma, `SliderTickProvider.isActive`), + así que un pulgar único en 40 con ticks en 0/50/100 deja los tres inactivos: + **la marca activa no es un estado del tick, es una relación con el rango**. Con + el chip `multiple` (20–80) el tick 50 se activa y el token alcanza. +- **`disabled-opacity`** — el escenario arranca habilitado; con su propio + interruptor, `opacity 0.4 → 0.123`. +- **`transition-duration` / `transition-ease`** — SON la transición que el guard + congela para poder medir lo demás. Sin congelar: `0.12s → 11.5s` en las cuatro + propiedades transicionadas del thumb, y `steps(4)`. + +**`hit-size-{k}` y `track-size-{k}` son dos knobs a propósito**, y conviene que +quede escrito antes de que alguien los funda: el primero es el área de agarre +(la altura del control), el segundo el grosor de la pintura sobre el `::before` +de la raíz. Un rail de 6 px con 32 px de agarre es el objetivo táctil, no un +descuadre. + +**Este contrato lo COMPONEN otros siete componentes** —`media-player`, +`skin-media-player`, `waveform` (su playhead ES este thumb), `color-picker`, +`gradient-builder`, `image-adjustments` y `time-range-picker`— que re-tiñen o +re-dimensionan estas mismas claves sobre su Slider embebido. `min-block-size`, +que el centinela leía muerta, es exactamente la que `media-player` escribe para +su volumen vertical. **Ninguna clave de este bloque se retira ni se renombra sin +barrer esos siete.** + +**Lo que este componente enseña**: cuando el escenario lleva un solo valor, el +estado «activo» de un indicador de rango **no existe**, y el guard lo lee como +un token que miente. diff --git a/scripts/theming-sentinel-exceptions.ts b/scripts/theming-sentinel-exceptions.ts index 4555fe6b6..6e28ce845 100644 --- a/scripts/theming-sentinel-exceptions.ts +++ b/scripts/theming-sentinel-exceptions.ts @@ -21,6 +21,26 @@ const PLANE_SUPERSEDED = 'SUPERSEDED by the depth plane: the part carries data-depth=overlay, and [data-depth=overlay] declares this property at the same specificity (0,1,0) and LATER in the cascade, so the recipe key is bypassed (see next-features 12.9, measured on tooltip, popover and float-panel)'; export const SENTINEL_EXCEPTIONS: Record> = { + slider: { + // 2026-08-24. Seven keys, seven measured reasons, zero retirements: an + // orientation, a pointer held DOWN, two demo states and the transition + // pair the guard freezes. Every one reaches once its state exists — the + // contract does not lie, the stage only wears one of its faces. + 'min-block-size': + "only [data-slider][data-orientation='vertical'] reads it, and the stage boots horizontal. Measured 2026-08-24 through the orientation chip of the demo: vertical -> reaches (block-size and min-block-size 160px -> 1234px)", + 'active-thumb-scale': + 'the pickup lift lives in `[data-slider-thumb]:active`, which needs the pointer HELD DOWN, and the static pass of the guard parks the pointer. Measured with a real mouse.down() on the real thumb, transitions frozen -> reaches (scale 1.15 -> 4.56)', + 'active-thumb-shadow': + 'same :active pickup, measured the same way -> reaches (rgba(15, 23, 42, 0.12) 0px 8px 24px -> rgb(1, 2, 3) 0px 0px 0px 7px)', + 'active-tick-bg': + 'a tick carries data-active only while its value sits INSIDE [min(values), max(values)] (soma SliderTickProvider.isActive), so one thumb at 40 with ticks at 0/50/100 leaves the three inactive — the token is not dead, its state is unreachable with a single value. Measured with the `multiple` chip of the demo (values 20-80, tick 50 goes active) -> reaches (oklch(0.5556 0.1829 305.86) -> rgb(1, 2, 3))', + 'disabled-opacity': + '`[data-slider][data-disabled]`, and the stage boots enabled. Measured with the disabled toggle of the demo -> reaches (opacity 0.4 -> 0.123)', + 'transition-duration': + 'the guard freezes transitions and this token IS the transition; measured unfrozen -> reaches (0.12s -> 11.5s on the four transitioned properties of the thumb)', + 'transition-ease': + 'same freeze; measured unfrozen -> reaches (cubic-bezier(0.4, 0, 0.2, 1) -> steps(4))' + }, 'scroll-area': { // 2026-08-24. Three keys, three measured reasons, zero retirements: one // MODE the demo has no control for, and the transition pair the guard diff --git a/src/uix/eidos/components/slider/README.md b/src/uix/eidos/components/slider/README.md index 9a562a94f..7ab1fd228 100644 --- a/src/uix/eidos/components/slider/README.md +++ b/src/uix/eidos/components/slider/README.md @@ -7,6 +7,51 @@ Fecha de revision: 2026-05-17. Air no tenia `slider` en `glm-5:src/uix/air/components`; solo habia iconos de sliders. La referencia local es Soma/Morfo. +## Talla y tema + +**39 claves públicas** en `lib/recipes/base.ts` (bloque `slider`), con el eje +`size` en cinco pasos (`xs`…`xl`). La receta **no consume la clave por talla +directamente**: cada eje dimensional entra por un privado resuelto +(`--_slider-{track,thumb,hit,tick}-size`) que los bloques `[data-slider][data-size]` +apuntan al paso en vigor — el patrón canónico de escala, y la razón de que sólo +UNA de las cinco pinte a la vez. La pestaña **Tokens** de su demo las resuelve +en vivo. + +| Grupo | Claves | +| ---------- | ----------------------------------------------------------------------------------------------------------------------------------- | +| escalas | `track-size-{xs…xl}` · `thumb-size-{xs…xl}` · `hit-size-{xs…xl}` · `tick-size-{xs…xl}` | +| caja | `min-inline-size` · `min-block-size` · `disabled-opacity` | +| rail | `track-radius` · `track-bg` · `range-bg` · `secondary-bg` | +| thumb | `thumb-radius` · `thumb-border-width` · `thumb-border` · `thumb-bg` · `thumb-shadow` · `active-thumb-scale` · `active-thumb-shadow` | +| tick | `tick-radius` · `tick-bg` · `active-tick-bg` | +| transición | `transition-duration` · `transition-ease` | + +`hit-size-{k}` es el ÁREA DE AGARRE (la altura del control), no el grosor de la +pintura: el rail se pinta con `track-size-{k}` sobre el `::before` de la raíz, +centrado dentro del `hit`. Son dos knobs a propósito — un rail de 6 px con 32 px +de agarre es el objetivo táctil, no un descuadre. + +**Este contrato lo COMPONEN otros siete componentes**, que re-tiñen o +re-dimensionan estas mismas claves sobre su propio Slider embebido: +`media-player` y `skin-media-player` (barra de tiempo y volumen, incluida +`min-block-size` para el volumen vertical), `waveform` (su playhead ES este +thumb), `color-picker` y `gradient-builder` (los raíles de canal), +`image-adjustments` (el rail de tono) y `time-range-picker`. Ninguna clave se +retira ni se renombra sin barrer esos siete. + +**Lo que el guard R-5.4 da por silencioso** (7 de 39, todas medidas y vivas, +ninguna retirada): `min-block-size` (sólo la orientación vertical la lee), +`active-thumb-scale` / `active-thumb-shadow` (piden el puntero MANTENIDO, y el +pase estático del guard lo aparca), `active-tick-bg` (un tick sólo se activa +dentro de `[min(valores), max(valores)]`, así que con un solo pulgar no hay tick +activo), `disabled-opacity` (el escenario arranca habilitado) y el par +`transition-duration` / `transition-ease`, que ES la transición que el guard +congela para poder medir lo demás. + +**El único literal es identidad y va firmado**: el `inline-size: 100%` del rail +horizontal — un rail ocupa su contenedor; su suelo sí es un knob, y se llama +`min-inline-size`. + ## Comparativa | Referencia | Superficie relevante | Estado Eidos | diff --git a/src/uix/eidos/components/slider/slider.css b/src/uix/eidos/components/slider/slider.css index cc2ffd6cd..39d53459c 100644 --- a/src/uix/eidos/components/slider/slider.css +++ b/src/uix/eidos/components/slider/slider.css @@ -67,7 +67,7 @@ } [data-slider][data-orientation='horizontal'] { - inline-size: 100%; + inline-size: 100%; /* literal: a rail spans its container — identity; its floor is `min-inline-size` */ block-size: var(--_slider-hit-size); min-inline-size: var(--slider-min-inline-size); }