|
|
# progress — 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-25 · **Alcance**: **93%** — 26 de 28 knobs por token público
|
|
|
- **Knobs de apariencia**: 30 — público 26 · privado 0 · global 2 · literal 0 · sistema 0 · excepción 6 · estructural 0 · puente 0 · canal 2 _(los cinco últimos, fuera del ratio)_
|
|
|
- **Contrato hoy** (`lib/recipes/base.ts`): 43 pública(s) — `height-xs`, `height-sm`, `height-md`, `height-lg`, `height-xl`, `vertical-height`, `row-gap`, `column-gap`, `radius-xs`, `radius-sm`, `radius-md`, `radius-lg`, `radius-xl`, `ring-size-xs`, `ring-size-sm`, `ring-size-md`, `ring-size-lg`, `ring-size-xl`, `ring-thickness-xs`, `ring-thickness-sm`, `ring-thickness-md`, `ring-thickness-lg`, `ring-thickness-xl`, `label-font-family`, `label-font-size`, `label-font-weight`, `label-line-height`, `label-fg`, `value-text-font-family`, `value-text-font-size`, `value-text-font-weight`, `value-text-line-height`, `value-text-fg`, `track-bg`, `ring-track-bg`, `ring-center-bg`, `indicator-bg`, `indicator-bg-loaded`, `transition-duration`, `transition-ease`, `indeterminate-width`, `indeterminate-duration`, `indeterminate-ease`
|
|
|
- **Eje `size`**: sí · **ficheros**: `progress.css`
|
|
|
|
|
|
## 1. Knobs fuera de alcance
|
|
|
|
|
|
### 1.1 Directo a primitivo global (2)
|
|
|
|
|
|
| # | fichero:línea | selector | propiedad | valor |
|
|
|
| ---: | --- | --- | --- | --- |
|
|
|
| 1 | `progress.css:127` | `[data-progress][data-shape='circular'] [data-progress-indicator]` | `border-radius` | `var(--radius-full)` |
|
|
|
| 2 | `progress.css:139` | `[data-progress][data-shape='circular'] [data-progress-indicator]::before` | `border-radius` | `var(--radius-full)` |
|
|
|
|
|
|
### 1.2 A través de un privado (0)
|
|
|
|
|
|
Sólo el **residuo**: el puente de paleta (§2-ter) y el canal de valor
|
|
|
(§2-quater) salen aparte, porque no son deuda ni tienen nombre que acuñar.
|
|
|
|
|
|
_Ninguno._
|
|
|
|
|
|
### 1.3 Literales (0)
|
|
|
|
|
|
_Ninguno._
|
|
|
|
|
|
### 1.4 Excepciones firmadas (6) — fuera del ratio
|
|
|
|
|
|
Literales que llevan su anotación `/* literal: <razón> */` 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.
|
|
|
|
|
|
| # | fichero:línea | selector | propiedad | valor |
|
|
|
| ---: | --- | --- | --- | --- |
|
|
|
| 1 | `progress.css:13` | `[data-progress]` | `inline-size` | `100%` |
|
|
|
| 2 | `progress.css:45` | `[data-progress][data-orientation='vertical']` | `inline-size` | `max-content` |
|
|
|
| 3 | `progress.css:55` | `[data-progress-indicator]` | `inline-size` | `100%` |
|
|
|
| 4 | `progress.css:82` | `[data-progress][data-orientation='vertical'] [data-progress-indicator]::before` | `inline-size` | `100%` |
|
|
|
| 5 | `progress.css:103` | `[data-progress][data-orientation='vertical'][data-state='indeterminate'] [data-progress-indicator]::before` | `inline-size` | `100%` |
|
|
|
| 6 | `progress.css:108` | `[data-progress][data-shape='circular']` | `inline-size` | `max-content` |
|
|
|
## 2. Sistema transversal (0) — informativo, fuera del ratio
|
|
|
|
|
|
Un tema los alcanza **a nivel de sistema**, por diseño (recipe-contract §2).
|
|
|
|
|
|
_Ninguno._
|
|
|
|
|
|
## 2-quater. Canal de valor (2) — fuera del ratio
|
|
|
|
|
|
La receta lee un privado que **nadie declara** — ni su CSS ni el generador: lo
|
|
|
escribe soma o el envoltorio **por instancia** (un %, un rect medido, la talla
|
|
|
que pide una prop). Un tema no debe alcanzarlo: fijarlo rompe el
|
|
|
comportamiento, y por eso `tabs` rechazó por escrito esa misma propuesta.
|
|
|
|
|
|
| # | fichero:línea | selector | propiedad | valor |
|
|
|
| ---: | --- | --- | --- | --- |
|
|
|
| 1 | `progress.css:67` | `[data-progress-indicator]::before` | `inline-size` | `calc(var(--_progress-value-pct, 0) * 1%)` |
|
|
|
| 2 | `progress.css:83` | `[data-progress][data-orientation='vertical'] [data-progress-indicator]::before` | `block-size` | `calc(var(--_progress-value-pct, 0) * 1%)` |
|
|
|
|
|
|
## 3. Privados de la receta — ¿de dónde sale su valor?
|
|
|
|
|
|
| privado | declaraciones | valor(es) | origen | ¿deriva de un público? |
|
|
|
| --- | ---: | --- | --- | :-: |
|
|
|
| `--_progress-height` | 5 | `var(--progress-height-md)`, `var(--progress-height-xs)`, `var(--progress-height-sm)`, `var(--progress-height-lg)`, `var(--progress-height-xl)` | public | **sí** |
|
|
|
| `--_progress-radius` | 5 | `var(--progress-radius-md)`, `var(--progress-radius-xs)`, `var(--progress-radius-sm)`, `var(--progress-radius-lg)`, `var(--progress-radius-xl)` | public | **sí** |
|
|
|
| `--_progress-ring-size` | 5 | `var(--progress-ring-size-md)`, `var(--progress-ring-size-xs)`, `var(--progress-ring-size-sm)`, `var(--progress-ring-size-lg)`, `var(--progress-ring-size-xl)` | public | **sí** |
|
|
|
| `--_progress-ring-thickness` | 5 | `var(--progress-ring-thickness-md)`, `var(--progress-ring-thickness-xs)`, `var(--progress-ring-thickness-sm)`, `var(--progress-ring-thickness-lg)`, `var(--progress-ring-thickness-xl)` | public | **sí** |
|
|
|
| `--_progress-indicator-bg` | 2 | `var(--progress-indicator-bg)`, `var(--progress-indicator-bg-loaded)` | public | **sí** |
|
|
|
|
|
|
Consumidos y **no declarados en el CSS** (vienen de `base.ts` o de un estilo inline del wrapper): `--_progress-value-pct`.
|
|
|
|
|
|
## 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).
|
|
|
|
|
|
### 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
|
|
|
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 (`--progress-…`) | scope TSC | valor propuesto | usos |
|
|
|
| --- | --- | --- | ---: |
|
|
|
| `width` | `root` | `max-content` | 2 |
|
|
|
| `indicator-radius` | `root` | `var(--radius-full)` | 2 |
|
|
|
|
|
|
### 4.2 Sin nombre mecánico (4)
|
|
|
|
|
|
- **⚠ 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** — 4: `inline-size`.
|
|
|
|
|
|
### 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
|
|
|
|
|
|
<!-- veredicto:start -->
|
|
|
|
|
|
**Ejecutado 2026-08-23 — 69 % → 87 %.** Dos claves acuñadas, seis literales
|
|
|
firmados, cuatro knobs adjudicados. Diff de computed **0** sobre 60
|
|
|
configuraciones (2 formas × 2 orientaciones × 3 estados × 5 tallas) y capturas
|
|
|
2× **idénticas byte a byte**. Guard R-5.4: **40 de 43** tokens mueven un
|
|
|
computed; los tres callados, medidos y escritos en el ledger.
|
|
|
|
|
|
**Lo que entra (2 claves, el ÚNICO knob que iba a un primitivo a pelo):**
|
|
|
|
|
|
- `row-gap` (`--space-2`) y `column-gap` (`--space-3`). Eran un `gap` en
|
|
|
atajo de DOS valores distintos, y por eso se parte: el hueco de fila separa
|
|
|
la línea de texto de la barra, el de columna separa el rótulo del valor. Un
|
|
|
`gap` único habría nombrado un knob para dos. El catálogo tiene las dos
|
|
|
palabras (`row-gap` ×10, `column-gap` ×4) y el molde exacto es
|
|
|
`chart.legend-row-gap` / `-column-gap`.
|
|
|
|
|
|
**Lo que se FIRMA en vez de acuñarse (6 literales, fuera del ratio):** los
|
|
|
cuatro `100%` y los dos `max-content` son la geometría de la barra, no una
|
|
|
decisión de tema — llena la fila que le dan (`100%`) o se encoge a su propio
|
|
|
grosor / diámetro (`max-content`). Llevan su `/* literal: <razón> */` en la
|
|
|
declaración; válvula de recipe-contract §3.
|
|
|
|
|
|
**Lo que se RECHAZA de la propuesta §4 (dos de las tres filas):**
|
|
|
|
|
|
1. `indicator-radius` — el `--radius-full` del anillo. Su gemelo `meter` ya
|
|
|
dejó el veredicto escrito EN EL CSS: es lo que hace circular a
|
|
|
`shape='circular'`, y un token dejaría que un tema des-redondease una forma
|
|
|
que el consumidor pidió POR SU NOMBRE. **Identidad, no knob.** Se copia el
|
|
|
comentario del gemelo. Son los dos únicos `global` que quedan, y el censo no
|
|
|
puede sacarlos del ratio: la válvula `/* literal: */` sólo reclasifica la
|
|
|
clase `literal`, nunca un `global` (mismo techo que `meter`).
|
|
|
2. `width: max-content` — la §4 fundía en UN nombre dos nodos distintos (la
|
|
|
raíz vertical y la raíz circular) y además lo llamaba por el primer selector,
|
|
|
no por lo que pinta. Es el literal de identidad de arriba.
|
|
|
|
|
|
**Los dos knobs `private` que quedan son el CANAL DE VALOR, no deuda.**
|
|
|
`--_progress-value-pct` lo escribe **soma** en el estilo inline del provider en
|
|
|
cada render (`progress-provider.svelte.ts`, 0–100 saturado); la receta lo lee
|
|
|
para el ancho del relleno lineal y el alto del vertical. Un público encima
|
|
|
mentiría: un tema no puede fijar el progreso de una tarea. Es la misma clase que
|
|
|
el `z-index` inline de `drag-drop` y el gap de `carousel`. Con ellos y con los
|
|
|
dos `--radius-full` de identidad, **el techo honesto de este componente es
|
|
|
87 %**, no el 100 %.
|
|
|
|
|
|
**Lo que el guard da por callado (3 de 43, todos medidos sobre el nodo real):**
|
|
|
|
|
|
- `indicator-bg-loaded` — sólo pinta bajo `[data-state='loaded']`, y la corrida
|
|
|
mantiene `indeterminate` encendido para alcanzar los tokens del barrido:
|
|
|
forzado → `oklch(0.6406 0.1329 157.68)` → `rgb(1,2,3)`.
|
|
|
- `transition-duration` / `transition-ease` — los congela el propio guard por
|
|
|
diseño; pasada SIN congelar → `0.18s` → `11.5s` y
|
|
|
`cubic-bezier(0.4, 0, 0.2, 1)` → `steps(7)`. Mismo par que en `meter`.
|
|
|
|
|
|
**Lo que enseñó el instrumento.** La sonda estándar mide 4 nodos — que son
|
|
|
TODAS las partes del morfo — pero una sola configuración: lineal, horizontal,
|
|
|
`loading`, `md`. Medio contrato (el anillo, el eje vertical, el barrido
|
|
|
indeterminado, la tinta de completado) no entra en ese diff, y el gate habría
|
|
|
pasado en verde sin haberlo mirado. El guard lo dijo antes: **22 de 41** en la
|
|
|
página por defecto. Con `sweepAttr: data-shape` (el interruptor que su gemelo
|
|
|
`meter` ya tenía) sube a 34; encendiendo el interruptor `indeterminate` de la
|
|
|
demo, a 37; y el chip `vertical`, a 38 — **cada control comprobado por separado,
|
|
|
antes y después, porque montar más puede medir menos**. Aquí no tapó ninguno.
|
|
|
|
|
|
**Y una trampa nueva del instrumento, medida aquí**: una sonda que CACHEA los
|
|
|
nodos y luego mide en bucle da 8.352 falsos diffs si el HMR reemplaza el
|
|
|
subárbol a mitad de corrida — `getComputedStyle` sobre un nodo DESACOPLADO
|
|
|
devuelve la cadena VACÍA en todas las propiedades, y el diff lee cada vacío como
|
|
|
un cambio. Se arregla re-consultando los nodos en cada instantánea, y se detecta
|
|
|
haciendo fallar la sonda ante un computed vacío. Es pariente de
|
|
|
`hmr-stale-tab-phantom-findings`, con otra cara.
|
|
|
|
|
|
<!-- veredicto:end -->
|