Firmas del usuario (2026-07-21) tras el dossier de referencia:
- E-1: componente canonico nav-tree (F1.8, data-driven — precedente Menubar)
resuelve la colision sidebar-app vs arbol-docs/B-5; fiche nueva con
fase 0 obligada contra nuestro tree-view; docs-shell compone NavTree.
- E-2: suelo de paridad del dossier = alcance v1 (regla en intro F1).
- E-3: F2 pasa de 10 a 14 (banner · team · contact · content-section);
fiches nuevas + galeria actualizada (verificada en navegador: 28 badges).
- E-4: distribucion registry/llms.txt/MCP → iniciativa propia (next-features
§9; ese archivo sigue fuera del commit por diff foraneo).
- E-5: ⌘K = listener app-land documentado (nota en F4.1).
Doctrina: promotion path de architecture/blocks.md actualizado a 8.
Verificacion: docs:check 0/0 (512).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Encargo del usuario (2026-07-21): estudio de referencia para "estar al menos
a la par y cuanto menos superarlo". 6 agentes de investigación web en
paralelo sobre fuentes oficiales vivas (varias verificadas a nivel de código
fuente: sidebar.tsx del registry shadcn, styles.js de tailwind-typography,
sources de Mantine/Starlight/Docusaurus/Ark).
- docs/process/RESEARCH-blocks-references.md — el dossier: P1 catálogos de
marketing (conteos+variantes) · P2 aplicación (ProTable/ProLayout,
dashboard-01, Tremor 303, Clerk/Supabase) · P3 F1 ligeros (APIs a nivel de
prop; scroll-state(stuck) como vocabulario de plataforma; role=alert de
shadcn = bug a no copiar) · P4 prose+sidebar (not-prose/:where()+donut;
shadcn 23 partes) · P5 docs shells (Fumadocs benchmark; colision F1.7 vs
nav-tree/B-5; ruta svelte2tsx para props TS) · P6 ecosistema Svelte
(shadcn-svelte 58 blocks solo-app; hueco "sistema integrado" VACIO) +
sintesis: 5 conclusiones, enmiendas E-1..E-5 (presentadas, firmas
pendientes), 10 angulos de superacion.
- PLAN-blocks.md — regla nueva §4 (toda fase 0 contrasta contra el dossier),
puntero de colision en F1.7, registro.
Verificacion: docs:check 0/0 (512 docs).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Tier nuevo aprobado 2026-07-21 (D-BLK firmadas; D-BLK.1 enmendada: vive en
src/uix/blocks/). Plan de ejecución completo en docs/process/PLAN-blocks.md.
- docs/architecture/blocks.md — doctrina E1 (regla de admisión canon-vs-block,
contrato B-1..B-11, promotion path); espejo de packs.md
- alias $blocks → src/uix/blocks (vite + svelte.config + tabla CLAUDE.md)
- src/uix/blocks/README.md — mapa del tier + template de README de block (B-9)
- scripts/blocks-check.ts + npm run blocks:check — guard mecánico del contrato
B con self-test de detectores (fixtures inline; 5532 archivos de
canon/arts/libs/packs escaneados para dirección de imports)
- web/routes/blocks/ — galería índice del catálogo planificado + layout de
bootstrap propio (+layout@.svelte, espejo mínimo del de /uix sin packs sema)
Verificación: docs:check 0/0 (511) · svelte-check 76E/51W = baseline exacto ·
blocks:check verde · galería comprobada en navegador (a11y-tree + estilos
computados en light y dark; captura de píxel bloqueada por panel suspendido).
Fuera del commit: fila E1 en docs/README.md y §8 en docs/next-features.md
(diff foráneo de otra sesión sin separar — ver registro del plan).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Nuevo componente emoji-picker y rediseño del escaparate de mensajeria.
emoji-picker (ruta 9 fases):
- Compone Popover (open/close/foco/portal) + Command (busqueda + grid + nav de
teclado + a11y combobox/listbox, su "grid mode" documentado) + dos ToggleGroup
(categorias + tono de piel). Delegated: sin eventos propios (D.1); el grid y
las celdas doble-registran Command.List/Item, conservando la a11y pero con
contrato visual propio (gana especificidad con selectores de 2 niveles, sin
tocar attrs [data-command-*] ajenos — eidos-lint limpio).
- Datos: $libs/emoji, dataset vendorizado de emojibase (MIT) horneado por
scripts/generate-emoji-data.ts (precedente lucide: JSON crudo en static/ +
script generador). ~1906 emoji con nombre CLDR + keywords, 316 con tono de
piel single-person. Grid acotado (categoria activa o resultados de busqueda),
sin virtualizacion (refinamiento v2). Helpers puros + test (12/12).
- morfo delegated (7 partes + header/footer de layout); soma (root + trigger +
content + grid + option + preview, provider-test 4/4); eidos (panel
empaquetado, receta con tokens --emoji-picker-*); langs es/en; demo 5 tabs
(excepcion delegated documentada); READMEs dobles.
Escaparate del bloque (web/routes/uix/demos/chat) — rehecho rico y correcto:
- Avatares por-autor con color solido (Avatar del ecosistema): antes gris
neutro indefinido, ahora teal/ambar/verde/indigo/primary bien contrastados.
- Dos columnas: chat-list (sidebar con badges + typing-en-lista) + sala.
- Conversacion rica: separador de dia, run agrupado, reaccion, reply citado,
recibos de lectura, typing con avatar, y el emoji-picker integrado en el
composer (inserta en el draft).
- Solape de burbujas (transitorio, estimateSize=48 con mensajes ~90-126px)
mitigado subiendo el estimate a 76.
Auditoria de tamanos/espacios: recipes del bloque limpios (todo con tokens
--space-*/--size-*/--color-*); unico literal (punto tono-capable) tokenizado.
Verificacion: check baseline 76 (0 nuevos) · component:audit emoji-picker PASS ·
eidos-lint invalid=0 · recipe-contract 30/30 · morfo:vocabulary 0 · emoji lib
12/12 · provider-test 4/4 · Playwright PASS (busqueda 'rocket'->rocket+
astronautas, click->onSelect+cierra, teclado, tono oscuro 👋->👋🏿, picker abre
en el composer). Capturas claro miradas.
next-features §7 marca emoji-picker SHIPPED.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Documenta el resultado (el solver de contraste era innecesario: el morph HEREDA
el contraste de donantes §40-compliant) en los docs de diseño/código:
- docs/rfcs/rfc-color-engine.md: §15 nueva (resolución Stage 2 con las 3 bancas
de evidencia + decisiones D1–D6), anotación de fases §11 (Phase 0 = tipos
diferidos D5; Phase 3 = base→seeds NO se hace, D2), footer + §14.
- src/arts/color/README.md: CONTRAST_PAIRS en la API + nota "contraste heredado,
no resuelto" con puntero a RFC §15 y reference §40.
- src/uix/eidos/COLOR_ENGINE_RFC.md (stub): deja de sobreafirmar que el tipo
ColorScaleSeed existe (está diferido, D5) + puntero a §15.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
La ejecución refutó la premisa del plan: el morph HEREDA el contraste del texto
(steps 11/12 = curva-L del donante verbatim; ~invariante al croma bajo
gamut-mapping), así que toda escala desde donantes §40-compliant cumple por
construcción. Medido en 3 bancos (base, leave-one-out, 45 semillas OOD): 0 fallos
del gate duro text-strong·12, min WCAG 9.7:1. El solver de luminancia no tenía
nada que resolver → descartado por especulativo. D2 = base verbatim (sin
migración base→seeds).
- $color: CONTRAST_PAIRS (arts/color/contrast-contract.ts) — tabla §40 como datos
compartidos: floor duro text-strong·12, banda text·11 (tope relacional, sin nº
mágico), border·7 exento
- scripts/contrast-audit.ts: muere la PAIRS pre-veredicto (4.5 duro contra §40),
consume CONTRAST_PAIRS + banco de regresión morph-generado
- eidos/lib/contrast-invariant.test.ts: guard de CI (4 tests) que bloquea la herencia
- docs: reference.md §40, next-features.md §1, changelog.md §45, plan (OUTCOME)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Componente nuevo del bloque chat v2: el panel de lista de salas/DMs.
Compone el Listbox del ecosistema (doble registro id/ref compartido):
selección única + roving-tabindex + typeahead + role=option vienen de
Listbox; chat-list aporta la disposición de fila (avatar · título/preview ·
meta) y el estado visual data-unread (negrita + Badge compuesto).
- morfo: expression 'delegated', sin eventos propios (D.1 — el actor es el
Listbox compuesto); parts provider/item(data-unread)/avatar/title/preview/meta.
- soma: ChatListProvider crea ListboxProvider dentro (value string <-> string[]);
ChatListItemProvider per-instance (unread = boolean crudo); 4 clases de
contenido explicitas.
- eidos: rejilla avatar | (titulo/preview) | meta, avatar y meta abarcan las
dos filas; truncado; unread -> titulo en negrita. No neutraliza el arquetipo
item (la conversacion ES una opcion seleccionable). Sin @keyframes; ejes
logicos.
- demo: 5 salas, seleccion limpia unread, typing-en-lista (compone ChatTyping),
Badge de no-leidos; tabs live/system/api/morfo/a11y.
- READMEs dobles (eidos con Passive justification, delegated); next-features 7
marca chat-list SHIPPED.
Verificacion: check baseline 76 (0 nuevos) · component:audit PASS ·
eidos-lint invalid=0 · recipe-contract 30/30 · morfo:vocabulary 0 · Playwright
PASS (role=listbox, 5 salas, aria-selected, unread=2, badges=2, typing-en-lista,
click + teclado seleccionan).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Plan de ejecucion para sesion aparte, VERIFICADO adversarialmente (workflow de
7 agentes contra codigo y docs) antes de escribirse — corrige 5 afirmaciones
de la respuesta que lo origino:
- "por delante de Radix (hex sRGB)": FALSO — el propio RFC §14 registra Radix
con APCA y P3 ("Radix style"); la posicion del repo es "on par"; nuestra
paleta enviada es sRGB-equivalente (oklch = siblings de hex).
- "el solver satisface la tabla §40": el UNICO floor duro ratificado es
text-strong·12 ≥ 4.5; text·11 quedo tier blando (decision D1); solid·9 esta
anclado exacto a la semilla; el script de auditoria aun codifica la tabla
PRE-veredicto (consumirla contradiria los veredictos → F0 = tabla como datos).
- "los tipos ya existen en parte": ColorScaleSeed/Source son solo prosa del
RFC; config-seeds imposibles hoy (solo applyColorScheme roles-only +
generatePalette full-33 como bancos).
- "792 hex en base.ts": exacto en total pero en DOS ficheros (288 base.ts +
504 color-scales.ts); 4 escalas ya autoradas desde semillas offline.
- "nada downstream cambia": nombres si (contract.ts), pero hay pins de VALOR
disenados para dispararse (hex literales, flip-set de polaridad,
generated/base.css byte-exacto) + persistencia rechaza version ≠ 1.
El plan: F0 tabla ratificada como datos + harness sobre output generado ·
F1 solver (biseccion contra sRGB gamut-mapeado, salida hex, desviacion minima
del donante) · F2 validacion en bancos de semilla + navegador · F3 (gated)
migracion base→seeds con re-ratificacion de pins y pase visual. 6 decisiones
de usuario ABIERTAS al frente (target text·11, semantica base+ΔE, modo
on-solid, colocacion/alcance, tipos Fase 0, normalizacion de la tabla).
next-features §1 enlaza el plan. (El hunk ajeno de chat-list en next-features
queda fuera del commit — danza clean-desde-HEAD.)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Actualiza el handoff en el design-record del bloque: v1 completo (incl.
onMessageSeen), los 3 remates de v2 shipped (typing avatars, read-by,
jump-to-message highlight con su decisión de visual-puro-sin-sema y el
gotcha del boolean en attrs de presencia), y los frentes v2 restantes
(chat-list, emoji-picker, threads, menciones, rangos bidireccionales) por
valor/independencia. docs:check 0 errores.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Tercer y último remate de v2 (batch elegido por el usuario). chat-message
gana el prop `emphasized` → attr `data-emphasized`: al saltar a una cita
(reply.onJump → scrollToIndex) la app enciende `emphasized` ~1.4s y la fila
objetivo hace un flash suave que se desvanece.
VISUAL PURO — sin evento de sema (desvía a propósito del signal-emphasize
-target del plan): las apps de referencia hacen un flash SILENCIOSO; un
signal que pitara por defecto sería molesto. Cumple 2-de-3 (soma escribe el
attr, eidos lo lee). El pulso = toggle de dos estados + transition (cero
@keyframes, R-4.5); reduced-motion = instantáneo.
Morfo attr + soma prop/provider (devuelve el booleano al resolver, no '')
+ eidos recipe (wash color-mix 14% de toda la fila) + demo componente
(botón flash) + escaparate (reply → jump → emphasize). next-features §7
marca los 3 remates shipped.
Verificado (Playwright): flash on → bg tintado → auto-clear a 1.4s;
escaparate intacto. audit PASS, eidos-lint invalid=0, recipe-contract
30/30, check en baseline (76).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Segundo remate de v2. chat-message gana la parte opcional `ReadBy`: una
fila de avatares "visto por N" que la app compone (AvatarGroup), típica
de mensajes propios; el emisor la alimenta con los acuses recolectados
vía el onMessageSeen (v1). DECORATIVA (aria-hidden): el indicador Status
ya transmite "read" a AT; las caras son realce visual.
Morfo part + soma provider/wrapper + eidos wrapper/recipe (fila de
avatares muted, ejes lógicos in/out) + demo con toggle. next-features §7
marca read-by y typing-avatars como shipped.
Verificado (Playwright): la fila aparece con 3 avatares + "Visto por 3",
aria-hidden, en la columna del cuerpo. component:audit PASS, eidos-lint
invalid=0, recipe-contract 30/30, check en baseline (76).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cierra Stage 1 de la iniciativa next-features §1 con los veredictos del usuario:
- reference.md §40 (doctrina standing): contrato de contraste por pares de
slots. Texto = dos tiers (text·11 secundario ≈APCA 60 / text-strong·12 AA
garantizado). Bordes = dos tiers WCAG 1.4.11 (decorativo exento: accent·7,
subtle·4/default·6, reposo-sobre-fondo · portador 3:1: solid·9 lo cumple, el
resto con señal redundante). Foco = eje de config (primitives.focusRing +
color.focus, un solo `outline` por §32); default suave, endurecer = valores
por config, no código.
- changelog.md §44: el chronicle datado (drift medido + veredictos + la
herramienta scripts/contrast-audit.ts).
- next-features.md §1: Stage 1 marcado DONE; Stage 2 (generador
by-construction) sigue abierto, gated en la migración base→seeds.
Sin cambios de código (el hallazgo del foco es una decisión de valores-por-
defecto vía config; defaults mantenidos por decisión del usuario). El script
de auditoría (commiteado antes) queda como herramienta de medición.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
La entrada #6 (THM-2 rollout) decía que los controles semánticos (checkbox/
radio/stepper/select/file-upload) y `Avatar.Badge` quedaban role-restricted
"por diseño". La iniciativa open-color-cage (2026-07-18/19) revirtió justo eso:
`color` = sistema completo en TODOS los componentes. Se añade la nota de
reversión con puntero al handoff; qr-code queda fuera por diseño. Sin cambios
de código.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Documentación del bloque chat-* recién shippeado, siguiendo la convención
del corpus (por-componente = READMEs dobles; por-lib = README; doctrina de
bloque = design record en decisions).
- docs/decisions/design-chat-block.md: la doctrina cross-cutting que abarca
los 4 componentes — la tesis comparativa (5 fallos del sector = nuestras
oportunidades), la topología de composición (triple registro Feed +
VirtualList anclado, Textarea, FileUpload, Icon lucide), el anclaje
(sticky monótono, pin contra DOM real), la doctrina visual del rediseño
de referencia y los 2 fixes de framework que destapó. Indexado en
decisions.md (sección "Component families — design records").
- src/libs/chat/README.md: los helpers puros (groupIntoRuns ventana 8min,
aggregateTypers, límites de día) — convención de README por lib.
docs:check 0 errores (503 docs). Los READMEs por componente y next-features
§7 ya iban en el commit del bloque (4c4878f13).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Familia chat-* completa, componiendo el ecosistema (Feed + VirtualList
anclado, Textarea, FileUpload, Avatar, Popover, Icon). Transporte-agnóstica:
el modelo de datos es de la app, los componentes reciben props + snippets.
- chat-log: Feed + VirtualList(anchor:'end') en triple registro; day
separators, unread divider, filas de sistema (Feed.Article), pill "N
nuevos" + notifyIncoming (announce a live-region), to-latest distance-aware.
- chat-message: raíz Feed.Article; runs ($libs/chat), ticks de entrega,
reply embebido, reacciones (chip pressed + tapback panel), acciones hover.
- chat-composer: Textarea (doble registro) + FileUpload; teclado
Enter/Shift+Enter/Esc/ArrowUp; typing saliente debounced (uix.timers);
barra reply/edit.
- chat-typing: sustain-streaming (visual-only, loop del sistema);
aggregateTypers 1/2/3+ con plantillas plurales.
- $libs/chat: groupIntoRuns (ventana 8 min estilo Discord) + aggregateTypers.
- VirtualList: extensión anchor:'end' (followOutput, prepend por clave
estable, sticky monótono, onAtEndChange) — sin scroller paralelo.
Diseño de referencia (WhatsApp/Telegram, pase lado-a-lado): burbujas suaves
(out = primary-element + texto normal; in = surface card + sombra) sobre
lienzo muted del log, meta dentro de la burbuja, tapback bar horizontal,
iconos lucide del ecosistema (nada de SVG a mano). Contrato de tamaños:
size="md" = 16px canónico.
Fixes de framework (afectaban más allá de chat):
- EngineLangs.t() ahora pela el prefijo '#?': los idlangrefs imperativos
(pagination/file-upload/chronos/chat) caían al fallback EN en locale es.
Fix parseLangRef ?? parsePathFallback + test de regresión (132/132).
- virtual-list commit-set-resize con channels:[]: es bookkeeping de
ResizeObserver, no gesto → dejaba de emitir beep/vibrate pre-interacción
(interventions de navigator.vibrate / AudioContext).
Escaparate messenger en /uix/demos/chat (bot de eco con uix.timers;
variante $connection señalada). Gaps v2 registrados en next-features §7.
Pase: component:audit 4×PASS · eidos-lint invalid=0 · recipe-contract
29/29 · Playwright escaparate PASS end-to-end · consola limpia ·
npm run check en baseline (76, cero errores nuevos).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Actualiza el handoff y el changelog §43 al estado FINAL de la iniciativa
(cerrada, cuarta sesión):
- open-color-cage-2026-07.md: añade las 2 fugas transitivas cerradas
(card-group-item · s-text) + el guard runtime, el fix del bug de Card/Avatar
(`b93cc6c5c`), la lista completa de commits (sesiones 3–4), y una sección
«Follow-ups» con lo menor que queda (override docs-chrome sobre `<Code>`,
ringColor roles-only, límites de los 2 guards, comentarios históricos) +
nota del estado del working tree (WIP de chat/palabras sin commitear, ajeno).
- changelog.md §43: el bug de Card/Avatar pasa de «flageado, NO arreglado» a
ARREGLADO con el resumen del fix (migración a resolveComponentColor + compose
del seed + canales ring/badge separados intactos).
Sin cambios de código. La iniciativa queda CERRADA sin huecos abiertos
(qr-code excluido por diseño). Contract 30/30.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Chart era el último hueco de la iniciativa. Su `color` NO tinta labels/ejes:
tinta la MARCA de datos (stroke de línea, fill de área/barra/burbuja, hue de
heatmap, arco de gauge, porción de pie/funnel/bar-list/polar, punto de smith).
Es SVG con resolución JS, así que NO usa el `data-color` + capa de paleta
compartida (eso es para cascadas CSS en HTML) — el mecanismo es propio.
Resolver central en `chart/context.ts`:
- `seriesColor` / `seriesSurface` reescritos + `seriesContrast` nuevo, todos
`number | ComponentColorProp`. Un índice cicla `SERIES_ROLES` (multi-serie,
sin cambios). Un valor explícito pasa por `resolveChartColor`:
· rol/intent → `var(--color-{role}-{solid,surface,contrast})`
· una de las 33 escalas → `var(--scale-{name}-{9,a2})` (steps solid /
surface-alpha del `PALETTE_SLOT_STEP`); contrast → white (no hay token)
· valor CSS crudo → verbatim (surface = `color-mix 15%`, contrast = white;
sin contraste garantizado, el trade-off de cualquier custom).
- `ChartContext.color` widenado a `number | ComponentColorProp`.
Todos los props `color?: ColorRole` (~14: ChartSeriesProps, Bubble, Sparkline,
ChartCategory, BarList, Funnel, Calendar/Heatmap, Smith×2, Gauge) →
`ComponentColorProp`. Los 3 builders que armaban el fill inline (funnel:
solid+contrast, heatmap:56, calendar-heatmap:118) refactorizados para pasar
por el helper — funnel pierde su `roleOf` local.
qr-code queda FUERA por decisión de diseño (color = tinta de módulos del QR,
contraste con el fondo, no encaja escalas).
Verificado en Chrome: las 3 ramas del resolver resuelven a color real
(--scale-teal-9→oklch, --scale-teal-a2→rgba translúcido, #3b82f6→verbatim,
mix translúcido ok); marcas existentes = primary-solid (roles behavior-
preserving). check chart limpio; contract 30/30. Docs: changelog §43 +
handoff actualizados (hueco chart cerrado, qr-code excluido por diseño).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Guard: test nuevo "keeps every component `*Color` prop open (THM-2 — the
cage stays open)" en recipe-css-contract.test.ts — escanea los alias
`export type XColor = …` de components/*/types.ts y FALLA si alguno se
estrecha por debajo de ComponentColorProp. Uniones aditivas ('muted',
'inherit', 'absent'…) pasan; alias resuelven transitivamente
(DatePickerColor = CalendarColor); WIP_TRACKS (chronos) re-entra con su
track; exento comentado: OnionColor = string (MAS ancho que la jaula,
normalizacion pendiente). Verde con el inventario real: 60+ alias abiertos.
Docs (todo lo aplazado del track):
- reference.md §25: reversion de los subconjuntos THM-2 registrada como
doctrina (color = sistema completo en TODOS; identidad ≠ evaluacion;
enforcement = el guard).
- Tracker clean-room §THM-2: nota de reversion (los narrows que THM-2 dejo
"por diseño" quedan abiertos, Avatar.Badge incluido).
- changelog.md §43: cronica de la iniciativa completa (mecanica, fases,
huecos señalados).
- Handoff open-color-cage-2026-07.md → CERRADO con el resumen de la tercera
sesion (cola completa, F4, F5, commits) y los 2 huecos pendientes de
decision: chart (11 props ColorRole) y qr-code (color inline semi-abierto,
invisible para el guard). El cuerpo queda como referencia de patrones A–E.
- 13 READMEs con afirmaciones ya falsas corregidos (roles-only, subsets,
"Locking to ColorRole", tablas de props): badge, css-field, field-langs,
float-panel, link, listbox, mark, metrics (solo el icon; Chart delega en
chart y queda como esta), password-field, proof-of-human (incl. la fila de
decision "descartar" → "hecho (reversion)"), range-calendar, textarea,
timeline. banner NO se toca (su ColorRole es del eje intent, otra cosa).
Contract test: 29/29 verde.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Reescribe docs/process/open-color-cage-2026-07.md tras los 7 commits de esta
sesión: familias hechas (field/calendar/time/list-grid/lists + link/mark/
button-group/highlight), los 5 PATRONES de migración (A eidos-wrapper · B
soma-routed · C mini-recipe month-grid · D tinta de contenido · E delegante), las
6 TRAMPAS cazadas (forma declarations[] para `parts`, recipe-exists fiable/no
duplicar clave, declarar solo slots consumidos, `--x:inherit`, cross-portal,
prefijo accent/accent-soft), y la COLA de ~11 standalones heterogéneos con su
patrón + trampa cada uno (incl. color-picker/float-panel intentados y revertidos
por matiz cross-portal/default). Operativa corregida: el total de `check` NO es
baseline estable (76↔94 por caché svelte-check) → verificar CERO en tocados;
índice git compartido + exclusiones (chat-track en soma/virtual-list, palabras,
alpha).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Motor (Fase 0): tipo abierto `ComponentColorProp`, helper `resolveComponentColor`,
derivación custom compartida `[data-color-custom]` (color-mix desde `--color-custom`)
+ forward extendido a `[data-{c}][data-color-custom]`; guard recipe-css-contract
actualizado.
Eidos-wrapper (14): radio-group, checkbox, stepper, toggle-group, select, badge,
editable, file-upload, tag-group, tags-input, surface, avatar, card. Patrón:
tipo -> ComponentColorProp, wrapper -> resolveComponentColor (estampa
data-color/data-color-custom/style), demo con picker + input custom. Incluye el
fix del bug de escalas de card (una escala caía al path custom y pintaba el CSS
named color en vez de la escala de paleta).
Soma-routed (button/switch/toggle): patrón morfo+soma limpio — el provider omite
data-color y emite data-color-custom para el valor crudo, con el intent evaluativo
ganando y suprimiendo el custom. Ensancha los tipos de color de la capa soma.
Arreglados 2 bugs del runtime del framework:
- html-presence con `v.literal('')` (valor vacío = falsy) nunca emitía el attr ->
`v.propRef('colorCustom')` (como data-disabled).
- el custom pisaba al intent evaluativo -> el prop `colorCustom` se anula con
intent no-neutral.
Bundle: incluye el rail segmentado de radio-group (variant=segmented, indicador
deslizante compartido soma-mide/eidos-pinta: measured-indicator + sliding-indicator)
de la misma sesión, intercalado en los ficheros de radio-group.
Handoff para continuar: docs/process/open-color-cage-2026-07.md (Fase 3/4/5 pendientes).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Re-auditoria completa del ciclo (batteria entera de guards + bordes
discriminantes medidos en navegador). Dos hallazgos, dos cierres:
1. El override directo de --_{c}-fg del named finish EMPATABA (0,3,0) con el
slice solid de Surface y dejaba el ganador al orden de hojas de estilo -
no es un contrato (el caso discriminante aurora-sobre-amber lo destapo:
la primera verificacion usaba primary, cuya contrast blanca coincidia con
la tinta de aurora y enmascaraba el empate). Fix por la doctrina D7: el
generador emite la var --_{c}-finish-ink y el slice solid de cada recipe
la consume con su contrast de fallback - gana por existir, nunca por
especificidad; las variantes no-solid quedan inertes por no leerla.
Verificado por valores: aurora-amber blanco (autorada) en Button/Badge/
Surface; flat/rampa amber oscuros (heredada); outline inerte.
2. Nombre no declarado (gradient="foo") degrada a la rampa - medido y
documentado en D11 como fallback gracioso.
Atribucion corregida de paso: el crash de morfo:vocabulary/morfo:check es el
morfo de text-blur (deuda preexistente ajena, chip de tarea creado) - NO
palabras como se asumio antes; surface/box/palabras validan OK.
Bateria final: eidos entero 331/333 (2 = proof-of-human preexistentes),
config 72/72, guard 5/5, audit 145/145, eidos-lint invalid:0 en los tres
consumidores, typecheck limpio.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Auditoria de la pregunta "respeta la transparencia todo el sistema": medido
por valores computados en navegador. El fill por capas lo hace correcto -
- identidades opacas (los 42 roles/escalas) -> acabado opaco (matchea el
solid de hoy);
- aurora/named = translucido POR DISENO (blobs color-mix(...,transparent)
sobre la base solida, alfa 0.62 medido) - la translucidez es el mecanismo;
- slots tinte (surface/track a2/a3) + variantes soft: el acabado es inerte,
su translucidez se preserva por no leerse;
- data-on inks: translucidos intencionales.
El unico borde (medido y documentado, no un bug del camino previsto): un
valor translucido forzado en el slot solid (no alcanzable por ninguna prop -
solid es la variante OPACA por definicion; la translucidez vive en los slots
tinte) hace que el color-mix hacia el ancla opaca suba el alfa (0.5 ->
0.545/0.631). La base background-color conserva su alfa, el elemento sigue
translucido en conjunto. Entrada fuera de contrato, no defecto - un framework
de referencia lo documenta en vez de dejarlo tacito (capitulo D2).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
D11 - Named finishes (el diseno A rescatado como opt-in explicito):
- gradientFinish.named opta gradientes del open cage como acabado, cada uno
con su tinta AUTORADA obligatoria (--gradient-{name}-ink, override de la
convencion --_{c}-fg a especificidad (0,3,0) sobre el slice de variante).
- La base sigue siendo el solid de la identidad (fill por capas, D2) -> el
aurora shipped son blobs de rol con alfa SIN color base final:
background-image-valido POR ARQUITECTURA (el caveat del mesh disuelto, no
exceptuado), y re-tine por tema y modo via referencias de rol.
- Honestidad documentada: la validacion numerica al peor stop de un string
CSS arbitrario no es implementable (stops desconocidos); posible solo para
gradientes de modelo (buildGradient) - diferida. El guard clava la sanidad
del config (named subconjunto de gradients + ink presente).
D12 - on/data-on minimo (Surface on="light|dark"):
- La foundation re-vincula --color-content-*/--color-border-default para el
subarbol (dark -> tinta on-solid; light -> on-solid-contrast; mixes oklch
82/64/32%). Verificado EN VIVO: un parrafo muted dentro del aurora computa
la tinta del contexto al 64%.
- Limites POR CONSTRUCCION y documentados en cada consumidor: componentes
anidados con tokens propios y contenido portaleado NO se re-entintan (la
inversion completa sigue siendo iniciativa independiente). forced-colors:
el contexto resuelve a CanvasText/GrayText (bloque extendido). Sin
color-scheme a proposito (solo chrome UA).
Ademas: prop gradient ampliada a (string & {}) para named en Button/Badge/
Surface; poda del orphan-guard (border/text de Surface: 3 slots x 8 colores,
lo que las variantes consumen); guard 5/5; audit 145/145; lab CASO 07 con el
aurora REAL (<Surface gradient="aurora" on="dark">) verificado por valores.
Docs: capitulo D11+D12 + registro D1-D12 + roadmap COMPLETO; reference p39;
changelog p42; plan cerrado (quedan declaradas: inversion completa, demo
propia de Surface, validacion de modelo para tintas named).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Box es layout-only por doctrina y rechaza background/color; Surface es la
primitiva que posee el lado del tratamiento: el caso hero / banda decorativa
/ bgGradient como primitiva de primera clase, no como escape-hatch style=.
- Composicion sobre <Box> (patron Section): hereda TODA la API de caja y
estampa data-surface + color/variant/gradient/rounded (attrs eidos-only de
wrapper, doctrina D8).
- Recipe palette-tint espejo de Card SIN chrome: _palette-* 5 slots x 8
colores -> el forward THM-2 ruta roles + 33 escalas donantes gratis
(verificado: color="teal" resuelve sin cableado extra).
- Variantes soft (tinte track, tinta global) y solid (lienzo saturado, texto
plano hereda contrast); gradient en el gate del acabado (rampa anclada +
spread; sin solid-hover -> extremo profundo cae a solid).
- Morfo declarativo patron Box (scope eidos, 1 parte, 0 eventos
justificados); eidos-lint invalid:0; audit 145/145 PASS (ficha completa:
Baseline/Decisiones/Gaps con disposiciones/Passive justification).
- Lab temas/gradientes CASO 07: heroes REALES con Surface (rampa primary,
spread plum, banda soft teal) verificados por valores computados; el
aurora nombrado queda como preview del siguiente paso.
Cola v2 restante: named finishes con tinta autorada + on/data-on.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Esta vez en el orden de la doctrina D6b: las dos puertas se midieron ANTES
de escribir la implementacion.
- Puerta 1 (sonda, 84 combos): la rotacion pura a L constante rompia grass
(+-4 grados) y gold (+-27) - L de OKLCH no es luminancia relativa. Rescate =
la propia doctrina aplicada suave: ambos stops rotados toman la mezcla
DEBIL del ancla (lift/3) -> 0 regresiones hasta +-45 grados.
- Puerta 2 (RCS en Chromium, por valores computados): el canal h de
relative color es <number> - calc(h +- 30deg) computa none -> el token
--gradient-finish-spread es SIN unidad ('30').
Shipped: gradient="spread" en Button+Badge (boolean | 'ramp' | 'spread';
data-gradient='spread' overridea la var del acabado - cero cambios de CSS de
recipe); token + override por tema (gradientFinish.spread); guard 4/4 con el
invariante del spread anclado; lab con especimenes reales (threat cruza el
360, gris C=0 queda ~plano - documentado, no caso especial); docs D10 en
theming/gradient-finish.md + reference p39 + changelog p42 + plan v1.5
completa.
Una doctrina, dos kinds: la rampa huye de la tinta con fuerza; el spread
huye suave mientras juega con el matiz.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- ThemeDefinition.gradientFinish.lift: el tema re-emite --gradient-finish-lift
en su bloque (misma especificidad, despues en cascada -> el tema gana);
claro y oscuro pueden llevar intensidades distintas, 0% apaga por tema.
Test: active-eidos-config.test.ts ("per-theme gradient-finish dial override").
- Capitulo de nivel libro docs/theming/gradient-finish.md: registro de
decisiones D1-D9 con el porque de cada una, las alternativas rechazadas con
evidencia (color-value/variante/bg-prop; pasos 7/11 con valores reales;
lift-up global medido y tumbado 52/84), la rectificacion documentada y la
leccion de proceso (medir antes de fijar defaults). Indexado en docs/README.
- reference.md paragrafo 39 enlaza el capitulo; changelog paragrafo 42 y el plan
marcan la v1.x completa (guard + Badge + override por tema).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
El gradiente entra al sistema como ACABADO (material) del fill, jamas como
valor del eje de color: prop `gradient` -> attr eidos-only `data-gradient`
(familia data-variant; NO morfo - el runtime lo resolveria desde props de
soma y mergeProps clobberea el stamp del wrapper).
- Fill por capas: background-color = base solida (degrada sola en
forced-colors) + background-image = rampa derivada de los slots de LA
instancia (roles + 33 escalas + custom gratis, tinta heredada).
- Dial unico de tema: primitives.gradientFinish.lift (26%) ->
--gradient-finish-lift (0% = apagado; override por instancia via cascada).
- RAMPA ANCLADA a la sombra de la tinta ("la rampa huye de la tinta"):
tinta blanca -> #000 fuerte abajo (CTA sombreado); tinta oscura -> #fff
fuerte arriba (glossy). Ancla+angulo por color x modo con el MISMO flip
del slot contrast. Rectificacion MEDIDA: el lift global hacia blanco
rompia la tinta heredada en 52/84 combos a 26% (techo global 0%) - el
contraste ahora solo puede mejorar: dial sin topes.
- Guard ejecutable gradient-finish-guard.test.ts (3/3): no-regresion <=40%
sobre 84 combos + set flat-fail clavado (cyan/orange, deuda on-solid
preexistente).
- Generador emite la VAR (--_{c}-fill-finish); el recipe pinta (solid +
re-assert en hover: su shorthand background resetea el longhand).
- Lab temas/gradientes: 9 casos con componentes reales sobre el token real
(dial en vivo, polaridad observable, evidencia de pasos 7/11, forced-colors).
- Docs: capitulo theming/gradient-finish.md (registro de decisiones D1-D9,
alternativas rechazadas con evidencia, leccion de proceso: medir ANTES de
fijar defaults) + reference.md paragrafo 39 + changelog paragrafo 42 + plan
docs/process/gradient-finish-plan-2026-07.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
8-step path distilled from how RotateAlign/Clock were actually built (morfo ->
langs -> soma provider vs the ChallengeHost -> telemetry block -> sema pack ->
eidos skin -> anti-bot tests -> demo+gates) + the non-negotiable invariants
(challenge never owns the verdict, no network inside, random per instance).
Also refreshed the stale bits: the orchestrator README now names both shipped
challenge engines and documents the rotation telemetry block alongside
pathAdherence; eidos README points to the recipe.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- soma: targetSnapDeg knob quantizes the random target bearing (the clock
needs hour-aligned targets: a free bearing misses the marked hour by up to
15deg vs the 8deg tolerance ~47% of the time); default off, unit-tested.
- eidos: clock.svelte + clock.css - face (11 ticks, numbers at 3/6/9,
accent target-hour badge), cuckoo house at 12 (roof, doorway, swinging
doors, sill), FRONT-facing messenger pigeon with the letter, hour hand.
Verified-state dispatch (doors swing -> peek -> hold -> fly) as bespoke
content choreography, every @keyframes annotated functional: per R-4.5;
sill occlusion via clipPath; outer/inner SVG transform split; fulfill
recolor on verdict; reduced-motion = static doors open + peek.
- exported ProofOfHuman.Clock + ClockProps; eidos README challenge list.
- demo: Clock chip reusing the rotate knobs; clock idle instruction; the
success card waits 3.3s for the cuckoo; corner stage-pigeon suppressed in
verifying AND verified for the clock (the cuckoo IS the pigeon). Includes
prettier normalization of the demo page.
- handoff doc rewritten: clock shipped; filming/throttling gotchas recorded.
Verified: 81/81 tests (poh scopes), component:audit 144/144 PASS, svelte-check
clean on touched files, end-to-end solves in real Chrome (light + dark).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Full self-contained handoff for a fresh session: architecture, both shipped
challenges (PathTrace + RotateAlign), the clock-skin spec (cuckoo + front-facing
pigeon, from the iterated mockup), the SVG transform-override gotcha, the
concurrency warning (another session on the same branch), verification-env
gotchas, and the commit list.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Split the anti-bot component into a purpose- and interaction-agnostic
orchestrator that owns the lifecycle stages + verdict, and a pluggable
challenge that runs the interaction and reports up via
ProofOfHumanChallengeHost. The primary challenge, PathTrace, has the user
follow a random curved path by pointer OR arrow keys.
This fixes the drag version's two real defects:
- keyboard bypass: the accessible path now yields genuine, non-replayable
behavioural signal (pathAdherence over a per-instance random curve); a
two-key script can't solve it.
- over-coupled abstraction: interaction + animation are decoupled from the
component identity (per-stage consumer slots; challenge is pluggable).
Layers:
- morfo: proof-of-human slimmed to orchestrator (Provider + 3 commits); new
path-trace morfo (Provider + Track + Token; handle-*).
- soma: orchestrator + ProofOfHumanChallengeHost; new path-trace (seeded
Catmull-Rom curve, corridor-gated monotonic progress, adherence telemetry,
pointer + arrow-key input). Old drag Token/Target wrappers retired.
- sema: verdict commits (proof-of-human) + trace handle earcons (path-trace).
- eidos: orchestrator + PathTrace that paints the curve/corridor/trail.
- demo rebuilt: carrier-pigeon skin over the per-stage slots.
Auto-focuses the token when the challenge is armed so the arrow keys start the
trace immediately (gated on the disabled->enabled transition).
Verified: 32/32 tests (incl. arrow trace completes with real signal + two-key
script can't solve, in real chromium) + visual in real Chrome (curve, failed,
verified; scorer rejects instant traces).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Record the last two F4-D optionals as resolved: (1) anti-CLS metric fallbacks
shipped (efc3f4c3); (2) the container-query dogfood is DECLINED — the framework
sizes via explicit `data-size` (79 components), zero are container-aware, so
routing a shipped recipe through `@container` would introduce a paradigm the
framework does not use (speculative, against CLAUDE.md §2). The seam stays a
documented reserved axis, already exercised by the test's synthetic recipe;
dogfood it when a real container-relative layout need arises. F4-D queue empty.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Workflow analysis (state map + 4 dispositions + adversarial stress) resolved the
direction dispute: the stagger/coordinated preset winning the (0,3,0) tie over
the generic commit-affirm firma is the DOCTRINE-CORRECT outcome (RFC B.2 — the
coordinated owns the visual axis, the generic firma is silenced). The earlier
"flip to firma" reading was backwards; that flip contradicts B.2 and its
channels:[] safety net is dormant; @layer sinks as a front-line fix (a semantic
ownership question answered with a global cascade tool + a reduced-motion
!important-inversion a11y risk).
No shipped component shows a defect (dropdown select feedback is sound+haptic
only; the item is fading out at select). The only real issue: the correct
outcome rides on an ACCIDENTAL source-order tiebreak (the explicit neutralization
was retired), undocumented + untested. Execute the proportionate fix:
- Regression test (motion.test.ts) pinning the (0,3,0)==(0,3,0) tie + the
signatures-before-presets emission order + the absence of a (0,4,0)
[data-event^='commit-select'] signature — a HAZARD MONITOR, so a generator
reorder or a widened signature fails loudly, not silently.
- Load-bearing comment at the render-css emission order.
- Doctrine note in motion.md: a known-fragile accident, not a contract; the
structural endpoint ("una firma por evento") + @layer are deferred/tracked.
Zero behavior change; base.css unchanged. Tests 28/28; npm run check src/ = 9
baseline, 0 new.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`--depth-{plane}-scrim` was declared (DepthPlane field, DEPTH_CUES, validation,
applyDepth) but never seeded, emitted or read — a write-surface with no reader
(applyDepth({p:{scrim}}) silently no-op'd; never present in generated base.css).
Analyzed via workflow (state map + 4 dispositions + adversarial stress). The
four collapse to prune vs promote-to-z-parity: wire-and-unify SINKS (contradicts
the test-locked z open-cage precedent + the MD3/Radix/Vaul consensus that the
veil is component-owned, not an elevation cue), relocate flips its verb to
retain, and complete-to-z-parity would mint a third alias of the --color-overlay
backdrop role. Unlike z (kept as an exposed open cage — it varies per plane and
is unpaintable-forever), scrim has no per-plane variance and no value to seed.
Prune: remove scrim from DepthPlane / DEPTH_CUES / validation / emitter +
applyDepth doc. Amend rfc-depth.md (token contract + Phase-4 note + atmosphere
rows), reference.md cue list, changelog, pendiente. The modal veil stays where
MD3/Radix/Vaul + the standing decision put it: --{component}-overlay-* tinted by
--color-overlay.
Zero observable change (never emitted). Tests 71/71; npm run check src/ = 9
baseline, 0 new; base.css unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Close the no-decision F4-D GAP items after the professional (reference-grounded
+ adversarial) analysis. Constant finding: near-zero real debt — most items are
deliberate reserved axes or stale audit premises.
- layer-4 / depth-z / container-queries -> KEEP (documented reserved axes; MD3/
Primer/Radix/Tailwind/DTCG precedent). The audit's "layer 4 sin consumidores"
is stale — it's consumed by banner/toast/avatar/editable/words + the THM-2
fallback. Fix the one real defect: the false comment at render-css.ts depth
block claiming the floating-wrapper consumes --depth-{plane}-z (it reads the
computed z from --{component}-content-z / --z-index-*; depth-z is an unwired
reserved read-surface).
- Register the 4 deliberate limits (LIM-SND/ICON/TOUCH/RECIPES) in the gap-log.
- assets-variables shipped in 7d4f9500; scrim + motion-direction remain design
decisions (documented, not executed to avoid pinning the wrong call).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ship the base theme's default families (Instrument Sans / Lora / Azeret Mono --
identity unchanged) as self-hosted VARIABLE woff2 (SIL OFL, latin subset, wght
range, preload on primary), so the engine's variable path is exercised by the
default theme, not merely capable. The hand-written themes/fonts.css is retired
(@font-face is config-driven from typography.families[].faces); the static TTF
set under static/fonts/air is removed.
Also close the theming-API gaps so it supports ANY variable font, not just
wght+opsz:
- FontAxes is now a 4-letter-tag map. renderFontFace emits a descriptor per
registered axis: wdth -> font-stretch: min% max%, slnt -> font-style: oblique
(OpenType slnt negated to the CSS oblique sign), alongside wght/opsz.
- TypographyStyle.variationSettings -> --style-{name}-font-variation-settings,
applied by the Text recipe (fallback normal), engaging non-standard axes
(GRAD/MONO/custom) and fixed axis defaults.
Verified: variable faces load with weight ranges + mid-axis rendering; new axis
+ variation-settings emission covered by active-eidos-config.test.ts (99 pass);
npm run check src/ = 9-error baseline (0 new); no default-font regression.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a "Foundation globals" section to eidos/archetypes.css (document-wide,
not archetype-keyed) closing the two zero-coverage systemic gaps + the
scrollbar item from the F4-D GAP menu:
- ::selection — translucent accent wash (background only, text keeps its
colour so it stays legible in light and dark)
- caret-color — accent identity on input/textarea/contenteditable, in
:where() so a field recipe can override (invalid -> threat)
- native scrollbars — themed + thin via the STANDARD properties on :root
(Firefox + Chromium >= 121). Heavy ::-webkit-scrollbar deliberately
skipped: ScrollArea draws overlay bars and hides the native one inline
(scrollbar-width: none on its viewport, which wins). Thumb colour matches
ScrollArea's thumb.
Cursors were already archetype-wired (trigger/close/action/item/option =
pointer, thumb = grab/grabbing, disabled = not-allowed, text zones = native
input + explicit cursor:text in date-field/editable) — no change needed.
All values read role tokens (--color-primary-solid, --color-content-muted),
lowest specificity so any component recipe still wins.
Verified live (tags-input demo, base-dark): input caret = --color-primary-solid
exactly; scrollbar-width thin applied; ::selection rule present + token
resolves; docs-harness :root override wins (app > foundation). npm run check
src/ = 9-error baseline, 0 new.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a close-of-session state block at the top: what shipped this session
(media-player Batch-4, THM-2 full rollout + avatar + closure, THM-4
5-hotspot adjudication) with commit refs, the real-component verification
method, and the remaining §9 / side queue (F4-D GAP scoping, motion
cascade-vs-signatures).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adjudicate color-picker (29), avatar (28), time-range-picker (26): all
0 debt — composition (de-dialoged pickers compose Popover; avatar's
per-role color now lives in the shared layer after the THM-2 migration;
sliders/toggle composed), behavioral (sema last-action, format, endpoint,
loading status), and per-file-lint blind spots (recipe-token variant
rules). Across the 5 biggest hotspots (156 unused) the only real debt was
stepper's dual-representation API, now consolidated. Confirms the doctrine:
the unused column stays informational, no machine rule warranted. THM-4
closed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Every step part (item/trigger/indicator/separator) declared BOTH
`data-state` (enum) AND `data-current`/`data-complete`/`data-incomplete`
(booleans) — the same state twice, used inconsistently (enum on
trigger/indicator, boolean on separator). Consolidate on the canonical
`data-state`:
- morfo: prune the 12 boolean declarations.
- provider: drop the current/complete/incomplete sources
(item/indicator/separator) + the trigger's explicit boolean writes +
the now-unused isComplete/isIncomplete getters (isCurrent stays — it
drives aria-current/tabindex).
- separator.css: [data-complete] -> [data-state='complete'].
- README + provider test updated to the single form.
Verified with the real component (Next advances → complete/current/
incomplete via data-state, boolean attrs gone, complete separator still
paints). stepper unused 30->18; provider test + contracts green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Burn the two biggest THM-4 hotspots. media-player (47 unused): 0 real
debt — all composition artifacts (composed Buttons/Sliders), behavioral
state mirrors of the media element, default-state enum values, or
documented-v2 roadmap surfaces (settings-button, custom captions).
stepper (30 unused): mostly legit + one API redundancy — every step
part declares both data-state (enum) and data-current/complete/incomplete
(booleans), used inconsistently (enum on trigger/indicator, boolean on
separator). Consolidating is a public-API design decision, not a prune.
Emerging read (2 batches): hotspots are mostly legitimate; genuine
declared-for-nothing debt is rare, confirming the doctrine — the debt
rate does not justify a machine rule. THM-4 is mostly documented
adjudication.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Record the rollout as shipped (05f0031c + cc7e9a16) and re-label the
Avatar.Badge as a deliberate design boundary, not debt: a status badge
takes a role/intent or a direct CSS color (MUI/Spectrum/Bootstrap =
semantic; Chakra/Mantine/Ant = direct), and no framework routes an
avatar's status badge through a brand-scale palette. THM-2 closed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The avatar body's per-instance palette now resolves through the shared
per-instance layer (size collapse, behavior-preserving for roles, adds
the 33 scales):
- avatar.svelte: isCanonicalColor recognizes palette scales, so a scale
name routes to data-color instead of the custom CSS-color path ('teal'
is also a CSS color name, so it used to fall to --avatar-color-custom).
- recipes/base.ts: _palette-{solid,surface,contrast,text,border} (host
fallback only - the avatar always emits a canonical data-color, so the
presence-guarded forward routes roles + scales) + _bg/_fg/_border move
from a variant x color matrix to variant-only readers of
--_avatar-palette-*. The matrix helper + intermediates stay (the badge
still uses them; 0 orphans). avatar.css unchanged.
- demo: scale picker.
Verified end-to-end with the REAL component (drive the demo color control
-> the avatar emits data-color -> the shared layer resolves the exact
scale): affirm+soft behavior-preserving, teal+soft = scale-teal-a2
(translucent surface), teal+solid = scale-teal-9.
The Avatar.Badge sub-part (aria-hidden decorative chip with its own color
+ default) stays on its role palette + custom-CSS: migrating it needs a
per-part forward + pruning ~36 orphaned intermediates, disproportionate
for a decoration whose arbitrary-color case already works. Documented for
a decision.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
media-player Batch-4 (closes SOM-4): the 10 sub-parts migrate to
renderProps() (35 attr-dup hits -> 0). Register the sources the morfo
reads (label propRef, on stateRef, direction/type) so renderProps emits
type/role/aria-*/data-state; keep only contract-only soma-owned attrs.
Pin the soma-attr-audit ceiling to 0.
THM-2 rollout - collapse each per-component palette cascade into the
shared per-instance layer (size win, behavior-preserving for roles,
adds the 33 scales):
- Rename palette tokens to canonical _palette-{slot} + route via the
presence-guarded forward: tags-input, stepper, badge, card, select
(parts-aware trigger+content), file-upload, editable, tag-group.
- Extend the shared vocabulary with alpha surface(a2)/surface-hover(a3)
slots (soft variant of variant-aware recipes) and hover(4) - the
latter also fixes Toggle's latent scale-hover.
- tag-group: variant-aware migration (solid/outline -> canonical,
soft -> alpha surface; merge outline-fg into text, prune orphans).
- toggle-group: replace the 4-role cross-recipe composition with a
hand-authored presence-scoped route to the shared layer (the
composition DSL can't presence-scope); covers roles + 33 scales.
- Widen color to ComponentColor (roles + 33 scales) where arbitrary
color is meaningful: badge, tag-group, tags-input, toggle-group, and
add the scale picker to their demos. Semantic controls stay restricted.
Verified end-to-end with the REAL components (drive the demo color
control -> the component emits data-color -> the shared layer resolves
the exact scale). Gates: recipe-css-contract/config/contracts/
soma-attr-audit/media-player suites green; 0 orphan tokens.
avatar remains for a focused pass: its variant-resolved matrix and badge
sub-part don't fit a mechanical migration.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per request: one `colors` prop that discriminates by shape instead of two
rival props. A `string[]` is an explicit stop list (unchanged); a bare
`string` names a canonical gradient from the theming's named set, resolved as
`var(--gradient-{name})` — exactly what the removed `preset` prop did. The set
is an open cage (`EidosConfig.gradients` / `applyGradients`), so the type stays
`string | readonly string[]`, not a closed union. Removed `preset` outright (no
back-compat shim, per project rule); the canonical S9 vocabulary and raw stops
are now the SAME input.
Docs shell: register a small canonical named set on `uix/+layout@` via
`applyGradients` (`brand`, `spectrum`) — role-derived, so they re-tint with the
seed and flip light/dark. Demo gains a live stops|named toggle so both branches
are a real testbed.
Finding worth recording (types + README): a named gradient painted through
`background-clip: text` must be a single `<image>` (linear/radial/conic). A
role-derived MESH serializes to radials + a base COLOR, and a bare color is not
a valid `background-image` (only the `background` shorthand accepts it) — so a
mesh name computes to `none` and never paints. The docs set is linear-only for
this reason.
Verified in real Chrome: stops branch paints the token gradient; `colors="spectrum"`
paints `var(--gradient-spectrum)` through the text. svelte-check 0 own errors,
component:audit 141/0/0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Estado: F0·F1·F2·F3·F4-C·SEM-4·THM-2 ✅ commiteadas/pusheadas. El bloque
ARRANQUE reescrito: media-player Batch-4 (cierra SOM-4) con el diagnóstico
completo (10 sub-partes hardcodean attrs que el morfo declara; migración
renderProps parte-a-parte; plantilla carousel/table; cierre con ceiling +
navegador) para ejecutar en frío sin re-scopear. Cola posterior: rollout
THM-2 (12) · hotspots THM-4 · F4-D GAP (scoping) · cascada-vs-firmas.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>