astra
alpha-0.1-background
alpha-0.1-dir-prefs
alpha-0.1-sec-dom
menubar-v4-safe
active-uix
morfo-runtime
morfo-driven-soma
semantuix
glm-5
main
sium-v1.0
${ noResults }
177 Commits (f19e91b985180af8516742d2166411a55765998c)
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
f19e91b985 |
uix(empty-state): F1.2 · componente base display (morfo+eidos) al suelo del dossier
Primera pieza F1 del plan blocks (PLAN-blocks.md; alcance E-2 = suelo de paridad, dossier §P3). Ruta de 9 fases completa: - morfo: 5 partes display (provider/media/title/description/actions), scope ['eidos'], 0 eventos con justificacion pasiva (patron renderEmptyState de las refs headless), Title role:'heading', texts.label - langs: components.empty-state.label (es/en) — el indice tambien recoge la retirada foranea del import de words (inseparable por staging de archivo; coherente con la migracion palabras ya enviada; words.ts sigue en su arbol) - eidos: compound EmptyState + Media(kind icon|media, placa 2x glifo) + Title(level 2-6, default h3 — modelo Atlaskit, tamano visual desacoplado) + Description(measure 45ch) + Actions(label -> role=group +aria-label, buttonGroupLabel); recipe sobre el bundle --size-* (titulo un paso discreto arriba; sm=in-collection, lg=hero); tokens publicos minimos (gap/actions-gap/media-bg/media-fg/media-radius/description-measure) - demo v2 9 tabs (harness: SystemAxes/MotionPanel/SemaPanel; snippet con paridad; escena in-collection via Card) + entrada nav (grupo Status) - README: Baseline · Comparativa (shadcn/Chakra/Atlaskit/AntD/Polaris) · Decisiones · Passive justification · Gaps con disposicion Verificacion: component:audit PASS · eidos-lint 5 morfo-backed + 4 eidos-only sancionados · morfo:check + morfo:vocabulary verdes · recipe-css-contract/api-contract/visual-attrs 37/37 · svelte-check 76E/51W = baseline exacto (cero regresion) · navegador claro Y oscuro por estilos computados (titulo 18->24px, placa 40->64px, chips vivos, cero errores de consola). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
3 months ago |
|
|
9a7a438a96 |
fix(emoji-picker): panel encaja en el Popover + ancho tokenizado, sin overflow
El contenido (331px) desbordaba el Popover (~284px, --popover-min-width-md) y el viewport lo recortaba con scroll horizontal — la ultima columna de emojis salia cortada, evidente en oscuro. Fixes: - El Popover recibe su ancho via prop `width` = TOKEN `--emoji-picker-panel-width` (23rem, token de componente al estilo color-picker content-width — NO hardcode). El contenido pasa a inline-size:100%; nada desborda el popover. - PopoverContent size="sm" (padding mas ajustado) + categorias en ToggleGroup size="xs" → las 9 caben sin scroll (antes 6/9). Tonos size="sm". - scrollbar-width:thin en el grid (el unico scroll, vertical, esperado). Tamaños: iconos/glifos canonicos (Icon size, --size-lg/xl-font-size, ToggleGroup size); grid-max-block-size min(50vh,17rem) replica el patron de command.css. Verificado en claro Y oscuro (capturas): content Hscroll 0, strip Hscroll 0, 9 categorias visibles, celda 36px, un solo panel; component:audit PASS · eidos-lint invalid 0 · recipe-contract 30/30 · check baseline 76. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
3 months ago |
|
|
d59b8af9aa |
uix(chat): v2 · emoji-picker (picker completo) + escaparate rico del bloque
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> |
3 months ago |
|
|
f9889b0b18 |
uix(chat): v2 · chat-list — panel de conversaciones (compone Listbox)
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> |
3 months ago |
|
|
8fe9d1faaa |
uix(chat): v2 · jump-to-message highlight en chat-message
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> |
3 months ago |
|
|
e838e7107b |
uix(chat): v2 · read-by list — parte ReadBy en chat-message
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> |
3 months ago |
|
|
4c4878f139 |
uix(chat): bloque de mensajería chat-* v1 (log · message · composer · typing)
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> |
3 months ago |
|
|
b93cc6c5c0 |
uix(color): arregla el custom-color roto de Card y Avatar (fill)
BUG (descubierto en la verificación adversarial de la iniciativa): un color
CSS crudo (p.ej. `color="#3b82f6"`) NO pintaba el FILL de Card ni Avatar —
caía a neutral. Card/Avatar (Fase 2a) usaban un split bespoke pre-helper
(`isCanonicalColor` + seed `--{c}-color-custom` + bloque derivador
`[data-{c}][data-color-custom]{--_{c}-palette-*: …}` en su .css). Ese bloque
quedó ENSOMBRECIDO por el forward compartido de THM-2 en `generated/base.css`
(misma especificidad (0,2,0), carga después → gana); para un valor crudo
`--palette-*` está sin definir (el shared layer lo deriva de `--color-custom`,
NO de `--{c}-color-custom`) → host neutral.
Fix (los alinea con TODO el resto de la iniciativa):
- card.svelte / avatar.svelte: el split bespoke → `resolveComponentColor`
(genérico: canónico→`data-color`; crudo→`data-color-custom` + seed
`--color-custom`, que el forward YA consume). Ambos ahora COMPONEN el seed
con el `style` del consumidor (`composeInlineStyle`) para que `...rest` no lo
pise — Card lo pisaba (style antes de rest), por eso el custom se perdía
incluso con el seed correcto.
- card.css / avatar.css: borrados los bloques bespoke `[data-{c}][data-color-
custom]{…}` (muertos + ensombrecidos). Las variant slices leen
`--_{c}-palette-*`, que el forward llena para rol/escala/custom uniformemente.
- Avatar RING y BADGE son canales de color SEPARADOS (`--avatar-ring-color-
custom` / `--avatar-badge-color-custom`): NO se tocan — usan su propio
namespace de tokens, el forward no los ensombrece y funcionan (verificado).
- base.ts: quitados los 4 tokens de recipe muertos (`card`/`avatar` ·
`color-custom` + `color-custom-contrast`); ring/badge se conservan.
Verificado en Chrome: Card real (picker del demo) + Avatar sintético pintan el
custom en soft/solid/outline/ghost, componiendo el style del consumidor;
roles/escalas siguen exactos (behavior-preserving); ring + badge intactos.
Orphan test resuelto (los `-contrast` ya no huérfanos). Los 2 fallos residuales
del contract son WIP de chat-composer (otro track, sin commitear), ajenos.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
3 months ago |
|
|
6cdba0182f |
uix(color): cierra 2 fugas transitivas (card-group-item · s-text) + guard runtime
Verificacion adversarial multi-agente (17 componentes de la sesion de cierre,
23 agentes) cazo 2 fugas que la verificacion individual en Chrome no cubrio —
componentes abiertos TRANSITIVAMENTE (heredan un alias abierto) y nunca
tocados ni verificados:
- **card-group-item**: estampaba `data-color={color}` CRUDO en ambas ramas
(button + div) sin `data-color-custom` ni seed → un valor CSS crudo (que el
tipo abierto `CardColor=ComponentColorProp` permite) caia a neutral y se
descartaba en silencio. Ahora pasa por `resolveComponentColor` (ruta
GENERICA `--color-custom`, que el forward compartido `[data-card][data-color-
custom]` consume). NOTA: NO usa el split bespoke de Card (`--card-color-
custom`) a proposito — esa ruta esta ENSOMBRECIDA por el forward de THM-2 y
no pinta (bug pre-existente de Card/Avatar, flageado aparte). Verificado en
Chrome: teal=escala exacta, #3b82f6=palette-solid exacto, neutral=sin estampa.
- **s-text**: `STextColor = TextColor` se abrio gratis al ensanchar TextColor
en Fase 4, pero su .svelte seguia en el puente pre-Fase-4
(`var(--color-content-${colorKey})` para TODO valor) → escalas/intents/crudo
producian vars inexistentes. Migrado a la rama eje-primero de Fase 4 (espejo
de Text): ink axis inline sin estampa; resto por el forward. Recipe
`_palette-text` + regla colored-only + stamp. Verificado en Chrome
(risk/teal exactos, custom aplicado, default sin estampa).
- **split-button**: revisado, SIN cambios — delega `{color}` crudo en
ButtonGroup→Button (ya verificado); no re-estampa.
Guard runtime NUEVO en recipe-css-contract.test.ts — "pairs a dynamic
`data-color` stamp with `data-color-custom`": el guard de tipos es ciego al
runtime (solo lee types.ts), asi que un stamp dinamico sin el par custom pasa
inadvertido (justo la clase de card-group-item). Este falla si algun .svelte
estampa `data-color={…}` dinamico sin `data-color-custom` en el mismo fichero;
literal estatico (`data-color="neutral"` del badge +N de avatar-group) exento;
WIP excluido. 30/30 verde.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
3 months ago |
|
|
67d7dc89cc |
uix(color): Fase 4 — tinta de contenido en text · heading · display · code · label
Los 5 primitivos tipograficos tenian OTRO eje de color: la tinta de contenido
(primary|secondary|muted|disabled|on-solid, donde primary = --color-content-
primary, NO el rol), interpolada inline por el wrapper (--_x-color) sin
data-color ni cascada CSS. Abrir el tipo a pelo rompia: color='teal' →
var(--color-content-teal) inexistente; 'muted' via resolveComponentColor →
--color-custom: muted invalido.
Apertura con RAMA EJE-PRIMERO (identica en los 5):
- tipo: `ComponentColorProp | 'muted' | 'disabled' | 'on-solid'` (label sin
on-solid — conserva su union exacta de 4). primary/secondary ya estan en
ComponentColorProp pero la rama los captura ANTES y les conserva el
significado de TINTA ("conserva su eje", handoff §Fase 4).
- wrapper: valor ∈ eje → var inline como siempre, SIN estampa; resto →
resolveComponentColor (canonico → data-color; raw → data-color-custom +
seed). Los dos caminos son mutuamente excluyentes.
- css: regla colored-only `--_x-color: var(--_x-palette-text)` — no pelea con
el eje porque el eje nunca estampa.
- recipe: `_palette-text` nueva por componente (host = fallback-only).
Text NO estaba en la lista del handoff (code/display/heading/label) pero tiene
la MISMA union cerrada y es el primitivo modelo de la familia — incluido y
declarado (dejarlo fuera era una incoherencia de la propia fase).
Verificado en Chrome: heading con las DOS ramas y prop real (chip muted →
inline var sin estampa; risk/teal/custom = probe exacto); label idem (la demo
monta color='primary' real → inline, y risk exacto sin el inline); text/
display risk/custom exactos; code el token fluye exacto — su pisoton visible
es una colision PRE-EXISTENTE del chrome de docs ([data-uix-docs] code, ya
pisaba el eje viejo; señalada como tarea aparte). Contract test 28/28 —
primera vez todo verde.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
3 months ago |
|
|
04659558d4 |
uix(color): abre `color` en proof-of-human + exencion fixed-tone formal
Apertura (era el ultimo standalone con cascada): - El accent del reto (2 slots: solid + element-soft) pasa de 7 bloques per-rol al forward compartido en la parte `root` (display:contents → cascada a todos los retos, verificado que el skin hereda). Recipe NUEVA (su comentario "needs no recipes/base.ts entry" quedaba obsoleto). - `ProofOfHumanColor = ComponentColorProp` (era ColorRole, README decia "8 roles-only"). Root + los 3 retos (clock/path-trace/rotate-align) estampan via resolveComponentColor — sus stamps son CSS-muertos hoy, pero con el tipo abierto habrian estampado raw strings como data-color. Exencion fixed-tone (los "2 fallos pre-existentes" del contract): - FIXED_TONE_COMPONENTS se hoista y comparte entre el test de color Y el de font-size, añadiendo proof-of-human: la paloma mensajera + carta son paleta identitaria del objeto dibujado (anotada linea a linea) y los font-size 13px/11px son unidades de usuario SVG del viewBox, no escala tipografica (anotacion añadida al .ra-card que no la tenia). Nota: esto tambien exime a natural-time-picker/color-picker del test de font-size (hoy limpios; su doctrina fixed-tone ya esta documentada). card-group: revisado, SIN cambios — card-group-item ya usa `CardColor` (= ComponentColorProp desde Fase 1): abierto por delegacion. Verificado en Chrome: default primary = probe exacto (2 slots), risk/jade exactos, skin hereda por display:contents, custom → seed + mix element. Contract: mis 2 pre-existentes ELIMINADOS; unico fallo restante = chat-message `--radius-xs` (WIP vivo de otra sesion, ajeno). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
3 months ago |
|
|
18026dfd84 |
uix(color): abre `color` en float-panel
El accent (regla del header, focus ring, grip de resize) pasa de 8 bloques per-rol al forward compartido en la parte `content` (el panel portala y estampa su propio `data-color` via ctx). El default real NO era el "fantasma" que temia el handoff: la recipe SI tiene `accent: neutral-border` y el root siempre estampa `color='neutral'` por defecto → estampa-siempre + regla generica es fiel (host del forward = neutral-border, el valor del default). El fallback sin-ctx (`var(--float-panel-accent)`) queda intacto. Tipos: `FloatPanelColor = ComponentColorProp` (era ColorRole); el Content resuelve via resolveComponentColor con seed `--color-custom` para raw values. Verificado en Chrome: default neutral = probe exacto; threat/iris = token compartido; sin estampa → fallback neutral-border; custom → color-mix del seed. Contract test sin fallos nuevos. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
3 months ago |
|
|
c9db0a8d26 |
uix(color): abre `color` en color-picker (cross-portal resuelto)
La trampa: el accent vivia en DOS scopes — cascada per-rol en el root y una re-declaracion fija `primary-border` en el contenido portalado. El forward del root nunca alcanzaba el portal: el prop `color` solo tintaba el ring del trigger; los rings del portal (area-thumb, slider-thumb, eye-dropper) eran SIEMPRE primary. Solucion (espejo de select): el forward apunta a las DOS partes que estampan — recipe `_palette-border` con `parts: ['trigger', 'content']` (fusionado en la recipe existente). El trigger consume el token directo en su ring; el content re-deriva su `--_color-picker-accent` del token EN SU scope (sus descendientes lo heredan como antes). Root: fuera la decl base + la cascada de 7 roles; el provider solo estampa el marcador canonico y el color RAW viaja por contexto (Trigger/Content/ChannelInput lo resuelven cada uno via resolveComponentColor, con seed `--color-custom` para valores raw). El marker div del ChannelInput enruta ademas el forward de color-field, como un ColorField standalone. Tipos: `ColorPickerColor = ComponentColorProp` (era ColorRole). Verificado en Chrome con el popover abierto: content portalado estampa su propio data-color; default primary = probe exacto en trigger y content; risk/teal en el scope del portal = token compartido; custom → color-mix del seed; el area-thumb hereda el accent. Contract test: mis cambios no añaden fallos (los 2 de chat-composer son WIP de otra sesion con recipe fuera del tree; 2 de proof-of-human pre-existentes). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
3 months ago |
|
|
6b37beed69 |
uix(color): abre `color` en field-langs · radio-cards · image
Tres aperturas de bajo peso: - field-langs: delegante puro (patron E) — pasa `color` al `<Field>` compuesto (abierto en Fase 3). Solo tipo: `FieldLangsColor = ComponentColorProp`. - radio-cards: comparte elemento provider con radio-group, cuyo forward ya enruta la paleta que las reglas de card consumen. Tipo abierto (era AffirmativeColorRole) + ruta custom en el wrapper (seed `--color-custom`). - image: migra el mecanismo bespoke pre-helper (`data-placeholder-color` + seed propio) al forward compartido, paridad con avatar (su gemelo declarado). Arregla de paso las escalas: `placeholderColor='teal'` caia a la rama custom y pintaba el NAMED COLOR css `teal` (hue equivocado) y `'iris'` era invalido; ahora resuelven a la escala canonica. `'neutral'` (default) no estampa y conserva el fallback neutral-track. Fuera el token de recipe `placeholder-color-custom` (huerfano tras la migracion). Verificado en Chrome (probe): radio-cards risk/mint/custom en el forward y el item checked consume borde+fondo (el gris inicial era la transition congelada del pane suspendido, caso conocido, verificado con transition:none); image default=neutral-track exacto, risk/teal=escala, custom=color-mix; field-langs estampa via Field. check: 0 errores en tocados; total 77 = 76 baseline + 1 de web/routes/uix/demos/chat (WIP de otra sesion). Contract test 26/28. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
3 months ago |
|
|
838b01a005 |
uix(color): abre `color` en metrics (featured icon) + form (submit/reset)
Dos aperturas por partes (`parts:` en recipe, forma `declarations:`): - metrics-icon: los 5 slots del featured icon (track/solid/text/contrast/ border) pasan de 8 bloques per-rol al forward compartido en la parte `icon`. Fuera el guard roles-only del wrapper (caia a neutral con cualquier valor no-rol); `plain` sigue sin estampar. Default primary estampado = identico. El flash de valor NO se toca: gradua por `data-event-intent` (sema), no por el prop `color`. `MetricsChartProps.color` queda cerrado: delega en la familia chart, que NO esta en la cola del handoff (hueco señalado aparte). - form submit/reset: 4 slots (solid/solid-hover/border/contrast) en las DOS partes via el mismo forward. `'neutral'` (default de reset) NO estampa: la base action look (element bg + text ink) ES el neutral — estamparlo habria cambiado reset a solid grey. Submit default primary = identico. Verificado en Chrome (probe getComputedStyle): metrics-icon risk 5/5 slots + crimson + custom (contrast blanco derivado); form threat 4/4 + iris + custom; reset sin estampa con look base intacto. check: 0 errores en tocados (total 76 = baseline); contract test 26/28 (2 pre-existentes proof-of-human). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
3 months ago |
|
|
60a30c2e60 |
uix(color): abre `color` en skeleton · spinner · textarea
Tres standalones al forward de paleta compartido (`resolveComponentColor` +
recipe `_palette-*`):
- skeleton: tinta el wash (slot track, 50% alpha) + el highlight del shimmer.
Colored-only: el default `'neutral'` NO estampa data-color y conserva su
mezcla base cross-mode (oklab + 16% solid). Delta declarado: affirm/fulfill/
risk/threat/loss ganan highlight tintado en el shimmer (antes solo primary/
secondary lo definian; la regla generica uniformiza la cobertura).
- spinner: patron C puro — la base consume `--_spinner-palette-{text,track}`
(host = primary, behavior-preserving); fuera los 7 bloques per-rol.
`'inherit'` se conserva como union aditiva con su regla literal
(currentColor); no puede viajar por el seed custom (trampa `--x: inherit`).
- textarea: tinta el borde de focus (slot solid). Colored-only: el default
`'primary'` no estampa y conserva `--focus-ring-color` (que NO es
primary-solid: es un mix al 52% — estampar habria cambiado el default).
Verificado en Chrome (probe getComputedStyle): default intacto en los tres;
risk/teal/amber/jade = token compartido exacto; custom hex → color-mix del
seed. check: 0 errores en los componentes tocados; contract test 26/28 (2
pre-existentes de proof-of-human).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
3 months ago |
|
|
1efa034397 |
uix(color): abre `color` en link + mark (tinta de contenido)
Migra los dos content-ink al forward de paleta compartido:
- link: `color` tinta el texto. Base pasa a `color: var(--_link-palette-text)`
(host = primary-text); se quita la cascada per-rol de 8 bloques. hover/active
siguen en primary (comportamiento pre-existente). Recipe nuevo `_palette-text`.
- mark (resaltado): `element` tinta el fondo (default = fulfill, el resalte
clásico), `text` tinta la tinta SOLO cuando hay color (la base sigue
`color: inherit`; no se puede poner `inherit` como host de custom-prop — trampa
del `--x: inherit`). Regla añadida `[data-mark][data-color],[…custom]{color:…}`.
Ambos wrappers pasan por `resolveComponentColor` (custom hex → data-color-custom
+ seed). Verificado en Chrome: link risk→texto risk, custom→color-mix; mark
fulfill por defecto, risk→fondo risk, custom→color-mix.
Nota: `check` marca 94 (no 76) por la caché incremental de svelte-check aflorando
errores `any` pre-existentes en demos ajenos (heroscrolling/animations/palabras/
alpha/temas); CERO errores en componentes tocados (link/mark limpios).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
3 months ago |
|
|
528ddaa7f9 |
uix(color): abre `color` en listbox · timeline · combobox
Migra los 3 al forward de paleta compartido, cada uno con su matiz: - listbox: 2 slots (solid = indicador activo, element = fila seleccionada), raíz bare `[data-listbox]`. El `accent-text` era código muerto (sin consumidor) → eliminado. Recipe nuevo. - timeline: 5 slots (solid/track/border/contrast/text) que alimentan `--_timeline-hue-*` del item; data-intent por item sigue mandando sobre el acento del root. Tipo ProgressiveColorRole → ComponentColorProp. Recipe existente ampliado. - combobox: la cascada vivía en CUATRO partes (control/input/trigger/content), así que el forward las cubre vía `parts: [...]` (forma declarations[]). 3 slots (track/border/text). Los 5 wrappers (root + 4 partes) pasan por `resolveComponentColor` para que el custom hex no se estampe crudo. Recipe existente (content-z) ampliado. Verificado en navegador: combobox data-color=risk → palette-border del control = token risk compartido; custom #00b894 → color-mix. table (lote previo) idem. check: 76 ajenos, 0 propios. Contract 26/28 (2 = proof-of-human pre-existente). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
3 months ago |
|
|
c5699bd019 |
uix(color): abre `color` en la familia lista/data-grid (10 componentes)
Migra table · tree-grid · tree-view · virtual-list · virtual-grid · feed ·
drag-drop · grid-list · clipboard · carousel al forward de paleta compartido.
Patrón 2-slots: `--_{c}-accent` (indicador sort/activo) -> `--_{c}-palette-solid`,
`--_{c}-accent-soft` (tinte de fila seleccionada) -> `--_{c}-palette-element`.
Recipe nuevo con host = primary; el generador emite base default + forward. Se
quita la cascada per-rol de cada css; tipo -> ComponentColorProp; wrapper vía
`resolveComponentColor`.
Detalles no uniformes resueltos:
- Los 7 que estampan `data-color` en `[data-{c}-root]` usan `parts: ['root']`
(forma declarations[], la única que el generador lee para parts) -> forward
`[data-{c}-root][data-color]`. Los 3 bare (grid-list/clipboard/carousel) van
al selector base.
- drag-drop ya tenía recipe (preview-z) -> fusionado, sin duplicar clave.
- tree-grid/tree-view solo tintan la fila (element); su `accent` (solid) no
tenía consumidor -> declaran solo `_palette-element`.
- virtual-list/virtual-grid tenían la cascada accent MUERTA (sin consumidor):
contenedores virtualizados sin visual por color. Tipo abierto (guard) +
wrapper inerte, pero SIN recipe (evita token de paleta huérfano).
Verificado en navegador (demo table): data-color=risk -> palette-solid/element
= tokens risk compartidos exactos; custom #00b894 -> solid=#00b894, element=
color-mix(...). check: 76 ajenos, 0 propios. Contract 26/28 (2 = proof-of-human).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
3 months ago |
|
|
889b03dae1 |
uix(color): abre `color` en year-grid + range-calendar (completa la familia calendar)
Mismo patrón mini-recipe que month-grid: recipe con 5 tokens `_palette-*`
(host = primary) -> el generador emite base default + forward
`[data-{c}][data-color], [data-{c}][data-color-custom]`; se quita la cascada
per-rol de la css y se renombran los consumidores accent -> palette; tipo ->
ComponentColorProp; wrapper vía `resolveComponentColor`.
- year-grid: espejo exacto de month-grid (5 slots, sin cross-refs).
- range-calendar: el END endpoint + `--calendar-range-end-*` bind a
`--_range-calendar-palette-*`; el swap de START a secondary para affirm/fulfill
(distinción de extremos) se conserva como regla per-rol (no es paleta).
Verificado en navegador (demo range-calendar): data-color=risk -> palette-solid
= range-end-solid = token risk compartido; affirm -> start = secondary; custom
#00b894 -> range-end = #00b894. check: 76 ajenos, 0 propios. Contract 26/28 (2 =
proof-of-human pre-existente).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
3 months ago |
|
|
861427fe12 |
uix(color): Fase 3 (parcial) — abre `color` en field-family · calendar · month-grid · time-picker/range
Continúa la apertura de la jaula del color hacia el sistema completo
(role · intent · 33 escalas · valor custom), tipando a `ComponentColorProp`
y enrutando por el forward `--palette-*` compartido:
- Field family: field base gana el forward `_palette-text`; search-field
y 8 hojas (password/mask/number/css/color/date/date-range/time-field)
adoptan `data-field` y heredan el forward. Wrappers vía
`resolveComponentColor`; demos con picker de roles/intents/escalas/custom.
- Calendar base: recipe `_palette-{solid,solid-hover,track,border,text}`;
calendar.css migra su cascada per-rol al forward compartido.
- month-grid: patrón mini-recipe (5 tokens `_palette-*`, sin recipe previo);
el generador emite base default + forward `[data-month-grid][data-color]`.
- time-picker / time-range-picker: tipo + wrapper.
Fix de regresión de runtime: la migración de la field-family dejó huérfana
la cascada `--_time-field-accent-*`, y time-range-picker re-exportaba el
Input crudo de soma, así que sus endpoints no recibían el color del picker.
Nuevo `time-range-picker-input.svelte` enhebra `data-color`/`data-color-custom`
a cada endpoint (verificado en navegador: color del picker -> ambos extremos).
time-picker (single) ya se salvaba por el overlay `data-field` en su raíz.
Fix de 9 errores de tipo: los factories de opts de los tests de provider
de switch/toggle no pasaban `colorCustom` (lo hicimos required en Fase 2b).
Regenera generated/base.css. check: 76 errores ajenos, 0 propios.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
3 months ago |
|
|
149c0fef6b |
uix(color): abre `color` al sistema completo (role·intent·paleta·valor) en la familia interactiva + rail segmentado de radio-group
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>
|
3 months ago |
|
|
d990fe42f9 |
eidos(gradient-finish): auditoria final - tinta named determinista via --_{c}-finish-ink (D7)
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>
|
3 months ago |
|
|
77ef3b0b3d |
eidos(gradient-finish): v2.2 named finishes + on/data-on - EL PLAN COMPLETO
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>
|
3 months ago |
|
|
58076cadfe |
eidos(surface): primitiva Surface - el lienzo temable (Box + tratamiento), v2.1
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> |
3 months ago |
|
|
63f6209e5f |
eidos(gradient-finish): v1.5 kind spread - rotacion de matiz anclada, medida antes de implementar
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>
|
3 months ago |
|
|
322a1939a8 |
eidos(gradient-finish): acabado anclado a la sombra de la tinta - v1 Button+Badge + dial temable + guard
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>
|
3 months ago |
|
|
326a25ec4d |
palabras: retirar el chrome words deprecado (FW)
Decision de usuario 2026-07-14: words (chrome eidos + demo) quedo deprecado en pos de palabras; existia como referencia historica. - Migradas las 4 piezas que palabras importaba (git mv, historia conservada, sin shims): words-activate -> palabras-activate, words-block-drag -> palabras-block-drag, words-block-drag-type -> palabras-block-drag-type, words-image-file -> palabras-image-file; imports actualizados a rutas locales. - Borrados src/uix/eidos/components/words/** (wrapper, menubar, bubble, inspector, gutter, rail, words.css, langs-menu/inspector, AUDIT visual) y la demo web/routes/uix/components/words/. - Sidebar: fuera el link Words, entra Palabras (la demo era inalcanzable desde la nav). - Podado el bloque `words:` de lib/recipes/base.ts + regeneracion (generated/base.css: -84 lineas, solo bajas --words-*). - Docs del track: referencias al AUDIT visual de words apuntan a git history. NO muere: soma/components/words (motor + provider + tests), morfo y langs de words — el fundamento vivo de palabras. Verificacion: engine 493/493 - guards eidos (recipe-css-contract + api-contract + visual-attrs) 35/35 - grep eidos/components/words en codigo = 0 - navegador (server propio): demo palabras monta sin errores de consola, activate fija bloque, grip abre panel (tabs + pills H1-H6/HC + texto/enlace). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
3 months ago |
|
|
efc3f4c301 |
feat(eidos): anti-CLS metric-override fallbacks for the default fonts
Ship a metric-overridden local() fallback face per default family (Fontaine /
next-font model): before the webfont loads, the browser uses a system font
(Arial / Georgia / Courier New) resized via size-adjust + ascent/descent/
line-gap-override to match the webfont's geometry, so text doesn't reflow when
the variable woff2 swaps in — zero CLS.
Values precomputed from @capsizecss/metrics (each webfont vs its system fallback)
and set in the config's `fallback` field (theme data — the sanctioned home, not
magic numbers in CSS). The engine emits the '{family} Fallback' @font-face and
injects it into the --font-family-* stack after the real family. Display repeats
Lora's fallback so its own token (hero/h1/h2, where CLS is most visible) is
covered without emitting a duplicate face.
Verified: the 3 fallback faces register in the browser; the stacks inject
'{family} Fallback'; tests 99/99; npm run check src/ = 9-error baseline, 0 new.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
3 months ago |
|
|
7d4f95005d |
feat(eidos): default fonts -> variable woff2 + full variable-font API
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>
|
3 months ago |
|
|
cc7e9a162a |
feat(theming): THM-2 - avatar root palette to the shared layer + 33 scales
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>
|
3 months ago |
|
|
05f0031cad |
feat(theming+soma): media-player Batch-4 (SOM-4) + THM-2 shared-palette rollout to 14 surfaces
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>
|
3 months ago |
|
|
0e75890f60 |
feat(theming): THM-2 — paleta universal por instancia (capa compartida + forward), 5/17 superficies
El mecanismo genérico ya existía (universalPaletteDecls auto-completaba las 33
escalas para recetas con tokens palette-*), pero con PREFIJO de componente
(--button-palette-*) — cada superficie pagaba ~21 KB, por eso solo button/toggle
lo tenían (rollout a las 17 = ~+315 KB). Colapso al modelo compartido:
- renderSharedPaletteLayer: UNA cascada component-agnóstica
[data-color='{role|scale}'] { --palette-{slot}: … } — 8 roles + 33 escalas,
emitida una vez (~19 KB). Roles 1:1 a --color-{role}-*; escalas con el step
map + on-solid contrast, valor idéntico a la cascada per-component retirada.
- renderRecipePaletteForward: toda receta con palette-* (público) o _palette-*
(privado) recibe [data-{c}][data-color] { --{c}-palette-{slot}:
var(--palette-{slot}, host-default) }. La guarda de PRESENCIA es la seguridad
de anidamiento — un hijo sin color dentro de un ancestro coloreado matchea
solo [data-{c}] (su default) y nunca lee el --palette-* heredado.
- Generalización a la paleta PRIVADA: checkbox/radio-group/switch ganan las 33
escalas con CERO cambio de CSS (ya consumen --_{c}-palette-*).
- universalPaletteDecls eliminado; 2 guards migrados al modelo compartido
(recipe-css-contract "shared layer + forward"; active-eidos-config contrast
en la capa compartida).
DONE (per-instance): button, toggle, checkbox, radio-group, switch (5/17).
Rollout de las 12 restantes en docs/process/continue-cleanroom-fixes §THM-2.
Doctrina en theming/reference §25 ("The per-instance palette layer").
Verificado en vivo (button + checkbox): color="teal" → escala teal exacta ·
affirm → rol exacto · primary → primary · nested-nocolor en ancestro teal →
primary (sin contaminación). Peso 345K → 329K. Gates: matriz 141/141 ·
eidos 314 · recipe-css-contract + active-eidos-config 98/98 · contracts 38/38.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
3 months ago |
|
|
4addfd7dbf |
fix(cleanroom): P1s ejecutados (EID-1/EID-2/SEM-1/MOR-4/THM-1/THM-3) + auditoría + plan scene/ambient
Clean-room fixes (2026-07-11, sobre los P1 de la auditoría):
- EID-1: el anillo de foco de la fundación migra a `outline` (§32) — el último
box-shadow del catálogo; :where()-wrapped para que las recetas siempre ganen;
doctrina actualizada en theming/reference + changelog.
- EID-2: opacidades literales de archetypes.css → var(--opacity-disabled/hover).
- SEM-1: política de live-region unificada — la urgencia va SOLO por intent
(threat/loss → assertive), runtime y AnnounceChannel ya coinciden.
- MOR-4: card-group con morfo propio (scope eidos) + guard de enumeración.
- THM-1: component-audit + recipe-css-contract escanean los *.css hermanos
(calendar-select, color-picker-spectrum, date-range-picker-time…); R-2.1
caza también hsl()/oklch(); violaciones de los secundarios corregidas.
- THM-3: canario de censo de toggle volteado (lint.test); commit-block promovido
a firma global block-shake; regen de generated/base.css.
Docs nuevos:
- docs/audit-new-10-july/audit-active-uix.md — auditoría clean-room 2026-07-10
del ecosistema (hallazgos P1–P3 con evidencia ruta:línea, matriz y gap analysis).
- docs/process/PLAN-scene-ambient-pack.md — plan por fases: motor arts/scene +
pack Ambient (45 animaciones de demos) + vía de promoción agéntica (Aura);
gate de decisiones D1–D9.
- docs/Disenando_lo_que_ocurre_FINAL.{docx,pdf} — edición FINAL del libro.
- README.md raíz (esqueleto de paquete).
Excluidos deliberadamente de este commit: words/palabras/chronos (pista
separada), web/routes/alpha (terminado, sin commitear por decisión), rt.css
(volcado de referencia Radix Themes).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
3 months ago |
|
|
83e13be649 |
refactor(eidos): promote calendar range colors to public API (S8)
The range endpoint colors lived as private `--_calendar-range-*` tokens declared
in range-calendar.css AND redundantly re-tuned by date-range-picker (reaching into
a sibling's privates). Promote them to the calendar-surface PUBLIC API: 8
`--calendar-range-{start,end}-{solid,solid-hover,border,text}` tokens in the
`calendar` recipe (`:root` defaults), consumed by range-calendar via the public
name. DateRangePicker drops its 6 `--_calendar-range-*` blocks — the composed
range calendar already paints from range-calendar.css, so the drp declarations
were redundant.
Deliberate asymmetry (documented in range-calendar.css): START defaults live in
the recipe `:root` so a theme can override them via config; END binds to the live
`--_calendar-accent-*` on the host (the accent is a per-color private that only
cascades there), so its `:root` defaults are the nominal public default. Both stay
public per the S8 verdict, preserving the original "end may differ from accent"
capability.
Value-preserving: recipe-css-contract 25/25, baseline 59, eidos-lint invalid 0,
browser-verified identical across default/affirm/secondary/fulfill on the
standalone RangeCalendar and the DateRangePicker popover.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
3 months ago |
|
|
d1670caa45 |
refactor(eidos): de-alias date/time pickers off sibling field recipe tokens
The date-range / time / time-range pickers referenced --date-field-* / --time-field-* recipe tokens to paint their field-shaped parts (standalone trigger box, wrap gap/font, embedded trigger). That is a cross-component alias — the exact drift the S6 Field composition eliminates. Point every one at the FIELD FAMILY public tokens instead (--field-control-* / --field-* / the B.1 --field-segment-height), or at the underlying primitive where Field has no matching token (the wrap column-gap keeps the tighter segment rhythm). Each substitution is computed-value-identical (both sides resolve to the same primitive) — adversarially verified + browser-confirmed on all four pickers. Also drop dead private declarations the compositions left behind (--_time-field-padding-inline / --_time-field-segment-padding-inline, and --_time-field-gap in time-picker) and, now that nothing aliases them, prune the 32 newly-orphaned date-field/time-field chrome tokens from the recipe + generated CSS (recipe-css-contract orphan guard green, 25/25). TimeRangePicker's embedded clock-trigger keeps sizing from the picker-local --_time-field-height (not --field-segment-height): its composed endpoints don't carry their own [data-size], so the field-family segment-height would pin to md. That endpoint size-threading gap is pre-existing (DateRangePicker threads it, TimeRangePicker doesn't) and tracked separately. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
3 months ago |
|
|
a820e5f337 |
fix(eidos): prune 167 orphaned field recipe tokens after S6 field.css move
The S6 Field composition moved each field's chrome (border / radius / bg / height / padding / font / label) into field.css, leaving the per-component recipe tokens still emitting but unconsumed. recipe-css-contract.test.ts's "does not leave declared public recipe variables orphaned" test went red with 167 tokens across spin/date/time/color/search/mask/password-field. Remove the confirmed-orphaned keys from each recipe (mask-field emptied → entry dropped entirely); regenerate generated/base.css. Value-preserving: the removed tokens had zero consumers (the test is the oracle), browser- confirmed identical rendering on search-field + password-field. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
3 months ago |
|
|
5eb92132b4 |
feat(fields): date-* family composes real DateFields via field.css (S6)
The date-* family now IS-a-Field then evolves, per the S6 mandate + the no-token-alias doctrine — no more marker-overlay borrowing of date-field.css. - date-field: chrome / label / invalid / disabled / focus come from field.css via the fieldRole identity (data-field / data-field-control); date-field.css keeps only segment specifics. No tokens aliasing the Field base. - Field recipe: add --field-segment-height (size-axis, family level). - DateField.Provider: expose syncPlaceholderToValue for controlled composition. - date-range-field: COMPOSES two real <DateField>s (DateField.Provider > DateField.Input per endpoint) bound to the range slice; size/variant/color thread through a visual context; CSS is layout-only. Fixes the field.css-not-loaded chrome regression. - DatePicker (single field): root carries data-field (A27); imports field.css; drops the dead date-field token re-declarations. - DateRangePicker: inherits the composed endpoints; imports field.css; removes the endpoint box that double-boxed the composed DateField. Baseline unchanged (59). Verified in browser: paint, edit coordination, size reactivity, no double-box. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
3 months ago |
|
|
53b6f629f2 |
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress)
Re-audit of the whole component catalog at pilot depth (91 fichas + the checkpoint verdicts in docs/audit/components/) and the executed fix packages. - P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 + API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d, component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts (role=application removed ×4, aria-selected off the Day, drp translationRef, field data-state prune, pin-input commit-set, media-player renames), 13 new sema packs + 12 morfos family-default → pack. - P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar); onValueCommit terminal-callback norm (pin-input/search/password/textarea + date/time/color-field add); typed validation reason + onInvalid (tags-input, css-field); index/onIndexChange (carousel); deselectable; openDelay/ groupSkipDelay; allowCustomValue; defaultValue prune. - P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel); touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual, 44 AAA). S6 (Field composition) + S8 (calendar-surface) pending. Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline); morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched components; per-component vitest suites green. Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md Excluded (broken by the N1 rename, left broken per user decision, not staged): words/**, palabras/**, chronos, web/routes/alpha/**. Reconciliation pending: the touch-rows ::before for checkbox/switch reverses changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed markers to a labeled-row/Field task — flagged for the user in the handoff. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
3 months ago |
|
|
92f9723388 |
feat(field): floating label mode + value-weight doctrine
Floating label (`<Field floatingLabel>`), the label typography doctrine, and
its propagation across the text-field family.
Floating label (Material outlined, text inputs only):
- CSS-only via :focus-within + :placeholder-shown — no new soma state. The
label starts as a placeholder inside the control and floats onto the top
border when focused/filled. Reuses Field.Label + Field.Input. Requires the
input to carry `placeholder=" "`. Segmented controls (Date/Time) not covered.
- Floated position is themeable via `--field-floating-label-top` (default 0 =
on the border) without touching the recipe.
Label weight doctrine ("label one step below the component"):
- The field VALUE is now `medium` and the label stays `regular` — one weight
step below (the size offset was already derived one step down). Base Field via
`--field-control-font-weight`; propagated to the specialized text fields
(number/css via spin-field, mask, password, search, textarea, date, time,
date-range, time-range, color) so every field value reads at medium.
Verified: recipe-css-contract + component-visual-attrs + api-contract (30),
eidos-lint 0 invalid, smoke, check 0 field errors. Browser: floating resting↔
floated transitions correctly; value 500 / label 400 across fields.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
3 months ago |
|
|
153d1ced98 |
feat(mask-field): MaskField — single-input character mask (Tier-1)
Generic pattern mask for free-form fixed-literal inputs (phone, SSN, credit card, IBAN, postal code, plate) — the confirmed gap across ark/bits/radix/react-aria (radix #1412 "not planned"). - Zero-dependency mask engine (mask-engine.ts): tokens 9/A/*, literals, escape, optional [] tail, accept-if-fits + caret mapping. Pure/DOM-free, node-tested (25 tests). Promotable to arts/mask later. - Soma provider composes base Field (OR-merge state, inputId register); reject-and-revert input + caret restore via tick(); commit-set on blur/Enter only. value = masked string, unmaskedValue derived. - Single role=textbox (never fake spinbutton segments); truthful value announcement; inputmode from mask; date/number/OTP delegate out. - Sema pack commit-set -> form.commit.subtle + tap (mirrors NumberField). - Eidos option-C wrapper + recipe (25 tokens) + demo (v2 6-tab testbed). Passes component:audit mask-field (0 err), 30 unit tests, recipe-css- contract, component-api-contract, eidos-lint (0 invalid), check, smoke. Note: soma barrel export + demo-app registration (sema pack + nav link) live in soma/components/index.ts and web/routes/uix/+layout@.svelte, left unstaged because they carry unrelated branch WIP. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
3 months ago |
|
|
bbd9c467e5 |
test(eidos): triage the 15 long-red suite failures — 289/289 green, check 61→59
Accumulated test debt (the eidos suite had been red across several feature
landings — THEME-SYS-1 named z-index, palette-first static donor library,
spin-field extraction, icon stroke-map refinement — none of them regressions;
baselined against HEAD). Each triaged stale-vs-bug by confirming current
behavior in code before touching a line.
Stale tests (code correct/improved, assertion not updated):
- z-index '76' -> 'var(--z-index-overlay-tooltip)' (the code is guarded by
THEME-SYS-1 in contracts.test.ts — the raw integer is forbidden).
- frost '80%' -> 'var(--depth-overlay-translucency, 80%)' (parameterized).
- C7 size-bundle coordinate ('--control-height-md' -> '--size-md-control-height').
- icon visual-stroke ratios 1.5->1.125, 1->0.923 (stroke map md 2->1.5, xxl
icon 48->52px; the arithmetic follows).
- delegate/CSS-only purple-9: the slim foundation legitimately emits the
role-referenced donor scales (purple=primary) as overridable :root defaults;
the real delegation guarantee is 'no baked [data-theme] block', which the
assertion now checks.
Test over-flagging (fixed the test, not the code):
- CSS-only shared layer: spin-field (no wrapper/index) excluded from the
wrapper/barrel/namespace contracts (CSS_ONLY_LAYERS).
- soma-runtime vars: 'var(--x, fallback)' is runtime-optional (the STUMBLES #7
soma->eidos surface) — only bare 'var(--x)' requires a recipe declaration.
- orphan check made corpus-wide: composition means a sibling consumes a
component's tokens (date-picker/month-grid consume calendar-control-*).
- natural-time-picker fixed-tone sky colors documented-exempt (Words-rail
doctrine).
- dialog drift-detector converted to a synthetic fixture (dialog.css no longer
ships the dead rule it self-tested against).
Real code fixes:
- declare '--pin-input-cell-readonly-bg' (I consumed it in C2b without
declaring it).
- remove 5 truly-dead tokens (drawer content-bg + 4 color-field
format-select/segment — 0 consumers anywhere in eidos/soma/web).
- clamp time-picker's 'size' to PopoverSize before forwarding (xs->sm):
TimePickerSize has xs, PopoverSize doesn't — a latent type bug my recipe
change surfaced via a complexity-budget flip. The fix dropped check 61->59,
also clearing navigation-menu-content of the same class.
card-group flagged as a REAL gap (KNOWN_MISSING_MORFO): it ships parts + chrome
but no morfo (morfo-first violation) — tracked, not silenced.
Verified: vitest src/uix/eidos 23 files / 289 tests green; npm run check 59
(better than the 61 baseline); component:audit 0 BROKEN.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
3 months ago |
|
|
6001d9bb47 |
feat(eidos): C7 — size-bundle adoption swept across the recipe catalog
The size bundle (--size-{k}-control-height / -font-size / -icon-size,
theming/reference §5) was emitted + guarded but consumed by ONE recipe
(the toggle pilot). This sweep points every recipe at the canonical
size COORDINATE instead of the raw primitive of the same coordinate:
366 refs across 34 recipes (--control-height-{k} -> --size-{k}-control-
height, and the font-size / icon-size coordinates likewise). Retuning a
size's bundle now reaches every consumer; a recipe that maps a key to a
DIFFERENT size's coordinate stays visible as a deliberate deviation.
The bundle vars are pure aliases of the primitives, so this is
computed-value identical — verified in a live browser: the accordion
chain --accordion-trigger-min-height-md = --size-md-control-height =
--control-height-md all resolve to calc(36px * 1 * 1). The generated
diff is 366-for-366 pure name swaps, nothing else moved. base/xxxl have
no size bundle and correctly stay on the typographic primitive.
Guard: recipe-css-contract forbids the raw size-coordinate primitive in
recipe token values (var(--control-height-{k}) etc.) — the drift can't
creep back. This is the ⚠️->✓ that theming/notes' comparison table flags
as the size canon's remaining asterisk (canon + guard, now + consumption).
component:audit 0 BROKEN; npm run check at the 61-error baseline; the
size-bundle guard + palette-cascade guard both green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
3 months ago |
|
|
62815cb485 |
feat(eidos): C6 — universal palette cascade moves into the generator
palette-{slot} is now RESERVED recipe vocabulary: normalizeRecipeTokens
(render-css) appends the full per-scale color:{scale} cascade for every
donor scale in PALETTE_SCALES to any token named palette-track/element/
border/solid/solid-hover/text/contrast — author-declared color:*
declarations win, only absent scales are appended. This is the
structural end of the hand-maintained-subset era: a recipe opts into
the per-instance palette by naming the token, and the generator
guarantees all scales (the 31-vs-33 drift class cannot recur at this
layer). Applies to app-config recipes too, since the hook sits where
every RecipeTokenSet is normalized.
Button's seven manual spreads are gone from base.ts (the helpers moved
into the generator); its generated output is byte-identical. Toggle —
which already exposed palette-* tokens — universalized itself: +330
generated lines, and <Toggle color="teal"> / color="steel" verified
resolving in a live browser (steel and fuchsia are exactly the two
scales the hand-kept lists used to miss). eidos-lint now classifies
donor-scale values on data-color as sanctioned eidos-only vocabulary
(the TSC color:* axis extension) instead of invalid — the morfo enum
keeps declaring the semantic roles, per the pilot's deliberate
runtime-open design. Guard: recipe-css-contract pins the LAST scale of
PALETTE_SCALES for every palette-bearing recipe in the generated css.
Also: the stale '31 physical color scales' test TITLE says 33 (its body
already asserted 33). Recorded in the plan: the eidos suite carries 15
pre-existing failures in 6 files (baselined against HEAD before this
change) — their triage is its own batch. npm run check at the 61-error
baseline; lint toggle baseline green again.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
3 months ago |
|
|
3095065f0c |
feat(eidos): recipe contract R-4.x + motion-channel migration 15/15 + theme-builder fixes + inventory decisions
Fable audit follow-through (fable_audit.md + fable-eidos-audit.md): - RECIPE_CONTRACT.md (E2 canon): the transversal systems every recipe must consume, enforced by component-audit R-4.1-4.6 (all at error; escape valves /* literal */ + /* functional */; WIP tracks excluded). Stale audit rules fixed against the current architecture (E-2.2 wrapper imports, D-1.2 v2 9-tab union, D-3.1 single snippet, TabsVariant mirror) - verdicts went 0/117/15 -> 75/50/5. - Motion migration 15/15: recipes off local @keyframes onto the channel - preset stamps (dropdown/context/select/combobox/tooltip/link-preview/ clipboard), new expand/collapse + value-flash signatures, shared-axis reverse pair, delayed-open open-alias in the preset trigger (PRESET_STATE_ALIASES), materials pattern for irreducible triggers (card/timeline/tabs/nav-menu/metrics). Duration/scale hooks keep every recipe's tuned values. - buildScheme (fase C): full a1..a12 alpha ramp per role (was a2/a3 - stale alphas after applyColorScheme), intentSeeds so temper starts from the ACTIVE theme's intent mapping (risk stays orange), alpha background self-derived from the scheme's own neutral step 1 (was hardcoded #fff/#111); mode now forces the donor variant. - Inventory decisions (fase D): semanticTracking axis removed (all-zero), border-hover slot dropped (0 consumers), separator slot adopted across line dividers (step 6, Radix divider tone), size-bundle consumption pilot on toggle (canonical coordinates consumed, deliberate deviations kept visible). - Docs: building-a-component.md (the one door, 9-phase route + known traps), PLAN-docs-reconciliation.md (fase 6 kickoff for a fresh session), THEMING wiring updates. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
3 months ago |
|
|
044221e4c7 |
feat(eidos): 33-color palette + palette-first theme builder + color guards
Palette: - Add `fuchsia` (~H334) and `steel` as the 32nd/33rd named scales; rename radix-scales.ts -> color-scales.ts and drop the "Radix parity" framing (seeded from Radix, but the palette is ours). - generatePalette: per-family nearest-hue donor (L-per-hue) so bright hues (yellow/amber/lime) stay vivid instead of landing muddy. Theme builder (web/routes/temas): - estudio: new "palette-first" color mode — character (vivacity/tone/neutral) regenerates the 33 families; roles are selected by hue harmony or manually and applied live to real components via --primitive-* overrides. - paleta: palette generator + role harmony chooser (the model, visualized). Color guards (THEMING.md §25.2/§25.4): - G1: completeColorRoleMap throws when a hierarchy alias and a valenced intent share a scale; the builder picker excludes intent-occupied scales. - G2: re-expose role slots bg2*2 / separator*6 / borderHover*8 / textStrong*12 (13 slots) — real UI/a11y needs the original nine had dropped. - G3: document the CVD / WCAG 1.4.1 rule (an intent needs a non-color cue) plus the affirm/fulfill activation distinction; audit-grounded. - G4: palette-invariant.test.ts checks the delta-E floor on every generator's OUTPUT (multi-step, monochrome-exempt); clamp builder tone to +0.08. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
3 months ago |
|
|
56eb9bd265 |
feat(ethereal): finalize — own engine is the runtime + dual-engine native path
Two parts: the cutover (own JS engine replaces @floating-ui as the runtime, dep out of the library) and the native CSS Anchor Positioning path layered on top. Cutover (P4, reconciled): - Flip USE_OWN_ENGINE -> true, then delete it (flag.ts removed). Collapse the A/B in use-floating + floating.svelte to own-only (drop the fui imports, the factory shim, the `as unknown as` casts). useFloating's `dom` is now required. - @floating-ui moved dependencies -> devDependencies: the LIBRARY (src/arts/ethereal + src/uix/soma) imports it NOWHERE; it stays only for the visual demo + the parity oracle. Zero-dep doctrine met for consumers. Dual engine (P2 — built + proven end-to-end): - `selectPositioningStrategy` ($ethereal/strategy.ts, 5 tests) picks native vs JS per instance on discriminators (arrow / shift / virtual-anchor / explicit-boundary; sticky is subsumed by shift). The behavioural twin of supportsCssAnchor. - eidos `renderFloatingNativeBlock`: @supports (anchor-name) and (position-area) with position-area from data-side/align (logical axes — validated against the spec), per-side gap margin, position-try-fallbacks for flip. base.css regenerated. - soma wrapper: when native, stamps data-floating-native + an inline anchor-name<-> position-anchor link and PAUSES the JS positioner (no computePosition, no autoUpdate). - New `avoidCollisions: 'flip'` (flip-only, no slide) decouples flip from shift so an overlay can flip natively. Popover demo: avoidCollisions = full / flip / off. - Proven in a real browser: a flip-only arrowless popover routes to native, positions via position-area, and flips natively (flip-block) at an edge — zero JS transform. Honest scope: shift has no native primitive, so native lights up only for non-shift overlays; the JS engine stays the default for collision-avoiding ones. data-side doesn't track a native flip, so native is gated to the arrowless case. Verified: 537 tests green (ethereal + 7 overlays + floating + generated-css), 0 new check errors. Docs: CONTINUE.md, PERF.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
3 months ago |
|
|
9fecb80fac |
chore(eidos): fold tabs trigger hover into the --state-* layer
The last neutral component on a bespoke hover. tabs trigger (transparent base) moves to the shared state layer: background: var(--tabs-trigger-bg-hover) -> background-image: linear-gradient(var(--state-hover), var(--state-hover)) Overlay (background-image) keeps it filled-safe for the segmented/pill variant. Removed the now-orphan --tabs-trigger-bg-hover recipe token + regenerated base.css. tabs.css is concurrently being reworked by the other session (segmented variant + focus outline migration, uncommitted). Staged ONLY the hover line via line-level staging (checkout HEAD + sed the single line) so their uncommitted segmented/focus work stays untouched + unstaged in the working tree. Completes the --state-* neutral-hover unification: every neutral component now uses the state layer; only valenced (palette) hovers keep their own. Verified: tabs.css uses --state-hover, token gone from base.css, segmented tokens intact; eidos suite 15 failed (pre-existing) / 255 passed. |
3 months ago |
|
|
3daa0e218e |
chore(eidos): fold color-picker neutral hovers into the --state-* layer
color-picker.css is clean (committed in
|
3 months ago |
|
|
4a90f3e688 |
chore(eidos): prune 16 orphan --{x}-bg-hover recipe tokens (post --state-* rollout)
The --state-* rollout ( |
3 months ago |