You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/docs/audit/theming/natural-time-picker.md

210 lines
15 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# natural-time-picker — 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-23 · **Alcance**: **74%** — 56 de 76 knobs por token público
- **Knobs de apariencia**: 77 — público 56 · privado 0 · global 14 · literal 6 · sistema 1 · excepción 0 _(los dos últimos, fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 62 pública(s) — `band-height-sm`, `band-height-md`, `band-height-lg`, `knob-size-sm`, `knob-size-md`, `knob-size-lg`, `band-height`, `knob-size`, `panel-gap-sm`, `panel-gap-md`, `panel-gap-lg`, `panel-gap`, `band-radius`, `band-border`, `band-border-width`, `line-width`, `line-radius`, `knob-radius`, `knob-shadow`, `knob-focus-ring`, `knob-focus-ring-offset`, `ticks-inset-inline`, `ticks-inset-block-end`, `tick-font-size`, `tick-font-family`, `display-gap`, `time-font-family`, `time-font-size`, `time-font-weight`, `time-letter-spacing`, `meridiem-margin-inline-start`, `meridiem-font-size`, `meridiem-font-weight`, `meridiem-fg`, `period-gap`, `period-font-size`, `period-font-weight`, `period-fg`, `steppers-gap`, `stepper-group-gap`, `stepper-label-font-size`, `stepper-label-font-weight`, `stepper-label-fg`, `stepper-label-letter-spacing`, `periods-gap`, `periods-margin-block`, `chip-font-size`, `trigger-gap`, `trigger-min-width`, `trigger-padding-block`, `trigger-padding-inline`, `trigger-bg`, `trigger-fg`, `trigger-border`, `trigger-border-width`, `trigger-radius`, `trigger-font-size`, `hover-trigger-border`, `open-trigger-border`, `open-trigger-ring`, `trigger-value-empty-fg`, `trigger-icon-fg`
- **Eje `size`**: sí · **ficheros**: `natural-time-picker.css`
## 1. Knobs fuera de alcance
### 1.1 Directo a primitivo global (14)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `natural-time-picker.css:40` | `[data-natural-time-picker-panel]` | `min-inline-size` | `var(--popover-min-width-md)` ⤴ prestado de `popover` |
| 2 | `natural-time-picker.css:44` | `[data-natural-time-picker-panel]` | `padding-block` | `var(--popover-padding-block-md)` ⤴ prestado de `popover` |
| 3 | `natural-time-picker.css:45` | `[data-natural-time-picker-panel]` | `padding-inline` | `var(--popover-padding-inline-md)` ⤴ prestado de `popover` |
| 4 | `natural-time-picker.css:46` | `[data-natural-time-picker-panel]` | `background` | `var(--color-surface-overlay)` |
| 5 | `natural-time-picker.css:47` | `[data-natural-time-picker-panel]` | `color` | `var(--color-content-primary)` |
| 6 | `natural-time-picker.css:48` | `[data-natural-time-picker-panel]` | `border` | `var(--border-width) solid var(--color-border-subtle)` |
| 7 | `natural-time-picker.css:49` | `[data-natural-time-picker-panel]` | `border-radius` | `var(--popover-radius)` ⤴ prestado de `popover` |
| 8 | `natural-time-picker.css:50` | `[data-natural-time-picker-panel]` | `box-shadow` | `var(--shadow-overlay)` |
| 9 | `natural-time-picker.css:53` | `[data-natural-time-picker-panel][data-size='sm']` | `min-inline-size` | `var(--popover-min-width-sm)` ⤴ prestado de `popover` |
| 10 | `natural-time-picker.css:54` | `[data-natural-time-picker-panel][data-size='sm']` | `padding-block` | `var(--popover-padding-block-sm)` ⤴ prestado de `popover` |
| 11 | `natural-time-picker.css:55` | `[data-natural-time-picker-panel][data-size='sm']` | `padding-inline` | `var(--popover-padding-inline-sm)` ⤴ prestado de `popover` |
| 12 | `natural-time-picker.css:58` | `[data-natural-time-picker-panel][data-size='lg']` | `min-inline-size` | `var(--popover-min-width-lg)` ⤴ prestado de `popover` |
| 13 | `natural-time-picker.css:59` | `[data-natural-time-picker-panel][data-size='lg']` | `padding-block` | `var(--popover-padding-block-lg)` ⤴ prestado de `popover` |
| 14 | `natural-time-picker.css:60` | `[data-natural-time-picker-panel][data-size='lg']` | `padding-inline` | `var(--popover-padding-inline-lg)` ⤴ prestado de `popover` |
### 1.2 A través de un privado (0)
_Ninguno._
### 1.3 Literales (6)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `natural-time-picker.css:39` | `[data-natural-time-picker-panel]` | `inline-size` | `100%` |
| 2 | `natural-time-picker.css:86` | `[data-natural-time-picker-time]` | `line-height` | `1` |
| 3 | `natural-time-picker.css:96` | `[data-natural-time-picker-meridiem]` | `line-height` | `1` |
| 4 | `natural-time-picker.css:124` | `[data-natural-time-picker-band]` | `inline-size` | `100%` |
| 5 | `natural-time-picker.css:222` | `[data-natural-time-picker-tick]` | `line-height` | `1` |
| 6 | `natural-time-picker.css:292` | `[data-natural-time-picker-panel] [data-natural-time-picker-periods]:not([data-icon-only]) [data-button]` | `inline-size` | `100%` |
### 1.4 Excepciones firmadas (0) — 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.
_Ninguno._
## 2. Sistema transversal (1) — 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 | `natural-time-picker.css:66` | `[data-natural-time-picker][data-disabled]` | `opacity` | `var(--opacity-disabled)` |
## 3. Privados de la receta — ¿de dónde sale su valor?
| privado | declaraciones | valor(es) | origen | ¿deriva de un público? |
| --- | ---: | --- | --- | :-: |
| `--_natural-time-picker-sky-night` | 1 | `#1a1d2e` | exception | no |
| `--_natural-time-picker-sky-dawn` | 1 | `#3a4a7a` | exception | no |
| `--_natural-time-picker-sky-gold` | 1 | `#f0b85a` | exception | no |
| `--_natural-time-picker-sky-day` | 1 | `#7fc8e8` | exception | no |
| `--_natural-time-picker-sky-dusk` | 1 | `#c4623a` | exception | no |
| `--_natural-time-picker-ink` | 1 | `#ffffff` | exception | no |
| `--_natural-time-picker-line` | 1 | `rgb(255 255 255 / 0.9)` | exception | no |
| `--_natural-time-picker-tick` | 1 | `rgb(255 255 255 / 0.88)` | exception | no |
| `--_natural-time-picker-tick-shadow` | 1 | `rgb(0 0 0 / 0.5)` | exception | no |
| `--_natural-time-picker-band-block` | 1 | `var(--natural-time-picker-band-height)` | public | **sí** |
| `--_natural-time-picker-knob` | 1 | `var(--natural-time-picker-knob-size)` | public | **sí** |
## 4. Propuesta de corrección
- **Consume la capa compartida `picker-shell`.** Un eje que la capa posee se consume como `var(--_x, var(--x))`; el consumidor **no acuña** `--natural-time-picker-{eje}` para él — sería un vocabulario paralelo (README de `eidos/components`, «Capas compartidas» regla 2).
- **Consume tokens públicos de `popover`.** Un token prestado importa la semántica de su dueño: la corrección no es duplicarlo con prefijo propio, sino la decisión de familia que la auditoría de fase 1 dejó registrada (`theming-audit.md` §B, familia calendar).
- **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` (13)
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 (`--natural-time-picker-…`) | scope TSC | valor propuesto | usos |
| --- | --- | --- | ---: |
| `panel-width` | `root` | `var(--popover-min-width-md)` | 1 |
| `panel-padding-block` | `root` | `var(--popover-padding-block-md)` | 1 |
| `panel-padding-inline` | `root` | `var(--popover-padding-inline-md)` | 1 |
| `panel-bg` | `root` | `var(--color-surface-overlay)` | 1 |
| `panel-fg` | `root` | `var(--color-content-primary)` | 1 |
| `panel-radius` | `root` | `var(--popover-radius)` | 1 |
| `panel-shadow` | `root` | `var(--shadow-overlay)` | 1 |
| `panel-width-sm` | `root` | `var(--popover-min-width-sm)` | 1 |
| `panel-padding-block-sm` | `root` | `var(--popover-padding-block-sm)` | 1 |
| `panel-padding-inline-sm` | `root` | `var(--popover-padding-inline-sm)` | 1 |
| `panel-width-lg` | `root` | `var(--popover-min-width-lg)` | 1 |
| `panel-padding-block-lg` | `root` | `var(--popover-padding-block-lg)` | 1 |
| `panel-padding-inline-lg` | `root` | `var(--popover-padding-inline-lg)` | 1 |
### 4.2 Sin nombre mecánico (7)
- **⚠ 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** — 3: `inline-size`.
- **⚠ 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`.
- **⚠ 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`.
### 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 -->
**Revisión 2026-08-20 — verificación previa a implementación (Opus).**
**Análisis (§1–§3): CORRECTO en cifras** (78 · 0 · 0 · 69 · 7 · 2), con el
mismo hallazgo de prefijo que sus hermanos de familia: el «privado 0» es
falso de nombre — la receta declara y consume `--_ntp-*` (`band-block`,
`sky-night/dawn/gold/day/dusk`, `knob`, `ink`, `line`), prefijo ABREVIADO que
viola la regla 5 de theming §6 («no abreviar el nombre del componente») y
deja al censo contándolos como `global`. **Paso previo: renombrar `--_ntp-*`
→ `--_natural-time-picker-*`** (mecánico, `git ls-files` antes).
**Propuesta (§4.1): APTA EN SU SUBCONJUNTO PROPIO, con tres correcciones:**
1. **Las filas `panel-*` que alias-an públicos de `popover`**
(`panel-width = var(--popover-min-width-md)`, paddings, radius) **no se
acuñan**: son el préstamo-con-prefijo que la nota de §4 prohíbe. Si el
panel se monta dentro de un Popover compuesto, su cromo se tema en la
ficha de popover; si el autor quiere ejes propios del panel, el valor es
el VERBATIM de hoy, nunca el alias del token ajeno. Decisión del autor.
2. **La talla vive SÓLO en el panel** (`[data-natural-time-picker-panel]
[data-size]`), que además viaja por portal: coordenadas por talla en
`root` (como propone la tabla) **+ nombre resuelto con
`parts: ['panel']`** y default `host` (tsc.md §multi-part) — sin `parts`,
las declarations por talla no alcanzan nunca el panel.
3. **El cielo es LA decisión de diseño de este componente**: la banda es un
gradiente de 5 paradas (`--_ntp-sky-*`). O (a) los cielos son
físicamente fijos (la válvula que recipe-contract §3 nombra literalmente:
«the natural clock's skies») → anotarlos y excepción R-5; o (b) son
temables → promover las 5 paradas a públicos
(`sky-night/dawn/gold/day/dusk`) y el gradiente las lee. Las dos son
coherentes; la elige el autor. La fila `band-bg` con el gradiente entero
como valor NO — un tema no debe re-escribir la rampa para mover un cielo.
**Lo acuñable tal cual** (patrón Sidebar, valores verbatim): `band-height/
radius/width`, `knob-width/height/radius/bg/fg` (leyendo los privados
renombrados o promoviéndolos), `display-gap`, `time-font-*`,
`meridiem-*`, `period-*`, `stepper-group-*` — partes limpias
(`panel`, `band`, `trigger`, `stepper-group`, `periods`, `meridiem`, `time`).
Los 7 literales: anotar o tokenizar uno a uno. Sonda §7.2: abrir el panel
ANTES de medir (portal + pantalla oculta congela rAF).
**Bloqueos de firma**: cielos fijos vs temables (punto 3) · panel propio vs
cromo de popover (punto 1) · D-TH.4 (familia tiempo, B7).
---
**EJECUTADO 2026-08-20.** Alcance **0 % → 61 %** · contrato **0 → 60 claves
públicas**. Los dos «bloqueos» de arriba resultaron NO serlo: la doctrina ya
estaba escrita y no la había leído al revisar.
1. **Los cielos no había que decidirlos**: el guard `recipe-css-contract` ya
lista `natural-time-picker` en `FIXED_TONE_COMPONENTS` — la excepción de tono
fijo (un cielo no cambia con el tema) — y la receta ya anota cada literal con
su válvula. Se quedan privados, como estaban.
2. **El panel sigue prestando `--popover-*`**: su cabecera dice que la superficie
ES el flotante canónico. Duplicarlo con prefijo propio sería el vocabulario
paralelo que el contrato prohíbe. Sólo se acuñó lo que sí es suyo: el
`panel-gap` por talla.
3. **Prefijo abreviado muerto**: 37 referencias `--_ntp-*` renombradas a
`--_natural-time-picker-*` (receta, wrapper, README y el propio guard).
Acuñado: banda y knob por talla con `parts: ['panel']` — conservando su
derivación `calc(--size-{k}-control-height * 1.7 + --space-2)` y `* 0.62`, así
que la expresión sigue siendo el knob —, línea indicadora, ticks, readout
(time / meridiem / period), steppers, chips y trigger.
Verificación: **diff de computed = 0** sobre 6.438 valores en 8 estados ·
centinela 40/62 automático, y los marcados como muertos verificados a mano:
`band-height-md`, `knob-size-md`, `panel-gap-md`, `line-width`, `line-radius` y
`ticks-inset-block-end` **alcanzan**; los `trigger-*` **no**, porque
`[data-popover-trigger]:not([data-archetype='field-trigger'])` gana la cascada y
viste el trigger — inertes ya antes de tokenizarlos, ahora dicho en el README.
Guards: `component:audit` PASS · `eidos-lint` 0 invalid · suite sin rojos nuevos
· `rtl:check` 0 · `docs:check` 0 · el guard del bundle obligó a apuntar
`tick-font-size` a `--size-xxs-font-size`.
El 39 % que no alcanza son los préstamos de `popover` y el trío de superficie
(`--color-surface-overlay`, `--color-border-subtle`, `--shadow-overlay`) que el
componente declara a mano en vez de estampar `data-depth='overlay'`: migrarlo es
otra decisión, anotada.
<!-- veredicto:end -->

Powered by TurnKey Linux.