20 KiB
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 · método y protocolo: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 comovar(--_x, var(--x)); el consumidor no acuña--date-range-picker-{eje}para él — sería un vocabulario paralelo (README deeidos/components, «Capas compartidas» regla 2). - Consume la capa compartida
picker-shell. Un eje que la capa posee se consume comovar(--_x, var(--x)); el consumidor no acuña--date-range-picker-{eje}para él — sería un vocabulario paralelo (README deeidos/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 guardrecipe-css-contractprohí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-startes 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:
borderes 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-gapdebe 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-sizedebe 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-heightdebe 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-borderdebe 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-trackdebe pasar a leer este público (o desaparecer) —--date-range-picker-hover-trigger-bg - el privado
--_date-range-picker-field-accent-textdebe pasar a leer este público (o desaparecer) —--date-range-picker-hover-trigger-fg - el privado
--_date-range-picker-calendar-font-sizedebe 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, unbackgrounden 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
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:
- Lado calendar (filas
calendar-*): alias de públicos decalendar→ 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. - Lado field (filas
trigger-*,font-size,invalid-border,disabled-trigger-opacity): leen públicos defieldy los privados renombrados del punto previo → mandato «Field composition» detheming-audit.md§5.3-3; se teman en la ficha del dueño, no aquí. Loshover-trigger-*con--_date-field-accent-*: promover el acento a público propio (accent-border/track/text) como en time-picker. - 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 ⚠ deyear-view-cell-fg/bgson estados sin parsear (seleccionado/en-rango/hoy) → separar por estado (selected-year-view-cell-*, etc.) leyendo el selector real. Elyear-view-prev-width/height = var(--control-height-sm)— comprobar contra el bundle (--size-sm-control-heightcomputa idéntico) y usar la coordenada del bundle. - Talla: el root estampa
data-size✓, pero el popup del calendario viaja por portal → cualquier token consumido dentro del content necesitapartso 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:
- Renombrado de privados con prefijo ajeno: 44
--_date-field-*y 9--_calendar-font-sizeque este CSS declaraba con el nombre de OTRO componente — se habrían cruzado con un DateField o un Calendar anidados. Ahora--_date-range-picker-*. - 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 enyear-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.