16 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-22 · Alcance: 51% — 40 de 79 knobs por token público
- Knobs de apariencia: 82 — público 40 · privado 6 · global 22 · literal 11 · sistema 3 (fuera del ratio)
- Contrato hoy (
lib/recipes/base.ts): 54 pública(s) —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 (22)
| # | fichero:línea | selector | propiedad | valor |
|---|---|---|---|---|
| 1 | color-picker.css:39 |
[data-color-picker] |
gap |
var(--space-1-5) |
| 2 | color-picker.css:68 |
[data-color-picker-label] |
color |
var(--color-content-primary) |
| 3 | color-picker.css:81 |
[data-color-picker-control] |
gap |
var(--space-2) |
| 4 | color-picker.css:150 |
[data-color-picker-trigger] |
font-family |
var(--style-label-font-family) |
| 5 | color-picker.css:152 |
[data-color-picker-trigger] |
line-height |
var(--leading-ui) |
| 6 | color-picker.css:207 |
[data-color-picker-value-swatch] |
background-image |
var(--cp-checker) |
| 7 | color-picker.css:222 |
[data-color-picker-value-swatch]::before |
background |
var(--cp-current-color, transparent) |
| 8 | color-picker.css:232 |
[data-color-picker-value-text] |
font-family |
var(--font-family-mono, var(--style-label-font-family)) |
| 9 | color-picker.css:235 |
[data-color-picker-value-text] |
font-weight |
var(--font-weight-regular) |
| 10 | color-picker.css:236 |
[data-color-picker-value-text] |
color |
var(--color-content-primary) |
| 11 | color-picker.css:243 |
[data-color-picker-value-text] |
line-height |
var(--leading-ui) |
| 12 | color-picker.css:431 |
[data-color-picker-channel-slider][data-slider]::before |
background |
var(--cp-channel-gradient, var(--slider-track-bg)) !important ⤴ prestado de slider |
| 13 | color-picker.css:432 |
[data-color-picker-channel-slider][data-slider]::before |
border |
var(--border-width) solid var(--color-border-subtle) |
| 14 | 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 |
| 15 | color-picker.css:446 |
[data-color-picker-channel-slider][data-slider]::after |
border-radius |
var(--slider-track-radius) ⤴ prestado de slider |
| 16 | color-picker.css:447 |
[data-color-picker-channel-slider][data-slider]::after |
background-image |
var(--cp-checker) |
| 17 | color-picker.css:466 |
[data-color-picker-channel-slider] [data-slider-thumb] |
border |
var(--border-width-medium) solid var(--color-surface-default) |
| 18 | color-picker.css:467 |
[data-color-picker-channel-slider] [data-slider-thumb] |
box-shadow |
0 0 0 1px color-mix(in srgb, var(--color-neutral-contrast) 45%, transparent), 0 1px 2px color-mix(in srgb, var(--color-neutral-contrast) 20%, transparent) |
| 19 | color-picker.css:484 |
[data-color-picker-transparency-grid] |
background-image |
var(--cp-checker) |
| 20 | color-picker.css:525 |
[data-color-picker-swatch-trigger] |
background-image |
var(--cp-checker) |
| 21 | color-picker.css:575 |
[data-color-picker-swatch-indicator] |
box-shadow |
0 0 0 1px var(--color-surface-default) |
| 22 | color-picker.css:591 |
[data-color-picker-eye-dropper] |
border |
var(--border-width) solid transparent |
1.2 A través de un privado (6)
| # | 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:233 |
[data-color-picker-value-text] |
font-size |
var(--_color-picker-trigger-font-size, var(--font-size-xs)) |
| 5 | color-picker.css:474 |
[data-color-picker-channel-slider] [data-slider-thumb]:focus-visible |
box-shadow |
0 0 0 1px color-mix(in srgb, var(--color-neutral-contrast) 45%, transparent), 0 1px 2px color-mix(in srgb, var(--color-neutral-contrast) 20%, transparent), 0 0 0 3px var(--_color-picker-accent) |
| 6 | color-picker.css:608 |
[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:507 |
[data-color-picker-swatch-group] |
inline-size |
100% |
| 10 | color-picker.css:571 |
[data-color-picker-swatch-indicator] |
inline-size |
0.5rem |
| 11 | color-picker.css:572 |
[data-color-picker-swatch-indicator] |
block-size |
0.5rem |
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:602 |
[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-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-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 (33)
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 |
channel-slider-bg |
root |
⚠ var(--cp-channel-gradient, var(--slider-track-bg)) !important / transparent !important |
2 |
channel-slider-shadow |
root |
⚠ 0 0 0 1px color-mix(in srgb, var(--color-neutral-contrast) 45%, transparent), 0 1px 2px color-mix(in srgb, var(--color-neutral-contrast) 20%, transparent) / var(--_color-picker-palette-border) |
2 |
gap |
root |
var(--space-1-5) |
1 |
label-fg |
root |
var(--color-content-primary) |
1 |
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 |
control-gap |
root |
var(--space-2) |
1 |
trigger-font-family |
root |
var(--style-label-font-family) |
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 |
trigger-line-height |
root |
var(--leading-ui) |
1 |
value-swatch-bg-image |
root |
var(--cp-checker) |
1 |
value-swatch-bg |
root |
var(--cp-current-color, transparent) |
1 |
value-text-font-family |
root |
var(--font-family-mono, var(--style-label-font-family)) |
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 |
value-text-font-weight |
root |
var(--font-weight-regular) |
1 |
value-text-fg |
root |
var(--color-content-primary) |
1 |
value-text-line-height |
root |
var(--leading-ui) |
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-image |
root |
var(--cp-checker) |
1 |
transparency-grid-bg-image |
root |
var(--cp-checker) |
1 |
swatch-trigger-bg-image |
root |
var(--cp-checker) |
1 |
swatch-indicator-width |
root |
0.5rem |
1 |
swatch-indicator-height |
root |
0.5rem |
1 |
swatch-indicator-shadow |
root |
0 0 0 1px var(--color-surface-default) |
1 |
eye-dropper-shadow |
root |
var(--_color-picker-palette-border) |
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 — 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 — 1:border-color. - ⚠ 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 (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-channel-slider-shadow,--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
(pendiente — lo escribe el autor; se conserva al regenerar)