22 KiB
range-calendar — 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-23 · Alcance: 0% — 0 de 13 knobs por token público
- Knobs de apariencia: 98 — público 0 · privado 7 · global 2 · literal 4 · sistema 85 · excepción 0 (los dos últimos, fuera del ratio)
- Contrato hoy (
lib/recipes/base.ts): 0 pública(s) · 5 privada(s) forward —_palette-solid,_palette-solid-hover,_palette-track,_palette-border,_palette-text - Eje
size: no · ficheros:range-calendar.css
1. Knobs fuera de alcance
1.1 Directo a primitivo global (2)
| # | fichero:línea | selector | propiedad | valor |
|---|---|---|---|---|
| 1 | range-calendar.css:269 |
[data-range-calendar-day][data-range-start] |
font-weight |
var(--font-weight-semibold) |
| 2 | range-calendar.css:278 |
[data-range-calendar-day][data-range-end] |
font-weight |
var(--font-weight-semibold) |
1.2 A través de un privado (7)
| # | fichero:línea | selector | propiedad | valor |
|---|---|---|---|---|
| 1 | range-calendar.css:224 |
[data-range-calendar-cell][data-in-range], [data-range-calendar-cell][data-in-preview-range] |
background |
var(--_range-calendar-palette-track) |
| 2 | range-calendar.css:317 |
[data-range-calendar-day][data-in-range]:not([data-selected]) |
color |
var(--_range-calendar-palette-text) |
| 3 | range-calendar.css:323 |
[data-range-calendar-day][data-preview-start], [data-range-calendar-day][data-preview-end], [data-range-calendar-day][data-in-preview-range]:not([data-selected]) |
border-color |
var(--_range-calendar-palette-border) |
| 4 | range-calendar.css:325 |
[data-range-calendar-day][data-preview-start], [data-range-calendar-day][data-preview-end], [data-range-calendar-day][data-in-preview-range]:not([data-selected]) |
color |
var(--_range-calendar-palette-text) |
| 5 | range-calendar.css:329 |
[data-range-calendar-day][data-focused]:not([data-selected]) |
border-color |
var(--_range-calendar-palette-border) |
| 6 | range-calendar.css:330 |
[data-range-calendar-day][data-focused]:not([data-selected]) |
background |
var(--_range-calendar-palette-track) |
| 7 | range-calendar.css:331 |
[data-range-calendar-day][data-focused]:not([data-selected]) |
color |
var(--_range-calendar-palette-text) |
1.3 Literales (4)
| # | fichero:línea | selector | propiedad | valor |
|---|---|---|---|---|
| 1 | range-calendar.css:51 |
[data-range-calendar] |
inline-size |
max-content |
| 2 | range-calendar.css:95 |
[data-range-calendar-header] |
inline-size |
100% |
| 3 | range-calendar.css:182 |
[data-range-calendar-month-panel] |
min-inline-size |
max-content |
| 4 | range-calendar.css:187 |
[data-range-calendar-grid] |
inline-size |
max-content |
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 (85) — 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 | range-calendar.css:50 |
[data-range-calendar] |
gap |
var(--calendar-gap) ⤴ prestado de calendar |
| 2 | range-calendar.css:53 |
[data-range-calendar] |
padding |
var(--calendar-padding) |
| 3 | range-calendar.css:54 |
[data-range-calendar] |
border |
var(--calendar-border-width) solid var(--calendar-border) ⤴ prestado de calendar |
| 4 | range-calendar.css:55 |
[data-range-calendar] |
border-radius |
var(--calendar-radius) ⤴ prestado de calendar |
| 5 | range-calendar.css:56 |
[data-range-calendar] |
background |
var(--calendar-bg) ⤴ prestado de calendar |
| 6 | range-calendar.css:57 |
[data-range-calendar] |
box-shadow |
var(--calendar-shadow) ⤴ prestado de calendar |
| 7 | range-calendar.css:58 |
[data-range-calendar] |
color |
var(--calendar-fg) ⤴ prestado de calendar |
| 8 | range-calendar.css:59 |
[data-range-calendar] |
font-family |
var(--calendar-font-family) ⤴ prestado de calendar |
| 9 | range-calendar.css:60 |
[data-range-calendar] |
font-size |
var(--calendar-font-size) |
| 10 | range-calendar.css:61 |
[data-range-calendar] |
line-height |
var(--calendar-line-height) ⤴ prestado de calendar |
| 11 | range-calendar.css:65 |
[data-range-calendar][data-variant='outline'] |
background |
var(--calendar-outline-bg) ⤴ prestado de calendar |
| 12 | range-calendar.css:66 |
[data-range-calendar][data-variant='outline'] |
box-shadow |
var(--calendar-outline-shadow) ⤴ prestado de calendar |
| 13 | range-calendar.css:94 |
[data-range-calendar-header] |
gap |
var(--calendar-header-gap) ⤴ prestado de calendar |
| 14 | range-calendar.css:103 |
[data-range-calendar-header-controls] |
gap |
var(--calendar-header-gap) ⤴ prestado de calendar |
| 15 | range-calendar.css:110 |
[data-range-calendar-heading] |
color |
var(--calendar-heading-fg) ⤴ prestado de calendar |
| 16 | range-calendar.css:111 |
[data-range-calendar-heading] |
font-weight |
var(--calendar-heading-font-weight) ⤴ prestado de calendar |
| 17 | range-calendar.css:121 |
[data-range-calendar-day]:focus-visible |
outline |
var(--focus-ring-width) solid var(--focus-ring-color) |
| 18 | range-calendar.css:128 |
[data-range-calendar-month-select], [data-range-calendar-year-select] |
block-size |
var(--calendar-control-size) |
| 19 | range-calendar.css:129 |
[data-range-calendar-month-select], [data-range-calendar-year-select] |
padding-inline |
var(--calendar-select-padding-inline) ⤴ prestado de calendar |
| 20 | range-calendar.css:130 |
[data-range-calendar-month-select], [data-range-calendar-year-select] |
border |
var(--calendar-control-border-width) solid var(--calendar-control-border) ⤴ prestado de calendar |
| 21 | range-calendar.css:131 |
[data-range-calendar-month-select], [data-range-calendar-year-select] |
border-radius |
var(--calendar-control-radius) ⤴ prestado de calendar |
| 22 | range-calendar.css:132 |
[data-range-calendar-month-select], [data-range-calendar-year-select] |
background |
var(--calendar-control-bg) ⤴ prestado de calendar |
| 23 | range-calendar.css:133 |
[data-range-calendar-month-select], [data-range-calendar-year-select] |
color |
var(--calendar-control-fg) ⤴ prestado de calendar |
| 24 | range-calendar.css:144 |
[data-range-calendar-month-select] |
min-inline-size |
var(--calendar-select-min-width) ⤴ prestado de calendar |
| 25 | range-calendar.css:145 |
[data-range-calendar-month-select] |
inline-size |
calc(var(--calendar-day-size) * 2.35) |
| 26 | range-calendar.css:149 |
[data-range-calendar-year-select] |
min-inline-size |
var(--calendar-select-min-width) ⤴ prestado de calendar |
| 27 | range-calendar.css:150 |
[data-range-calendar-year-select] |
inline-size |
calc(var(--calendar-day-size) * 2.15) |
| 28 | range-calendar.css:155 |
[data-range-calendar-month-select]:hover, [data-range-calendar-year-select]:hover |
border-color |
var(--calendar-hover-control-border) ⤴ prestado de calendar |
| 29 | range-calendar.css:156 |
[data-range-calendar-month-select]:hover, [data-range-calendar-year-select]:hover |
background-image |
linear-gradient(var(--state-hover), var(--state-hover)) |
| 30 | range-calendar.css:157 |
[data-range-calendar-month-select]:hover, [data-range-calendar-year-select]:hover |
color |
var(--calendar-hover-control-fg) ⤴ prestado de calendar |
| 31 | range-calendar.css:170 |
[data-range-calendar-months] |
gap |
var(--calendar-months-gap) ⤴ prestado de calendar |
| 32 | range-calendar.css:181 |
[data-range-calendar-month-panel] |
gap |
var(--calendar-grid-gap) ⤴ prestado de calendar |
| 33 | range-calendar.css:196 |
[data-range-calendar-head-cell] |
inline-size |
var(--calendar-day-size) |
| 34 | range-calendar.css:197 |
[data-range-calendar-head-cell] |
block-size |
var(--calendar-weekday-size) ⤴ prestado de calendar |
| 35 | range-calendar.css:198 |
[data-range-calendar-head-cell] |
color |
var(--calendar-weekday-fg) ⤴ prestado de calendar |
| 36 | range-calendar.css:199 |
[data-range-calendar-head-cell] |
font-size |
var(--calendar-weekday-font-size) ⤴ prestado de calendar |
| 37 | range-calendar.css:200 |
[data-range-calendar-head-cell] |
font-weight |
var(--calendar-weekday-font-weight) ⤴ prestado de calendar |
| 38 | range-calendar.css:205 |
[data-range-calendar-week-number] |
color |
var(--calendar-weekday-fg) ⤴ prestado de calendar |
| 39 | range-calendar.css:210 |
[data-range-calendar-week-number][scope='row'] |
block-size |
var(--calendar-day-size) |
| 40 | range-calendar.css:211 |
[data-range-calendar-week-number][scope='row'] |
font-size |
var(--calendar-weekday-font-size) ⤴ prestado de calendar |
| 41 | range-calendar.css:212 |
[data-range-calendar-week-number][scope='row'] |
font-weight |
var(--calendar-weekday-font-weight) ⤴ prestado de calendar |
| 42 | range-calendar.css:217 |
[data-range-calendar-cell] |
border-radius |
var(--calendar-day-radius) ⤴ prestado de calendar |
| 43 | range-calendar.css:232 |
[data-range-calendar-day] |
inline-size |
var(--calendar-day-size) |
| 44 | range-calendar.css:233 |
[data-range-calendar-day] |
block-size |
var(--calendar-day-size) |
| 45 | range-calendar.css:234 |
[data-range-calendar-day] |
border |
var(--calendar-day-border-width) solid var(--calendar-day-border) ⤴ prestado de calendar |
| 46 | range-calendar.css:235 |
[data-range-calendar-day] |
border-radius |
var(--calendar-day-radius) ⤴ prestado de calendar |
| 47 | range-calendar.css:236 |
[data-range-calendar-day] |
background |
var(--calendar-day-bg) ⤴ prestado de calendar |
| 48 | range-calendar.css:237 |
[data-range-calendar-day] |
color |
var(--calendar-day-fg) ⤴ prestado de calendar |
| 49 | range-calendar.css:239 |
[data-range-calendar-day] |
font-weight |
var(--calendar-day-font-weight) ⤴ prestado de calendar |
| 50 | range-calendar.css:251 |
[data-range-calendar-day]:hover |
border-color |
var(--calendar-hover-day-border) ⤴ prestado de calendar |
| 51 | range-calendar.css:252 |
[data-range-calendar-day]:hover |
background-image |
linear-gradient(var(--state-hover), var(--state-hover)) |
| 52 | range-calendar.css:253 |
[data-range-calendar-day]:hover |
color |
var(--calendar-hover-day-fg) ⤴ prestado de calendar |
| 53 | range-calendar.css:257 |
[data-range-calendar-day][data-selected] |
border-color |
var(--calendar-range-end-border) ⤴ prestado de calendar |
| 54 | range-calendar.css:258 |
[data-range-calendar-day][data-selected] |
background |
var(--calendar-range-end-solid) ⤴ prestado de calendar |
| 55 | range-calendar.css:259 |
[data-range-calendar-day][data-selected] |
color |
var(--calendar-day-selected-fg) ⤴ prestado de calendar |
| 56 | range-calendar.css:265 |
[data-range-calendar-day][data-range-start] |
border-color |
var(--calendar-range-start-border) ⤴ prestado de calendar |
| 57 | range-calendar.css:266 |
[data-range-calendar-day][data-range-start] |
background |
var(--calendar-range-start-solid) ⤴ prestado de calendar |
| 58 | range-calendar.css:267 |
[data-range-calendar-day][data-range-start] |
color |
var(--calendar-range-start-text) ⤴ prestado de calendar |
| 59 | range-calendar.css:268 |
[data-range-calendar-day][data-range-start] |
box-shadow |
inset 2px 0 0 0 var(--calendar-range-start-border) ⤴ prestado de calendar |
| 60 | range-calendar.css:274 |
[data-range-calendar-day][data-range-end] |
border-color |
var(--calendar-range-end-border) ⤴ prestado de calendar |
| 61 | range-calendar.css:275 |
[data-range-calendar-day][data-range-end] |
background |
var(--calendar-range-end-solid) ⤴ prestado de calendar |
| 62 | range-calendar.css:276 |
[data-range-calendar-day][data-range-end] |
color |
var(--calendar-range-end-text) ⤴ prestado de calendar |
| 63 | range-calendar.css:277 |
[data-range-calendar-day][data-range-end] |
box-shadow |
inset -2px 0 0 0 var(--calendar-range-end-border) ⤴ prestado de calendar |
| 64 | range-calendar.css:282 |
[data-range-calendar-day][data-range-start][data-range-end] |
border-color |
var(--calendar-range-end-border) ⤴ prestado de calendar |
| 65 | range-calendar.css:283 |
[data-range-calendar-day][data-range-start][data-range-end] |
background |
linear-gradient( 135deg, var(--calendar-range-start-solid) 0 50%, var(--calendar-range-end-solid) 50% 100% ) ⤴ prestado de calendar |
| 66 | range-calendar.css:288 |
[data-range-calendar-day][data-range-start][data-range-end] |
color |
var(--calendar-day-selected-fg) ⤴ prestado de calendar |
| 67 | range-calendar.css:292 |
[data-range-calendar-day][data-selected]:hover |
border-color |
var(--calendar-range-end-solid-hover) ⤴ prestado de calendar |
| 68 | range-calendar.css:293 |
[data-range-calendar-day][data-selected]:hover |
background |
var(--calendar-range-end-solid-hover) ⤴ prestado de calendar |
| 69 | range-calendar.css:297 |
[data-range-calendar-day][data-range-start]:hover |
border-color |
var(--calendar-range-start-solid-hover) ⤴ prestado de calendar |
| 70 | range-calendar.css:298 |
[data-range-calendar-day][data-range-start]:hover |
background |
var(--calendar-range-start-solid-hover) ⤴ prestado de calendar |
| 71 | range-calendar.css:302 |
[data-range-calendar-day][data-range-end]:hover |
border-color |
var(--calendar-range-end-solid-hover) ⤴ prestado de calendar |
| 72 | range-calendar.css:303 |
[data-range-calendar-day][data-range-end]:hover |
background |
var(--calendar-range-end-solid-hover) ⤴ prestado de calendar |
| 73 | range-calendar.css:307 |
[data-range-calendar-day][data-range-start][data-range-end]:hover |
background |
linear-gradient( 135deg, var(--calendar-range-start-solid-hover) 0 50%, var(--calendar-range-end-solid-hover) 50% 100% ) ⤴ prestado de calendar |
| 74 | range-calendar.css:335 |
[data-range-calendar-day][data-today]:not([data-selected]) |
border-color |
var(--calendar-day-today-border) ⤴ prestado de calendar |
| 75 | range-calendar.css:336 |
[data-range-calendar-day][data-today]:not([data-selected]) |
color |
var(--calendar-day-today-fg) ⤴ prestado de calendar |
| 76 | range-calendar.css:337 |
[data-range-calendar-day][data-today]:not([data-selected]) |
font-weight |
var(--calendar-day-today-font-weight) ⤴ prestado de calendar |
| 77 | range-calendar.css:341 |
[data-range-calendar-day][data-outside-month] |
color |
var(--calendar-day-outside-fg) ⤴ prestado de calendar |
| 78 | range-calendar.css:342 |
[data-range-calendar-day][data-outside-month] |
opacity |
var(--calendar-day-outside-opacity) ⤴ prestado de calendar |
| 79 | range-calendar.css:346 |
[data-range-calendar-day][data-weekend]:not([data-selected]) |
color |
var(--calendar-day-weekend-fg) ⤴ prestado de calendar |
| 80 | range-calendar.css:350 |
[data-range-calendar-day][data-holiday]:not([data-selected]) |
box-shadow |
var(--calendar-day-holiday-shadow) |
| 81 | range-calendar.css:355 |
[data-range-calendar-day][data-unavailable] |
text-decoration-color |
var(--calendar-day-unavailable-decoration) ⤴ prestado de calendar |
| 82 | range-calendar.css:361 |
[data-range-calendar-day][data-disabled], [data-range-calendar-day][data-unavailable] |
opacity |
var(--calendar-disabled-opacity) ⤴ prestado de calendar |
| 83 | range-calendar.css:365 |
[data-range-calendar][data-invalid] |
border-color |
var(--calendar-invalid-border) ⤴ prestado de calendar |
| 84 | range-calendar.css:369 |
[data-range-calendar][data-disabled] |
opacity |
var(--calendar-disabled-opacity) ⤴ prestado de calendar |
| 85 | range-calendar.css:385 |
[data-range-calendar-day][data-event], [data-range-calendar-prev-button][data-event], [data-range-calendar-next-button][data-event], [data-range-calendar-month-select][data-event], [data-range-calendar-year-select][data-event] |
box-shadow |
var(--calendar-event-shadow) |
3. Privados de la receta — ¿de dónde sale su valor?
La receta no declara privados propios en su CSS.
Consumidos y no declarados en el CSS (vienen de base.ts o de un estilo inline del wrapper): --_range-calendar-palette-border, --_range-calendar-palette-solid, --_range-calendar-palette-solid-hover, --_range-calendar-palette-text, --_range-calendar-palette-track.
4. Propuesta de corrección
- Consume la capa compartida
calendar-surface. Un eje que la capa posee se consume comovar(--_x, var(--x)); el consumidor no acuña--range-calendar-{eje}para él — sería un vocabulario paralelo (README deeidos/components, «Capas compartidas» regla 2). - Consume tokens públicos de
calendar. 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).
4.1 Tokens a declarar en lib/recipes/base.ts (4)
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 (--range-calendar-…) |
scope TSC | valor propuesto | usos |
|---|---|---|---|
day-font-weight |
root |
var(--font-weight-semibold) |
2 |
width |
root |
max-content |
1 |
month-panel-width |
root |
max-content |
1 |
grid-width |
root |
max-content |
1 |
4.2 Sin nombre mecánico (8)
- ⚠ 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: el privado que alimenta este knob no se declara en el CSS (viene de
base.tso de un estilo inline) — hay que resolverlo antes de nombrarlo — 7:background,color,border-color.
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. Cifras reproducidas con --only range-calendar
(98 · 0 · 7 · 84 · 4 · 3); filas muestreadas contra el CSS (:51 gap, :54
padding vía privado ajeno, :55 border shorthand) — coinciden, y las marcas
«⤴ prestado de calendar» son reales: la receta consume ~80 tokens públicos
de calendar y hasta privados suyos (--_calendar-day-size,
--_calendar-font-size, --_calendar-padding), y a la inversa PISA públicos
de calendar con privados propios (range-calendar.css:30:
--calendar-range-end-solid: var(--_range-calendar-palette-solid)).
Propuesta (§4.1): NO IMPLEMENTAR. Dos defectos verificados:
- Acuña 59 alias de
--calendar-*— exactamente lo que la nota de §4 prohíbe («no duplicarlo con prefijo propio») y lo que la purga de alias mató en §39 del changelog («no quiero alias»). Un tema que moviera--range-calendar-day-bg=var(--calendar-day-bg)no ganaría nada: el knob real seguiría siendo del calendar. - Los nombres colisionan porque el lector de estados no conoce los del
día:
day-fgcon 7 valores mezcladata-range-start,data-range-end,data-today,data-outside,data-weekend,data-selected— estados de la familia calendar que la derivación genérica no parsea. Ademásfont-size/day-width/day-heightproponen valores sobre privados AJENOS (--_calendar-*): un tokenrootque referencia un privado es la clase eager-freeze que G1 existe para impedir.
Disposición: este componente NO se tokeniza en solitario. Su corrección es
la decisión de familia calendar registrada en theming-audit.md §B
(«la familia calendar se formaliza como capa compartida/arquetipo»; los
préstamos medidos: range-calendar 79 · month/year-grid 65 · drp 38) — una capa
calendar-surface con contrato propio que los cinco consumidores lean, con la
misma regla que las demás capas compartidas (var(--_x, var(--x)), el
consumidor no acuña). Orden: va en el bloque B7 de F2 (D-TH.4), DESPUÉS de esa
decisión. Lo único accionable aquí y ahora si el autor lo firma: partir los 3
border shorthand en ejes lógicos y anotar los 4 literales.
Bloqueos de firma: decisión de familia calendar (sin firmar) · D-TH.4
(orden) · D-TH.2 (los max-content/100% de layout quedan fuera del knob).