13 KiB
| title | type | audience | status | date |
|---|---|---|---|---|
| Handoff — Abrir la jaula del color (`color` = sistema completo en todos los componentes) | process | human + agent | en curso — Fase 0/1/2a/2b ✅ · Fase 3 mayoría ✅ · cola de ~11 standalones + Fase 4/5 pendientes | 2026-07-18 (actualizado tras la sesión de continuación, 7 commits) |
Handoff — Abrir la jaula del color
Objetivo
El prop color de un componente debe aceptar todo el sistema de color del
ecosistema — por role, por intent, por paleta (33 escalas donantes) y por valor
(raw CSS) — en TODOS los componentes. Decisión de diseño del usuario
(2026-07-18): revierte la restricción de THM-2 (2026-07-12) que dejaba los
controles semánticos en subconjuntos; la jaula se abre siempre, sin
excepciones (incluida la tinta de contenido).
Doctrina de respaldo: theming/reference.md §25 (THM-2)
- §39 —
color= IDENTIDAD (role/scale/custom), desacoplada de la evaluación (que vive eninvalid+ elintentdel evento).
⚡ Empieza aquí (nueva sesión)
- Lee este doc entero + la memoria
project-open-color-cage-2026-07-18. - El motor y el patrón están LISTOS y probados. Lo que queda es ~11 componentes standalone HETEROGÉNEOS (§Cola). NO son un batch uniforme: cada uno rompe el patrón de una forma distinta y con su propia trampa.
- Verifica el estado:
npm run check(⚠️ el total oscila 76↔94, NO es baseline estable; verifica CERO errores en los componentes que toques, no el total). - Arranca por los MENOS peligrosos de la cola (skeleton/spinner/textarea), no por los portalados (color-picker/float-panel).
Estado — HECHO
Motor (Fase 0) ✅ — la costura compartida, LISTA
ComponentColorProp(src/uix/eidos/lib/types.ts) =ComponentColor | (string & {})— el tipo canónico ABIERTO al que todos apuntan.resolveComponentColor(color)(src/uix/eidos/lib/component-color.ts) — el helper del wrapper:{ dataColor, isCustom, customStyle }. Canónico →data-color; valor CSS crudo →data-color-custom+ seed--color-custom.- Derivación custom compartida (
render-css.tsrenderSharedPaletteLayer):[data-color-custom]deriva los 10 slots--palette-*desdevar(--color-custom)porcolor-mix. El forwardrenderRecipePaletteForwardemite[data-{c}][data-color], [data-{c}][data-color-custom]→ toda receta con tokens_palette-*gana roles + 33 escalas + custom con CERO CSS extra. - Guard
recipe-css-contract.test.tscon la forma del forward + invariante custom.
Fase 1/2a/2b ✅ (commit 149c0fef6, pre-continuación)
- Eidos-wrapper (14): radio-group·checkbox·stepper·toggle-group·select·badge· editable·file-upload·tag-group·tags-input·surface·avatar·card. Ref: checkbox.
- Soma-routed (button/switch/toggle):
colorpor soma/morfo, dos ejes intent+color. Ref: button. (2 bugs de framework arreglados:html-presenceconv.literal('')→ usarv.propRef; custom pisaba intent evaluativo → anular.)
Fase 3 — familias (esta sesión, 7 commits) ✅
| Commit | Componentes |
|---|---|
861427fe1 |
Field family (field base + search/password/mask/number/css/color/date/date-range/time-field) + calendar + month-grid + time-picker/time-range-picker + fix regresión runtime + 9 fixes de tipo (tests switch/toggle colorCustom) |
4adcc4c7f |
sweep custom-threading: 12 sub-partes date/time (trigger/calendar/clock/input/root) por resolveComponentColor |
889b03dae |
year-grid + range-calendar |
c5699bd01 |
familia lista/data-grid (10): table·tree-grid·tree-view·virtual-list·virtual-grid·feed·drag-drop·grid-list·clipboard·carousel |
528ddaa7f |
listbox + timeline + combobox |
1efa03439 |
link + mark (tinta de contenido) |
28aacf092 |
button-group + highlight (delegan a Button/Mark) |
Cada familia verificada en Chrome (role · escala · custom → token compartido exacto).
Los PATRONES (elige el correcto por componente)
Antes de tocar un componente, identifica su patrón:
A. Eidos-wrapper (estampa data-color directo). Ref: checkbox. Ya tiene
forward-ready recipe → solo tipo + wrapper resolveComponentColor.
B. Soma-routed (color por soma+morfo). Ref: button. Toca soma types/provider/
component + morfo + wrapper. Dos ejes intent+color.
C. Mini-recipe (cascada --_X-accent per-rol → forward). Ref: month-grid. El
patrón dominante de Fase 3:
- Recipe (
lib/recipes/base.ts): añade tokens_palette-{slot}(host = token de rol actual, normalmente primary → behavior-preserving). - CSS: quita las decls base
--_X-accent-*+ los bloques per-rol[data-X][data-color='rol']; renombra los consumidoresvar(--_X-accent-*)→var(--_X-palette-*). - types:
XColor→ComponentColorProp. - wrapper:
resolveComponentColor(estampa el trío +style). npm run generate:eidos-css+ verifica el forward engenerated/base.css.
D. Tinta de contenido (color:/background: per-rol directo). Ref: link
(1 slot text) / mark (element+text). Reemplaza la cascada color: var(--color-{rol}-text)
por color: var(--_X-palette-text) + recipe _palette-text.
E. Delegante (pasa el color a un hijo ya abierto). Ref: button-group
(contexto→Button) / highlight (→Mark). Solo tipo + wrapper.
⚠️ TRAMPAS (todas cazadas esta sesión — NO repetir)
parts:SOLO se lee en la formadeclarations: [...], no en{ value, scope }(render-css.tsexpandToDeclarationsWithPartsl.2183). Si la cascada vive en[data-X-root]o[data-X-content](no el provider bare), el recipe necesita'_palette-slot': { parts: ['root'], declarations: [{ value, scope: 'host' }] }. Verifica congrep '\[data-X...\]\[data-color=' X.cssDÓNDE se estampa data-color.- Verifica
recipe-existsFIABLE antes de añadir (grep^\t'?X'?: \{). Varios componentes YA tienen recipe (z-index/otros): drag-drop, combobox, timeline, color-picker, float-panel. FUSIONA en el existente, NO dupliques la clave (clave duplicada = el último gana, silencioso; svelte-check lo marca). - Declara SOLO los slots con consumidor real (
grep 'var(--_X-palette-slot)'). Slots sinvar()consumidor = token de recipe HUÉRFANO → falla el orphan test. Si TODO estaba muerto (virtual-list/grid) → NO pongas recipe (tipo abierto + wrapper inerte basta). - Trampa
--x: inherit: un custom-prop con valorinheritNO guarda el tokeninherit; hace que el prop herede del padre (→ inválido). Para "default = inherit" usa una regla[data-X][data-color],[…custom]{ color: var(--_X-palette-text) }y deja la base encolor: inherit(patrón demark). - Cross-portal: si el componente RE-DECLARA su accent en 2 scopes (trigger +
contenido portalado), el forward en el root NO alcanza el portal. Requiere data-color
- forward en el scope del portal (color-picker cae aquí — ver §Cola).
accentbare vsaccent-soft: renombra-softPRIMERO, luego el barevar(--_X-accent)(con)de cierre) → evita el solape de prefijo.
Cola — ~11 standalones HETEROGÉNEOS (lo que falta)
Cada uno es trabajo INDIVIDUAL con su análisis y verificación propia. Orden sugerido: los menos peligrosos primero.
Patrón D/simple (menos peligrosos — empieza aquí)
- skeleton, spinner, textarea, metrics, form — tienen cascada per-rol pero cada
uno pone una PROPIEDAD distinta (
color/background/border) directa. Analiza qué propiedad tinta cada uno y a qué slot mapea (grep 'data-color=' + var(--color-). spinner además tiene| 'inherit'en su tipo (conservar como unión aditiva). - field-langs —
FieldLangsColor = ColorRole, no estampa data-color (¿compone Field?); investigar cómo colorea. - radio-cards —
AffirmativeColorRole; compone RadioGroup (ya abierto). Probable delegante (patrón E) o mini-recipe. - image —
ImagePlaceholderColor = ColorRole | (string & {})(ya semi-abierto); el placeholder. Poco trabajo.
Patrón C con TRAMPA (compound/portalado — CUIDADO, requieren verificación)
- color-picker — cascada 1 slot (accent→border) en
[data-color-picker], PERO re-declara--_color-picker-accenten 2 scopes (trigger l.28 + contenido portalado l.99). El forward en el root no alcanza el portal → el contenido portalado necesita su propio data-color+forward. Compound (root+trigger+ channel-input estampan data-color; las partes threading raw necesitan resolveComponentColor como el sweep de combobox). Recipe YA existe (fusionar). Intentado y revertido esta sesión por el matiz cross-portal. - float-panel — cascada 1 slot (accent→border) en
[data-float-panel-content](parte →parts: ['content']). PERO su default base es--_float-panel-accent: var(--float-panel-accent)y--float-panel-accentNO existe en el recipe → el comportamiento actual es "sin acento salvo data-color". Un hostprimary-borderlo cambiaría a "siempre primary" (cambio silencioso). Verifica primero el default REAL (¿el content-wrapper siempre estampa data-color víactx?.color? ¿ctx.color default 'primary'?). Recipe YA existe (fusionar). Intentado y revertido esta sesión.
Especiales
- proof-of-human — fixed-tone. Sus literales de color/font-size en
clock.css/rotate-align.cssYA fallan 2 tests del contract (raw-color + font-size) — añadir aFIXED_TONE_COMPONENTS(recipe-css-contract.test.ts) o tokenizar. Sucolores accesorio. - chronos — en
WIP_TRACKS(excluido del contract); scheduler sin commitear. - card-group — el
.svelteraíz NO tiene propcolor(sus items componen Card, ya abierto).card-group-itemsí; revisar si necesita apertura.
Fase 4 — Tinta de contenido (code, display, heading, label)
Otro eje (primary|secondary|muted|disabled|on-solid). Abrir como unión que
conserva su eje: ComponentColorProp | 'muted' | 'disabled' | 'on-solid'; cablear
el color de texto al slot text de la paleta (patrón D, como link/mark). Nota:
link/mark YA se hicieron con este patrón — reúsalo.
Fase 5 — Guard estructural + docs
- Guard: test que falla si algún tipo
*Colores más estrecho queComponentColorProp(salvo la unión aditiva de tinta:| 'muted' | ...). Ancla:scripts/component-audit.ts:1327orecipe-css-contract.test.ts. Esto ENFORCEA "la jaula siempre abierta" y cierra la iniciativa. - Docs:
reference.md §25+ trackercontinue-cleanroom-fixes-2026-07.md §THM-2(registrar la reversión) + changelog.
Operativa (verificada esta sesión)
- Dev server:
preview_startcon la configverifyde.claude/launch.json(:5201). Reiniciar tras tocar un morfo (HMR no recargamorfo/*.tsfiable — compile cacheado por WeakMap). - ⚠️
checkNO es baseline estable: la caché incremental de svelte-check aflora intermitentemente erroresanyPRE-EXISTENTES de demos ajenos (heroscrolling/ animations/palabras/alpha/temas): oscila 76↔94. La verificación FIABLE es CERO errores en los componentes TOCADOS (grep 'ERROR "src...components/X'), no el total. - Tras tocar un recipe:
npm run generate:eidos-css+ verifica el forward emite engenerated/base.css(grep '\[data-X...\]\[data-color\],'). NO hace falta tras solo.svelte/.ts. - Contract test:
npx vitest run src/uix/eidos/recipe-css-contract.test.ts— 26/28 pasan; los 2 fallos son proof-of-human (pre-existente). >2 fallos = tu cambio (orphan/phantom/forward). - Navegador: probe
getComputedStylede--{c}-palette-{slot}sobre el elemento que estampa data-color; fuerzadata-color='risk'y compara con un<div data-color='risk'>probe (--palette-{slot}); custom constyle.setProperty('--color-custom', '#00b894')→ esperacolor-mix.
⚠️ ÍNDICE GIT COMPARTIDO
El working tree tiene WIP de OTROS tracks sin commitear. Al commitear:
git reset -q + git add de rutas EXPLÍCITAS + git diff --cached --name-only
para verificar ANTES de git commit -F - (backticks en el mensaje → usar -F, no -m).
EXCLUIR siempre:
menubar(eidos/soma/morfo),dropdown-menu,palabras,words— track palabras.soma/components/virtual-list/*+web/routes/uix/components/virtual-list/+page.svelte— el track de chat (chat-*) extiende VirtualList ("anchor end/chat mode"), AJENO al color. Yo solo tocoeidos/components/virtual-list/*.web/routes/alpha/,.claude/settings.local.json.- Cambios de comentario "Words"→"Palabras" en css ajenos (p.ej. color-picker.css).
Referencias de patrón (copiar de estos)
- Mini-recipe (C): month-grid (recipe
_palette-*+ css + types + wrapper). - Con
parts: combobox (parts: ['control','input','trigger','content']) / table (parts: ['root']). - Tinta (D): link (1 slot) / mark (element+text, regla colored-only).
- Delegante (E): button-group / highlight.
- Sweep de sub-partes que threading raw: workflow con ref
select-trigger.svelte.