15 KiB
chart — 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-22 · Alcance: 49% — 45 de 91 knobs por token público
- Knobs de apariencia: 95 — público 45 · privado 0 · global 36 · literal 10 · sistema 4 (fuera del ratio)
- Contrato hoy (
lib/recipes/base.ts): 39 pública(s) —line-width,axis-width,grid-width,bar-radius,point-radius,area-opacity,slice-stroke-width,transition-duration,transition-ease,tooltip-bg,tooltip-border,tooltip-radius,tooltip-shadow,tooltip-padding,tooltip-gap,crosshair-fg,crosshair-width,axis-fg,grid-fg,separator-fg,smith-boundary-fg,track-bg,track-height,track-radius,gap,legend-row-gap,legend-column-gap,legend-item-gap,swatch-size,swatch-radius,tooltip-swatch-size,bar-list-row-gap,bar-list-column-gap,bar-segment-gap,bar-segment-track-gap,bar-segment-track-radius,bar-segment-fill-min-inline-size,bar-segment-legend-row-gap,bar-segment-legend-column-gap - Eje
size: no · ficheros:chart.css
1. Knobs fuera de alcance
1.1 Directo a primitivo global (36)
| # | fichero:línea | selector | propiedad | valor |
|---|---|---|---|---|
| 1 | chart.css:46 |
[data-chart-empty] |
color |
var(--color-content-secondary) |
| 2 | chart.css:47 |
[data-chart-empty] |
font-size |
var(--style-label-font-size) |
| 3 | chart.css:58 |
[data-chart-axis-label] |
fill |
var(--color-content-secondary) |
| 4 | chart.css:59 |
[data-chart-axis-label] |
font-size |
var(--style-caption-font-size) |
| 5 | chart.css:133 |
[data-chart-tooltip] |
font-size |
var(--style-caption-font-size) |
| 6 | chart.css:134 |
[data-chart-tooltip] |
color |
var(--color-content-primary) |
| 7 | chart.css:141 |
[data-chart-tooltip-x] |
font-weight |
var(--font-weight-medium) |
| 8 | chart.css:142 |
[data-chart-tooltip-x] |
color |
var(--color-content-primary) |
| 9 | chart.css:148 |
[data-chart-tooltip-row] |
color |
var(--color-content-secondary) |
| 10 | chart.css:159 |
[data-chart-tooltip-value] |
color |
var(--color-content-primary) |
| 11 | chart.css:176 |
[data-chart-title] |
font-size |
var(--style-label-font-size) |
| 12 | chart.css:177 |
[data-chart-title] |
font-weight |
var(--font-weight-medium) |
| 13 | chart.css:178 |
[data-chart-title] |
color |
var(--color-content-primary) |
| 14 | chart.css:193 |
[data-chart-legend-item] |
font-size |
var(--style-caption-font-size) |
| 15 | chart.css:194 |
[data-chart-legend-item] |
color |
var(--color-content-secondary) |
| 16 | chart.css:224 |
[data-chart-bar-list-label] |
font-size |
var(--style-label-font-size) |
| 17 | chart.css:225 |
[data-chart-bar-list-label] |
color |
var(--color-content-primary) |
| 18 | chart.css:242 |
[data-chart-bar-list-value] |
font-size |
var(--style-label-font-size) |
| 19 | chart.css:243 |
[data-chart-bar-list-value] |
color |
var(--color-content-secondary) |
| 20 | chart.css:283 |
[data-chart-bar-segment-legend-item] |
font-size |
var(--style-caption-font-size) |
| 21 | chart.css:284 |
[data-chart-bar-segment-legend-item] |
color |
var(--color-content-secondary) |
| 22 | chart.css:287 |
[data-chart-bar-segment-legend-value] |
color |
var(--color-content-primary) |
| 23 | chart.css:335 |
[data-chart-smith-label] |
fill |
var(--color-content-secondary) |
| 24 | chart.css:336 |
[data-chart-smith-label] |
font-size |
var(--style-caption-font-size) |
| 25 | chart.css:345 |
[data-chart-smith-point-label] |
fill |
var(--color-content-primary) |
| 26 | chart.css:346 |
[data-chart-smith-point-label] |
font-size |
var(--style-caption-font-size) |
| 27 | chart.css:347 |
[data-chart-smith-point-label] |
font-weight |
var(--font-weight-medium) |
| 28 | chart.css:370 |
[data-chart-funnel-label] |
font-size |
var(--style-label-font-size) |
| 29 | chart.css:371 |
[data-chart-funnel-label] |
font-weight |
var(--font-weight-medium) |
| 30 | chart.css:380 |
[data-chart-funnel-side] |
font-size |
var(--style-label-font-size) |
| 31 | chart.css:383 |
[data-chart-funnel-name] |
fill |
var(--color-content-primary) |
| 32 | chart.css:384 |
[data-chart-funnel-name] |
font-weight |
var(--font-weight-medium) |
| 33 | chart.css:387 |
[data-chart-funnel-meta] |
fill |
var(--color-content-secondary) |
| 34 | chart.css:399 |
[data-chart-gauge-label] |
fill |
var(--color-content-primary) |
| 35 | chart.css:400 |
[data-chart-gauge-label] |
font-size |
var(--font-size-xl) |
| 36 | chart.css:401 |
[data-chart-gauge-label] |
font-weight |
var(--font-weight-medium) |
1.2 A través de un privado (0)
Ninguno.
1.3 Literales (10)
| # | fichero:línea | selector | propiedad | valor |
|---|---|---|---|---|
| 1 | chart.css:25 |
[data-chart-frame] |
inline-size |
100% |
| 2 | chart.css:29 |
[data-chart-plot] |
inline-size |
100% |
| 3 | chart.css:35 |
[data-chart] |
inline-size |
100% |
| 4 | chart.css:45 |
[data-chart-empty] |
block-size |
100% |
| 5 | chart.css:135 |
[data-chart-tooltip] |
min-inline-size |
max-content |
| 6 | chart.css:171 |
[data-chart-figure] |
inline-size |
100% |
| 7 | chart.css:215 |
[data-chart-bar-list] |
inline-size |
100% |
| 8 | chart.css:237 |
[data-chart-bar-list-bar] |
block-size |
100% |
| 9 | chart.css:254 |
[data-chart-bar-segment] |
inline-size |
100% |
| 10 | chart.css:264 |
[data-chart-bar-segment-fill] |
block-size |
100% |
2. Sistema transversal (4) — 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 | chart.css:271 |
[data-chart-bar-segment-fill][data-dim='true'] |
opacity |
var(--opacity-scrim) |
| 2 | chart.css:313 |
[data-chart-heat-cell][data-dim='true'] |
opacity |
var(--opacity-scrim) |
| 3 | chart.css:321 |
[data-chart-cal-cell][data-dim='true'] |
opacity |
var(--opacity-scrim) |
| 4 | chart.css:361 |
[data-chart-polar-petal][data-dim='true'] |
opacity |
var(--opacity-scrim) |
3. Privados de la receta — ¿de dónde sale su valor?
La receta no declara privados propios en su CSS.
4. Propuesta de corrección
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 (--chart-…) |
scope TSC | valor propuesto | usos |
|---|---|---|---|
empty-fg |
root |
var(--color-content-secondary) |
1 |
empty-font-size |
root |
var(--style-label-font-size) |
1 |
axis-label-fill |
root |
var(--color-content-secondary) |
1 |
axis-label-font-size |
root |
var(--style-caption-font-size) |
1 |
tooltip-font-size |
root |
var(--style-caption-font-size) |
1 |
tooltip-fg |
root |
var(--color-content-primary) |
1 |
tooltip-width |
root |
max-content |
1 |
tooltip-x-font-weight |
root |
var(--font-weight-medium) |
1 |
tooltip-x-fg |
root |
var(--color-content-primary) |
1 |
tooltip-row-fg |
root |
var(--color-content-secondary) |
1 |
tooltip-value-fg |
root |
var(--color-content-primary) |
1 |
title-font-size |
root |
var(--style-label-font-size) |
1 |
title-font-weight |
root |
var(--font-weight-medium) |
1 |
title-fg |
root |
var(--color-content-primary) |
1 |
legend-item-font-size |
root |
var(--style-caption-font-size) |
1 |
legend-item-fg |
root |
var(--color-content-secondary) |
1 |
bar-list-label-font-size |
root |
var(--style-label-font-size) |
1 |
bar-list-label-fg |
root |
var(--color-content-primary) |
1 |
bar-list-value-font-size |
root |
var(--style-label-font-size) |
1 |
bar-list-value-fg |
root |
var(--color-content-secondary) |
1 |
bar-segment-legend-item-font-size |
root |
var(--style-caption-font-size) |
1 |
bar-segment-legend-item-fg |
root |
var(--color-content-secondary) |
1 |
bar-segment-legend-value-fg |
root |
var(--color-content-primary) |
1 |
smith-label-fill |
root |
var(--color-content-secondary) |
1 |
smith-label-font-size |
root |
var(--style-caption-font-size) |
1 |
smith-point-label-fill |
root |
var(--color-content-primary) |
1 |
smith-point-label-font-size |
root |
var(--style-caption-font-size) |
1 |
smith-point-label-font-weight |
root |
var(--font-weight-medium) |
1 |
funnel-label-font-size |
root |
var(--style-label-font-size) |
1 |
funnel-label-font-weight |
root |
var(--font-weight-medium) |
1 |
funnel-side-font-size |
root |
var(--style-label-font-size) |
1 |
funnel-name-fill |
root |
var(--color-content-primary) |
1 |
funnel-name-font-weight |
root |
var(--font-weight-medium) |
1 |
funnel-meta-fill |
root |
var(--color-content-secondary) |
1 |
gauge-label-fill |
root |
var(--color-content-primary) |
1 |
gauge-label-font-size |
root |
var(--font-size-xl) |
1 |
gauge-label-font-weight |
root |
var(--font-weight-medium) |
1 |
4.2 Sin nombre mecánico (9)
- ⚠ 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 — 9:inline-size,block-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, 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
EJECUTADO 2026-08-22 — 9 % → 49 %, 22 claves nuevas fusionadas en el bloque existente (17 → 39). El 49 % es el techo de este diseño, no un trabajo a medias: lo que queda fuera está medido y cada clase tiene su razón escrita abajo.
La propuesta §4 pedía 71 claves y habría escrito ~20 con el MISMO valor. La
medición que lo decidió: las 84 filas fuera de alcance colapsaban a 26 valores
distintos — 9× content-secondary, 9× content-primary, 9× 100%, 6×
font-size-sm, 6× font-size-xs, 6× font-weight-medium, 5×
surface-default, 4× radius-sm, 3× border-default. Nombrar por parte
habría dado cinco claves para el pelo que separa marcas y nueve para la tinta
de anotación. Es la inflación que F2-B ya cazó en spinner, con otra forma.
El texto NO recibe tokens de chart, por doctrina. Toma su ROL DE TAMAÑO de
los estilos con nombre —caption (xs) para las anotaciones y label (sm) para
el texto de cromo— y su TINTA y PESO de las ranuras del sistema
(--color-content-*, --font-weight-*). Acuñar --chart-label-font-size
sería inventar tipografía por componente, que es exactamente lo que el autor
prohibió el mismo día para la familia text-effects. Precedente de consumir la
capa: code-block, kbd, announce. Doce declaraciones cambian de
var(--font-size-xs|sm) a var(--style-caption|label-font-size), mismo valor.
Consecuencia medida en la métrica: esas doce y las veinte de tinta/peso
siguen contando como global, porque el censo sólo exime --style-* a los
PRIMITIVOS tipográficos y chart no lo es. El censo penaliza aquí hacer lo
correcto — la misma nota que dejó listbox en F2-A (§13).
Lo que la receta sí posee: la tinta de lo que no es texto — cinco claves,
cada una un concepto que el CSS repite: axis-fg (eje + rejilla smith +
conector del funnel), grid-fg (rejilla + radar), separator-fg (el pelo de
superficie entre marcas contiguas: porción, punto activo, punto smith, pétalo,
segmento), smith-boundary-fg (el borde del disco, más fuerte a propósito) y
track-bg (raíl del bar-list y del gauge). Más 17 de geometría de cromo.
Un incumplimiento heredado, corregido: el CSS leía
var(--control-height-sm, var(--space-7)) — coordenada CRUDA, que
recipe-css-contract prohíbe en un token de receta, con un respaldo muerto (el
primitivo existe). El contrato guarda var(--size-sm-control-height), el
bundle, que es 1:1 con él.
Fuera de contrato por identidad (los 10 literales): nueve 100% que
significan «ocupa a tu padre» y el min-inline-size: max-content del tooltip,
que es la corrección de composición de la misma clase ya adjudicada en
date-picker. Moverlos no es tematizar, es romper el layout.
Una escotilla sin cerrar: el --font-size-xl del número del gauge. Ningún
estilo con nombre juega ese papel (un readout de dato no es un h2) y acuñar
gauge-label-font-size sería tipografía por componente. Anotado en §13.
El instrumento estaba ciego de tres maneras, y las tres se arreglaron ANTES de medir (ninguna era culpa de los tokens):
- La superficie de chart vive en DIECISÉIS rutas de demo, una por tipo de
gráfico:
/chartmonta una línea con área y nada más (58 nodos de 302 en la página). El guard ganóurls— un token está VIVO si alguna superficie del componente lo sigue— y la sonda se corrió por las dieciséis. - El guard no leía NINGUNA propiedad SVG. Sin
fill/stroke/strokeWidth/fillOpacity/r/rx, doce de las 39 claves de un componente que dibuja en SVG leían muertas estando vivas. Misma clase que el hueco defilter/backdrop-filterdel día anterior. - El valor centinela se elegía por SUBCADENA:
slice-stroke-widthrecibíargb(1, 2, 3)por contener «stroke», valor inválido para una longitud, y leía muerta. Ahora la dimensión se decide por el final del nombre.
Con eso: sonda 0 diffs en 161.850 valores computados (16 rutas × 7 estados)
y centinela 37/39, con transition-duration y -ease adjudicadas y
medidas sin la congelación (0.12s → 11.5s · cubic-bezier → steps(4)).