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/date-range-picker.md

231 lines
20 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.

# date-range-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-20 · **Alcance**: **40%** — 20 de 50 knobs por token público
- **Knobs de apariencia**: 53 — público 20 · privado 9 · global 19 · literal 2 · sistema 3 _(fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 22 pública(s) — `accent-border`, `accent-track`, `accent-text`, `overview-gap`, `overview-width`, `overview-header-gap`, `overview-heading-fg`, `overview-heading-font-weight`, `overview-nav-size`, `overview-nav-radius`, `overview-nav-fg`, `overview-nav-font-size`, `overview-grid-gap`, `overview-cell-height`, `overview-cell-radius`, `overview-cell-fg`, `overview-cell-font-size`, `in-range-overview-cell-bg`, `in-range-overview-cell-fg`, `endpoint-overview-cell-bg`, `endpoint-overview-cell-fg`, `endpoint-overview-cell-border`
- **Eje `size`**: sí · **ficheros**: `date-range-picker-time.css`, `date-range-picker.css`
## 1. Knobs fuera de alcance
### 1.1 Directo a primitivo global (19)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `date-range-picker-time.css:11` | `.range-time` | `gap` | `calc(var(--space-8) + var(--space-1))` |
| 2 | `date-range-picker-time.css:12` | `.range-time` | `padding-block-start` | `var(--space-2)` |
| 3 | `date-range-picker-time.css:13` | `.range-time` | `border-block-start` | `var(--border-width, 1px) solid var(--color-neutral-border)` |
| 4 | `date-range-picker.css:166` | `[data-date-range-picker][data-invalid] [data-field-control]` | `border-color` | `var(--field-invalid-control-border)` ⤴ prestado de `field` |
| 5 | `date-range-picker.css:189` | `[data-date-range-picker-trigger]` | `border` | `var(--field-control-border-width) solid var(--field-control-border)` ⤴ prestado de `field` |
| 6 | `date-range-picker.css:190` | `[data-date-range-picker-trigger]` | `border-radius` | `var(--field-control-radius)` ⤴ prestado de `field` |
| 7 | `date-range-picker.css:191` | `[data-date-range-picker-trigger]` | `background` | `var(--field-control-bg)` ⤴ prestado de `field` |
| 8 | `date-range-picker.css:217` | `[data-date-range-picker-trigger][data-disabled], [data-date-range-picker-trigger]:disabled` | `opacity` | `var(--field-disabled-opacity)` ⤴ prestado de `field` |
| 9 | `date-range-picker.css:232` | `[data-date-range-field-input] [data-date-range-picker-trigger]` | `inline-size` | `var(--field-segment-height)` ⤴ prestado de `field` |
| 10 | `date-range-picker.css:233` | `[data-date-range-field-input] [data-date-range-picker-trigger]` | `block-size` | `var(--field-segment-height)` ⤴ prestado de `field` |
| 11 | `date-range-picker.css:237` | `[data-date-range-field-input] [data-date-range-picker-trigger]` | `border-radius` | `var(--radius-sm)` |
| 12 | `date-range-picker.css:248` | `[data-date-range-field-input] [data-date-range-picker-trigger]:hover, [data-date-range-field-input] [data-date-range-picker-trigger]:focus-visible` | `background` | `var(--field-segment-active-bg)` ⤴ prestado de `field` |
| 13 | `date-range-picker.css:249` | `[data-date-range-field-input] [data-date-range-picker-trigger]:hover, [data-date-range-field-input] [data-date-range-picker-trigger]:focus-visible` | `color` | `var(--field-segment-active-text)` ⤴ prestado de `field` |
| 14 | `date-range-picker.css:271` | `[data-date-range-picker-calendar][data-range-calendar]` | `gap` | `var(--calendar-gap)` ⤴ prestado de `calendar` |
| 15 | `date-range-picker.css:279` | `[data-date-range-picker-calendar][data-range-calendar]` | `color` | `var(--calendar-fg)` ⤴ prestado de `calendar` |
| 16 | `date-range-picker.css:280` | `[data-date-range-picker-calendar][data-range-calendar]` | `font-family` | `var(--calendar-font-family)` ⤴ prestado de `calendar` |
| 17 | `date-range-picker.css:282` | `[data-date-range-picker-calendar][data-range-calendar]` | `line-height` | `var(--calendar-line-height)` ⤴ prestado de `calendar` |
| 18 | `date-range-picker.css:407` | `[data-date-range-picker-year-view-prev], [data-date-range-picker-year-view-next], [data-date-range-picker-month-view-prev], [data-date-range-picker-month-view-next]` | `border` | `var(--border-width) solid transparent` |
| 19 | `date-range-picker.css:432` | `[data-date-range-picker-year-view-cell], [data-date-range-picker-month-view-cell]` | `border` | `var(--border-width) solid transparent` |
### 1.2 A través de un privado (9)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `date-range-picker.css:26` | `[data-date-range-picker]` | `gap` | `var(--space-1-5) var(--_date-range-picker-field-gap)` |
| 2 | `date-range-picker.css:29` | `[data-date-range-picker]` | `font-size` | `var(--_date-range-picker-field-font-size)` |
| 3 | `date-range-picker.css:186` | `[data-date-range-picker-trigger]` | `inline-size` | `var(--_date-range-picker-field-height)` |
| 4 | `date-range-picker.css:187` | `[data-date-range-picker-trigger]` | `block-size` | `var(--_date-range-picker-field-height)` |
| 5 | `date-range-picker.css:202` | `[data-date-range-picker-trigger]:hover` | `border-color` | `var(--_date-range-picker-field-accent-border)` |
| 6 | `date-range-picker.css:203` | `[data-date-range-picker-trigger]:hover` | `background` | `var(--_date-range-picker-field-accent-track)` |
| 7 | `date-range-picker.css:204` | `[data-date-range-picker-trigger]:hover` | `color` | `var(--_date-range-picker-field-accent-text)` |
| 8 | `date-range-picker.css:208` | `[data-date-range-picker-trigger]:focus-visible` | `border-color` | `var(--_date-range-picker-field-accent-border)` |
| 9 | `date-range-picker.css:281` | `[data-date-range-picker-calendar][data-range-calendar]` | `font-size` | `var(--_date-range-picker-calendar-font-size)` |
### 1.3 Literales (2)
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `date-range-picker.css:27` | `[data-date-range-picker]` | `inline-size` | `100%` |
| 2 | `date-range-picker.css:272` | `[data-date-range-picker-calendar][data-range-calendar]` | `inline-size` | `max-content` |
## 2. Sistema transversal (3) — 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 | `date-range-picker.css:170` | `[data-date-range-picker][data-disabled]` | `opacity` | `var(--opacity-disabled)` |
| 2 | `date-range-picker.css:210` | `[data-date-range-picker-trigger]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
| 3 | `date-range-picker.css:488` | `[data-date-range-picker-year-view-cell]:focus-visible, [data-date-range-picker-month-view-cell]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--color-primary-border)` |
## 3. Privados de la receta — ¿de dónde sale su valor?
| privado | declaraciones | valor(es) | origen | ¿deriva de un público? |
| --- | ---: | --- | --- | :-: |
| `--_date-range-picker-field-height` | 4 | `var(--field-control-height-md)`, `var(--field-control-height-xs)`, `var(--field-control-height-sm)`, `var(--field-control-height-lg)` | global | no |
| `--_date-range-picker-field-gap` | 4 | `var(--space-1)`, `var(--space-0-5)`, `var(--space-1-5)` | global | no |
| `--_date-range-picker-field-font-size` | 4 | `var(--field-control-font-size-md)`, `var(--field-control-font-size-xs)`, `var(--field-control-font-size-sm)`, `var(--field-control-font-size-lg)` | global | no |
| `--_date-range-picker-field-accent-border` | 8 | `var(--date-range-picker-accent-border)`, `var(--color-secondary-border)`, `var(--color-neutral-border)`, `var(--color-affirm-border)`, `var(--color-fulfill-border)`, `var(--color-risk-border)` …(+2) | global, public | no |
| `--_date-range-picker-field-accent-track` | 8 | `var(--date-range-picker-accent-track)`, `var(--color-secondary-track)`, `var(--color-neutral-track)`, `var(--color-affirm-track)`, `var(--color-fulfill-track)`, `var(--color-risk-track)` …(+2) | global, public | no |
| `--_date-range-picker-field-accent-text` | 8 | `var(--date-range-picker-accent-text)`, `var(--color-secondary-text)`, `var(--color-neutral-text)`, `var(--color-affirm-text)`, `var(--color-fulfill-text)`, `var(--color-risk-text)` …(+2) | global, public | no |
| `--_date-range-picker-calendar-font-size` | 8 | `var(--calendar-font-size-md)`, `var(--calendar-font-size-xs)`, `var(--calendar-font-size-sm)`, `var(--calendar-font-size-lg)` | global | no |
## 4. Propuesta de corrección
- **Consume la capa compartida `field-segment-state`.** Un eje que la capa posee se consume como `var(--_x, var(--x))`; el consumidor **no acuña** `--date-range-picker-{eje}` para él — sería un vocabulario paralelo (README de `eidos/components`, «Capas compartidas» regla 2).
- **Consume la capa compartida `picker-shell`.** Un eje que la capa posee se consume como `var(--_x, var(--x))`; el consumidor **no acuña** `--date-range-picker-{eje}` para él — sería un vocabulario paralelo (README de `eidos/components`, «Capas compartidas» regla 2).
- **Consume tokens públicos de `calendar`, `field`.** 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` (37)
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 (`--date-range-picker-…`) | scope TSC | valor propuesto | usos |
| --- | --- | --- | ---: |
| `hover-trigger-bg` | `root` | ⚠ `var(--date-range-picker-accent-track)` / `var(--color-secondary-track)` / `var(--color-neutral-track)` / `var(--color-affirm-track)` / `var(--color-fulfill-track)` / `var(--color-risk-track)` / `var(--color-threat-track)` / `var(--color-loss-track)` / `var(--field-segment-active-bg)` | 9 |
| `hover-trigger-fg` | `root` | ⚠ `var(--date-range-picker-accent-text)` / `var(--color-secondary-text)` / `var(--color-neutral-text)` / `var(--color-affirm-text)` / `var(--color-fulfill-text)` / `var(--color-risk-text)` / `var(--color-threat-text)` / `var(--color-loss-text)` / `var(--field-segment-active-text)` | 9 |
| `hover-trigger-border` | `root` | ⚠ `var(--date-range-picker-accent-border)` / `var(--color-secondary-border)` / `var(--color-neutral-border)` / `var(--color-affirm-border)` / `var(--color-fulfill-border)` / `var(--color-risk-border)` / `var(--color-threat-border)` / `var(--color-loss-border)` | 8 |
| `trigger-border` | `root` | ⚠ `var(--date-range-picker-accent-border)` / `var(--color-secondary-border)` / `var(--color-neutral-border)` / `var(--color-affirm-border)` / `var(--color-fulfill-border)` / `var(--color-risk-border)` / `var(--color-threat-border)` / `var(--color-loss-border)` | 8 |
| `trigger-radius` | `root` | ⚠ `var(--field-control-radius)` / `var(--radius-sm)` | 2 |
| `calendar-font-size-md` | `size:md` | `var(--calendar-font-size-md)` | 2 |
| `calendar-font-size-xs` | `size:xs` | `var(--calendar-font-size-xs)` | 2 |
| `calendar-font-size-sm` | `size:sm` | `var(--calendar-font-size-sm)` | 2 |
| `calendar-font-size-lg` | `size:lg` | `var(--calendar-font-size-lg)` | 2 |
| `gap` | `root` | `calc(var(--space-8) + var(--space-1))` | 1 |
| `padding-block` | `root` | `var(--space-2)` | 1 |
| `gap-md` | `size:md` | `var(--space-1)` | 1 |
| `gap-xs` | `size:xs` | `var(--space-0-5)` | 1 |
| `gap-sm` | `size:sm` | `var(--space-0-5)` | 1 |
| `gap-lg` | `size:lg` | `var(--space-1-5)` | 1 |
| `font-size-md` | `size:md` | `var(--field-control-font-size-md)` | 1 |
| `font-size-xs` | `size:xs` | `var(--field-control-font-size-xs)` | 1 |
| `font-size-sm` | `size:sm` | `var(--field-control-font-size-sm)` | 1 |
| `font-size-lg` | `size:lg` | `var(--field-control-font-size-lg)` | 1 |
| `invalid-border` | `root` | `var(--field-invalid-control-border)` | 1 |
| `trigger-width-md` | `size:md` | `var(--field-control-height-md)` | 1 |
| `trigger-width-xs` | `size:xs` | `var(--field-control-height-xs)` | 1 |
| `trigger-width-sm` | `size:sm` | `var(--field-control-height-sm)` | 1 |
| `trigger-width-lg` | `size:lg` | `var(--field-control-height-lg)` | 1 |
| `trigger-height-md` | `size:md` | `var(--field-control-height-md)` | 1 |
| `trigger-height-xs` | `size:xs` | `var(--field-control-height-xs)` | 1 |
| `trigger-height-sm` | `size:sm` | `var(--field-control-height-sm)` | 1 |
| `trigger-height-lg` | `size:lg` | `var(--field-control-height-lg)` | 1 |
| `trigger-bg` | `root` | `var(--field-control-bg)` | 1 |
| `disabled-trigger-opacity` | `root` | `var(--field-disabled-opacity)` | 1 |
| `trigger-width` | `root` | `var(--field-segment-height)` | 1 |
| `trigger-height` | `root` | `var(--field-segment-height)` | 1 |
| `calendar-gap` | `root` | `var(--calendar-gap)` | 1 |
| `calendar-width` | `root` | `max-content` | 1 |
| `calendar-fg` | `root` | `var(--calendar-fg)` | 1 |
| `calendar-font-family` | `root` | `var(--calendar-font-family)` | 1 |
| `calendar-line-height` | `root` | `var(--calendar-line-height)` | 1 |
### 4.2 Sin nombre mecánico (5)
- **⚠ decisión: `border-block-start` 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-start`.
- **⚠ 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** — 1: `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)** — 3: `border`.
### 4.3 Avisos sobre los tokens propuestos (8)
- **⚠ decisión: el valor es una expresión — el token puede llevar la expresión entera o sólo su término variable** — `--date-range-picker-gap`
- **el privado `--_date-range-picker-field-gap` debe pasar a leer este público (o desaparecer)** — `--date-range-picker-gap-md`, `--date-range-picker-gap-xs`, `--date-range-picker-gap-sm`, `--date-range-picker-gap-lg`
- **el privado `--_date-range-picker-field-font-size` debe pasar a leer este público (o desaparecer)** — `--date-range-picker-font-size-md`, `--date-range-picker-font-size-xs`, `--date-range-picker-font-size-sm`, `--date-range-picker-font-size-lg`
- **el privado `--_date-range-picker-field-height` debe pasar a leer este público (o desaparecer)** — `--date-range-picker-trigger-width-md`, `--date-range-picker-trigger-width-xs`, `--date-range-picker-trigger-width-sm`, `--date-range-picker-trigger-width-lg`, `--date-range-picker-trigger-height-md`, `--date-range-picker-trigger-height-xs`, `--date-range-picker-trigger-height-sm`, `--date-range-picker-trigger-height-lg`
- **el privado `--_date-range-picker-field-accent-border` debe pasar a leer este público (o desaparecer)** — `--date-range-picker-hover-trigger-border`, `--date-range-picker-trigger-border`
- **el privado `--_date-range-picker-field-accent-track` debe pasar a leer este público (o desaparecer)** — `--date-range-picker-hover-trigger-bg`
- **el privado `--_date-range-picker-field-accent-text` debe pasar a leer este público (o desaparecer)** — `--date-range-picker-hover-trigger-fg`
- **el privado `--_date-range-picker-calendar-font-size` debe pasar a leer este público (o desaparecer)** — `--date-range-picker-calendar-font-size-md`, `--date-range-picker-calendar-font-size-xs`, `--date-range-picker-calendar-font-size-sm`, `--date-range-picker-calendar-font-size-lg`
### 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** (55 · 0 · 0 · 49 · 3 · 3), y el
«privado 0» vuelve a ser falso de nombre: la receta declara localmente
`--_date-field-*` (`date-range-picker.css:7-12,33-34`) **y hasta
`--_calendar-font-size`** (`:16,:39`) — privados con prefijo de OTROS
componentes, redeclarados por talla. Si un `date-field` o un `calendar`
reales conviven cerca, los nombres se cruzan. **Paso previo: renombrar ambos
grupos a `--_date-range-picker-*`** (mecánico, `git ls-files` antes).
**Propuesta (§4.1): NO APTA tal cual** — es el híbrido de sus dos familias y
hereda los defectos de ambas:
1. **Lado calendar** (filas `calendar-*`): alias de públicos de `calendar` →
NO acuñar (nota de §4; purga de alias §39). Va con la **decisión de
familia calendar** (`theming-audit.md` §B, préstamo medido: 38 tokens),
bloque B7.
2. **Lado field** (filas `trigger-*`, `font-size`, `invalid-border`,
`disabled-trigger-opacity`): leen públicos de `field` y los privados
renombrados del punto previo → mandato «Field composition» de
`theming-audit.md` §5.3-3; se teman en la ficha del dueño, no aquí. Los
`hover-trigger-*` con `--_date-field-accent-*`: promover el acento a
público propio (`accent-border/track/text`) como en time-picker.
3. **Lo genuinamente propio y acuñable: la vista de década** (`year-view-*`)
— partes limpias, valores verbatim válidos. Corrección de nombres: las
colisiones ⚠ de `year-view-cell-fg/bg` son estados sin parsear
(seleccionado/en-rango/hoy) → separar por estado
(`selected-year-view-cell-*`, etc.) leyendo el selector real. El
`year-view-prev-width/height = var(--control-height-sm)` — comprobar
contra el bundle (`--size-sm-control-height` computa idéntico) y usar la
coordenada del bundle.
4. **Talla**: el root estampa `data-size` ✓, pero el popup del calendario
viaja por portal → cualquier token consumido dentro del content necesita
`parts` o resolverse en el nodo portalado (verificar en la sonda §7.2 con
el popup ABIERTO).
**Bloqueos de firma**: decisión de familia calendar · mandato
field-composition · D-TH.4 (B7). Sin esas dos firmas, aquí sólo procede el
renombrado de privados + `year-view-*`.
---
**EJECUTADO 2026-08-20 (exactamente lo que no dependía de otra firma).** Alcance
**0 % → 42 %** · contrato **0 → 22 claves públicas**.
Hecho, los dos puntos que la revisión marcó como accionables:
1. **Renombrado de privados con prefijo ajeno**: 44 `--_date-field-*` y 9
`--_calendar-font-size` que este CSS declaraba con el nombre de OTRO
componente — se habrían cruzado con un DateField o un Calendar anidados.
Ahora `--_date-range-picker-*`.
2. **La vista de década / mes a contrato**: `overview-*` (separación, ancho,
cabecera, navegación, rejilla, celdas) con los estados del rango separados
por nombre (`in-range-overview-cell-*`, `endpoint-overview-cell-*`), que es
justo la colisión que la propuesta automática tenía en `year-view-cell-fg`.
Y el acento a público.
No hecho, con dueño: la mitad de calendario espera la **decisión de familia
calendar** (§B) y la de campo el **mandato de composición de Field** (§5.3-3).
Verificación: **diff de computed = 0** sobre 493 valores en 8 estados ·
`component:audit` PASS · suite eidos sin rojos nuevos · `rtl:check` 0 ·
`docs:check` 0.
<!-- veredicto:end -->

Powered by TurnKey Linux.