15 KiB
color-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-23 · Alcance: 75% — 59 de 79 knobs por token público
- Knobs de apariencia: 82 — público 59 · privado 7 · global 2 · literal 11 · sistema 3 · excepción 0 (los dos últimos, fuera del ratio)
- Contrato hoy (
lib/recipes/base.ts): 71 pública(s) —gap,control-gap,label-fg,trigger-font-family,trigger-line-height,value-font-family,value-font-weight,value-fg,value-line-height,swatch-border-width,channel-track-border-width,channel-track-border,channel-thumb-border-width,channel-thumb-border,channel-thumb-shadow,swatch-ring-gap,eye-dropper-border-width,content-width-sm,content-width-md,content-width-lg,trigger-height-sm,trigger-height-md,trigger-height-lg,trigger-padding-inline-sm,trigger-padding-inline-md,trigger-padding-inline-lg,trigger-gap,trigger-radius,trigger-border-width,trigger-border,trigger-bg,trigger-fg,trigger-hover-border,trigger-swatch-size-sm,trigger-swatch-size-md,trigger-swatch-size-lg,trigger-swatch-radius,area-height,area-radius,area-border-width,area-border,area-thumb-size,area-thumb-radius,area-thumb-border-width,area-thumb-border,area-thumb-shadow,transparency-cell,transparency-cell-alt,swatch-size-sm,swatch-size-md,swatch-size-lg,swatch-radius,swatch-border,swatch-checked-ring,swatch-gap,eye-dropper-size,eye-dropper-radius,eye-dropper-bg,eye-dropper-fg,eye-dropper-hover-fg,hue-0,hue-60,hue-120,hue-180,hue-240,hue-300,hue-360,saturation-floor,transition-duration,transition-ease,disabled-opacity· 1 privada(s) forward —_palette-border - Eje
size: sí · ficheros:color-picker-spectrum.css,color-picker.css
1. Knobs fuera de alcance
1.1 Directo a primitivo global (2)
| # | fichero:línea | selector | propiedad | valor |
|---|---|---|---|---|
| 1 | color-picker.css:444 |
[data-color-picker-channel-slider][data-slider]::after |
block-size |
var(--_slider-track-size, var(--slider-track-size-md)) ⤴ prestado de slider |
| 2 | color-picker.css:446 |
[data-color-picker-channel-slider][data-slider]::after |
border-radius |
var(--slider-track-radius) ⤴ prestado de slider |
1.2 A través de un privado (7)
| # | fichero:línea | selector | propiedad | valor |
|---|---|---|---|---|
| 1 | color-picker.css:75 |
[data-color-picker-label] |
font-size |
calc(var(--_color-picker-trigger-font-size) - (var(--font-size-md) - var(--font-size-sm))) |
| 2 | color-picker.css:151 |
[data-color-picker-trigger] |
font-size |
var(--_color-picker-trigger-font-size) |
| 3 | color-picker.css:167 |
[data-color-picker-trigger]:focus-visible |
border-color |
var(--_color-picker-palette-border) |
| 4 | color-picker.css:222 |
[data-color-picker-value-swatch]::before |
background |
var(--_color-picker-current-color, transparent) |
| 5 | color-picker.css:233 |
[data-color-picker-value-text] |
font-size |
var(--_color-picker-trigger-font-size, var(--font-size-xs)) |
| 6 | color-picker.css:431 |
[data-color-picker-channel-slider][data-slider]::before |
background |
var(--_color-picker-channel-gradient, var(--slider-track-bg)) !important ⤴ prestado de slider |
| 7 | color-picker.css:605 |
[data-color-picker-eye-dropper]:focus-visible |
box-shadow |
0 0 0 2px var(--_color-picker-accent) |
1.3 Literales (11)
| # | fichero:línea | selector | propiedad | valor |
|---|---|---|---|---|
| 1 | color-picker.css:40 |
[data-color-picker] |
inline-size |
100% |
| 2 | color-picker.css:250 |
[data-color-picker-area] |
inline-size |
100% |
| 3 | color-picker.css:290 |
[data-color-picker-area-background]::before |
background |
linear-gradient(to right, white, transparent) |
| 4 | color-picker.css:299 |
[data-color-picker-area-background]:dir(rtl)::before |
background |
linear-gradient(to left, white, transparent) |
| 5 | color-picker.css:304 |
[data-color-picker-area-background]::after |
background |
linear-gradient(to top, black, transparent) |
| 6 | color-picker.css:368 |
[data-color-picker-area][data-x-channel='hue'][data-y-channel='brightness'] [data-color-picker-area-background]::after |
background |
linear-gradient(to top, black, transparent) |
| 7 | color-picker.css:425 |
[data-color-picker-channel-slider][data-slider] |
inline-size |
100% |
| 8 | color-picker.css:460 |
[data-color-picker-channel-slider][data-slider] [data-slider-range] |
background |
transparent !important |
| 9 | color-picker.css:504 |
[data-color-picker-swatch-group] |
inline-size |
100% |
| 10 | color-picker.css:568 |
[data-color-picker-swatch-indicator] |
inline-size |
0.5rem |
| 11 | color-picker.css:569 |
[data-color-picker-swatch-indicator] |
block-size |
0.5rem |
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 (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 | color-picker.css:162 |
[data-color-picker-trigger]:hover:not([data-disabled]) |
background-image |
linear-gradient(var(--state-hover), var(--state-hover)) |
| 2 | color-picker.css:169 |
[data-color-picker-trigger]:focus-visible |
outline |
var(--focus-ring-width) solid var(--focus-ring-color) |
| 3 | color-picker.css:599 |
[data-color-picker-eye-dropper]:hover:not([data-disabled]):not([data-unsupported]) |
background-image |
linear-gradient(var(--state-hover), var(--state-hover)) |
3. Privados de la receta — ¿de dónde sale su valor?
| privado | declaraciones | valor(es) | origen | ¿deriva de un público? |
|---|---|---|---|---|
--_color-picker-content-width |
3 | var(--color-picker-content-width-md), var(--color-picker-content-width-sm), var(--color-picker-content-width-lg) |
public | sí |
--_color-picker-trigger-height |
3 | var(--color-picker-trigger-height-md), var(--color-picker-trigger-height-sm), var(--color-picker-trigger-height-lg) |
public | sí |
--_color-picker-trigger-padding-inline |
3 | var(--color-picker-trigger-padding-inline-md), var(--color-picker-trigger-padding-inline-sm), var(--color-picker-trigger-padding-inline-lg) |
public | sí |
--_color-picker-trigger-swatch-size |
3 | var(--color-picker-trigger-swatch-size-md), var(--color-picker-trigger-swatch-size-sm), var(--color-picker-trigger-swatch-size-lg) |
public | sí |
--_color-picker-trigger-font-size |
3 | var(--font-size-md), var(--font-size-sm), var(--font-size-lg) |
global | no |
--_color-picker-swatch-size |
4 | var(--color-picker-swatch-size-md), var(--color-picker-swatch-size-sm), var(--color-picker-swatch-size-lg) |
public | sí |
--_color-picker-checker-cell |
2 | 6px |
literal | no |
--_color-picker-checker |
2 | linear-gradient(45deg, var(--color-picker-transparency-cell) 25%, transparent 25%), linear-gradient(-45deg, var(--color-picker-transparency-cell) 25%, transparent 25%), linear-gradient(45deg, transparent 75%, var(--color-picker-transparency-cell) 75%), linear-gradient(-45deg, transparent 75%, var(--color-picker-transparency-cell) 75%) |
public | sí |
--_color-picker-accent |
1 | var(--_color-picker-palette-border) |
private | no |
Consumidos y no declarados en el CSS (vienen de base.ts o de un estilo inline del wrapper): --_color-picker-channel-gradient, --_color-picker-current-color, --_color-picker-palette-border.
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--color-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--color-picker-{eje}para él — sería un vocabulario paralelo (README deeidos/components, «Capas compartidas» regla 2). - Consume tokens públicos de
slider. 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 (17)
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 (--color-picker-…) |
scope TSC | valor propuesto | usos |
|---|---|---|---|
area-background-bg |
root |
⚠ linear-gradient(to right, white, transparent) / linear-gradient(to left, white, transparent) / linear-gradient(to top, black, transparent) |
3 |
label-font-size-md |
size:md |
var(--font-size-md) |
1 |
label-font-size-sm |
size:sm |
var(--font-size-sm) |
1 |
label-font-size-lg |
size:lg |
var(--font-size-lg) |
1 |
trigger-font-size-md |
size:md |
var(--font-size-md) |
1 |
trigger-font-size-sm |
size:sm |
var(--font-size-sm) |
1 |
trigger-font-size-lg |
size:lg |
var(--font-size-lg) |
1 |
value-text-font-size-md |
size:md |
var(--font-size-md) |
1 |
value-text-font-size-sm |
size:sm |
var(--font-size-sm) |
1 |
value-text-font-size-lg |
size:lg |
var(--font-size-lg) |
1 |
area-bg |
root |
linear-gradient(to top, black, transparent) |
1 |
channel-slider-height |
root |
var(--_slider-track-size, var(--slider-track-size-md)) |
1 |
channel-slider-radius |
root |
var(--slider-track-radius) |
1 |
channel-slider-bg |
root |
transparent !important |
1 |
swatch-indicator-width |
root |
0.5rem |
1 |
swatch-indicator-height |
root |
0.5rem |
1 |
eye-dropper-shadow |
root |
var(--_color-picker-palette-border) |
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 — 4: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 — 3:border-color,background.
4.3 Avisos sobre los tokens propuestos (2)
- el privado
--_color-picker-trigger-font-sizedebe pasar a leer este público (o desaparecer) —--color-picker-label-font-size-md,--color-picker-label-font-size-sm,--color-picker-label-font-size-lg,--color-picker-trigger-font-size-md,--color-picker-trigger-font-size-sm,--color-picker-trigger-font-size-lg,--color-picker-value-text-font-size-md,--color-picker-value-text-font-size-sm,--color-picker-value-text-font-size-lg - el privado
--_color-picker-accentdebe pasar a leer este público (o desaparecer) —--color-picker-eye-dropper-shadow
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 — 51 % → 75 %: 20 claves de costura y un PREFIJO ABREVIADO retirado de todo el árbol.
--cp-* era un prefijo abreviado, que theming §6 r5 prohíbe («no
abrevies el nombre del componente»). Cinco nombres —checker,
checker-cell, current-color, channel-gradient, trigger-color—
y 45 ocurrencias en 8 ficheros, tres de ellos de SOMA (el provider los
estampa inline, su test los lee y sus tipos los declaran). Renombrados a
--_color-picker-*: son privados de la receta, y dos de ellos —el damero y
el gradiente de canal— ya derivaban de un público, así que el censo los
reclasifica solo. Precedente: los --gb-* / --_ntp-* de F2-A.
Lo cosido: el ritmo del componente (gap, control-gap), la tinta de la
etiqueta, la tipografía del disparador y la del VALOR —que es metadato: mono,
regular, con su propia tinta y su interlineado—, el borde de la muestra, el
raíl del canal y su pulgar, el hueco del anillo de la muestra y el borde del
cuentagotas. Veinte claves.
Una sombra que es DOS y hace una sola cosa: el pulgar del canal lleva
contorno + elevación en un solo box-shadow; se acuña como UN token
(channel-thumb-shadow) y la regla de foco lo compone con su tercer anillo,
en vez de repetir las dos sombras.
El instrumento no veía DOS familias de nodo, y las dos se arreglaron
midiendo: las muestras del picker son <ColorSwatch> (llevan
data-color-swatch, no data-color-picker-*) y sus sliders de canal son
SLIDERS (el pulgar y el raíl llevan data-slider-*). Cuatro tokens vivos
leían muertos por eso.
Lo que queda rojo, y es ANTERIOR a este commit: el guard reporta 23 tokens
sin adjudicar, todos del bloque que ya existía —las muestras (7), la rueda de
tono (hue-0..360 + saturation-floor), el ancho del panel por talla, el
cuentagotas y las dos de transición—. Antes de este commit eran 24, así que
la cifra baja; su adjudicación es un pase propio y queda anotada en
next-features §13. Lo que YO añadí está medido: dos adjudicadas montando el
disparador de muestra dentro del panel abierto, el resto en vivo.
Sonda antes/después: 0 diffs en 1.350 valores (8 estados) — medida dos veces, una tras el renombrado y otra tras la costura. Tests de soma del color-picker: 7/7 tras el renombrado.