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 }
6 Commits (e522e04e65eaa3d8fb303442aed87ab0f64a9c62)
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
f7be59cdcf |
uix(background): se va `Backdrop`, que llevaba desde F1 absorbido y esperando tu palabra
D-BG.1 firmó la absorción y el borrado el 2026-08-17, pero el borrado exigía orden explícita del autor y no la tenía; llegó hoy. Se van cinco ficheros: `eidos/components/backdrop/` entero —`backdrop.css`, `backdrop.svelte`, `index.ts`, `types.ts`— y su morfo `morfo/components/backdrop.ts`. Con `git rm`, no con `rm`, para que el índice lo registre. El censo se repitió justo antes de tocar nada, no se dio por bueno el de ayer: ningún import del componente ni del morfo fuera de su propia carpeta, no lo exporta el índice de eidos, no aparece en ningún índice de morfos, sin langs, sin demo, y ningún `import.meta.glob` que lo cargara por convención. Los gates lo confirman por aritmética, que es mejor que por ausencia de ruido: `morfo:check` pasa de 168 morfos a 167 y de 8 saltados sin demo a 7 — exactamente uno, el que se fue. `check` conserva sus 72 errores preexistentes y no menciona backdrop en ninguno (el recuento de ficheros baja de 6985 a 6981). vitest de `eidos` + `morfo` 653/654, con el rojo de siempre (`skin-media-player`). `docs:check` 0/635. `smoke` 320/320. Lo que NO se borra son las citas. La §Baseline del README de `Background` sigue nombrando a `Backdrop` —ahora fechada, con la nota de que sus cuatro tramas (`glow · mesh · grid · dots`), su máscara de desvanecido y su caída bajo `prefers-contrast` se migraron valor por valor, y de que la historia guarda el original— porque esa fila explica algo que el código ya no puede: por qué este componente se llama `Background` y no `Backdrop`. El nombre colisionaba con el velo modal (MUI, Vuetify, y el propio archetype `overlay`), y esa colisión es la razón del nombre que hoy llevan `data-background`, `--background-*` y `Background.Video`. Con esto el eje queda cerrado: las cuatro firmas del autor ejecutadas, la verificación en navegador completa —travel, 60 fps, reflow y la rama `@supports not`— y el paseo A–H registrado. Vivo sólo queda lo que pertenece a otros ejes: `Ambient` honrando la pausa (del pack) y el puerto de estado para `<video>`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
4a9315d977 |
uix(background): el velo era el dim del modal, y con on="light" combatía a su propia tinta
D-BG.20. El scrim tomaba prestado `--color-overlay`, que es `surface.backdrop`: el dim de los modales (MD3/Radix/Vaul según el changelog), afinado POR MODO porque una página clara necesita menos atenuación que una oscura. Es una herramienta de ATENUACIÓN, y el scrim la usaba como tinta de LEGIBILIDAD — la misma enfermedad que la escala `--opacity-*` prestada que se arregló esta misma mañana, y que trajo tres defectos. El primero, que la misma fotografía se leía distinto según el modo: 1,48:1 en claro y 2,10:1 en oscuro. Una fotografía no cambia con el modo, y la legibilidad no es un eje que el modo pueda mover. El segundo, un TECHO: el alpha propio de la tinta (0,42 / 0,66) acotaba la escala entera, de modo que ni el peso máximo alcanzaba AA y ningún paso nuevo podría haberlo alcanzado. El tercero es el que no había visto y es el peor: con `on="light"` el velo era OSCURO bajo tinta oscura. El scrim combatía a la tinta que existía para sostener. La tinta del velo pasa a ser el SUELO de la tinta que `on` puso en vigor, que es lo que D12 ya define: `--color-content-on-solid-contrast` bajo `on="dark"`, `--color-content-on-solid` bajo `on="light"`, y `--color-surface-default` sin `on`, porque ahí la copia toma la tinta de la página. Tres roles que ya existían —ninguno inventado, §16.C— consumidos como capa 4 → capa 3 (§3) y sin alias. Con la tinta opaca el peso ES el alpha, así que `strength` significa una sola cosa; antes significaba dos, porque con `color="teal"` el ink ya era `--palette-solid`, opaco, y sólo el default era translúcido. La escala se re-afina por el TRABAJO de cada paso, medida con la matemática del propio framework (`$color`: `apcaLc` + `wcagContrastRatio`) sobre la peor obra de cada contexto: `xs` 0,08 · `sm` 0,13 · `md` 0,19 · `lg` 0,40 · `xl` 0,70. `xl` es el único paso que promete legibilidad sobre CUALQUIER fotografía, y lo promete contra los cuatro suelos que el framework ya usa —el criterio del par on-solid (`lib/on-solid.ts`: APCA |Lc| ≥ 60 ∧ WCAG ≥ 3) y el AA 4,5 de §40— en ambos contextos: `on="dark"` sobre foto blanca 6,45:1 con Lc 85, `on="light"` sobre foto negra 8,29:1 con Lc 61. 0,70 es el mínimo que cierra los cuatro; el más exigente, el APCA de `on="light"`, pedía 0,695. Los pasos por debajo son atmósfera y el README lo dice: no son garantías. Verificado en Chrome, no calculado: sin `on` el velo sale `oklch(0.9911 0 0 / 0.19)`, el suelo de la página en claro; con `on="dark"`, `#1c1917` al 19%; con `on="light"`, BLANCO al 19% bajo tinta oscura, que es el arreglo; `xl` en `on="light"` sobre foto negra mide 8,25:1 por píxel donde `$color` predijo 8,29. Y el consumidor real: el hero en layout `background` pinta `#1c1917 / 0.19` con titular blanco y da 1,48 / 5,17 — exactamente lo que daba antes en modo claro, paridad a la cifra. Los heroes en modo oscuro se aclaran de 0,297 a 0,19, que es el modo soltando una decisión que nunca fue suya. El selector de contexto va envuelto en `:where()`, y no por estética: a pelo llega a (0,4,0) y le gana a `[data-color]`, de modo que un `<Background.Scrim color="teal">` dentro de un stack `on="dark"` pintaba el suelo en vez de teal — el sistema de color abierto de §25 derrotado por un selector de conveniencia. Envuelto, la familia baja a (0,2,0): el contexto gana al bloque base por orden y pierde contra el color explícito. Medido antes y después. Queda anotado que el vignette sigue consumiendo `--color-overlay`, y ahí es correcto: es una trama que atenúa bordes, no un velo de legibilidad. Esta decisión salió de leer la doctrina entera después de que el autor preguntara si mi recomendación era conforme al sistema de color. No lo era: yo proponía una tinta opaca única, que servía sólo a `on="dark"` e ignoraba los otros dos contextos, y medía el contraste con un canvas WCAG-only en vez de con los suelos del framework. Los tres puntos los corrigió la documentación. Gates: audit PASS 0/0 · eidos-lint invalid 0, class-hooks 0 · rtl:check 0/180 · vitest eidos 434/435 (el rojo es `skin-media-player`, el de siempre) · `check` con los mismos 72 errores preexistentes y ninguno propio · prettier limpio. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
c691a88c1a |
uix(background): un fondo nunca suena, y no estaba resuelto sino evitado
D-BG.19, planteada por el autor: qué pasa con un `Background.Video` cuyo clip
trae pista de audio, y cómo se relaciona con el interruptor de sonido del
framework o con un «no sound» de la app. La respuesta que había en el código era
que no había respuesta. `muted` era una prop con default `true` y la razón
escrita al lado no era doctrinal sino mecánica —un clip sin mutear tiene el
autoplay rechazado en todos los navegadores—, de modo que el silencio era un
efecto secundario de la política del navegador y no una decisión de nadie.
Lo que eso significaba en cuanto alguien escribiera `muted={false}`: el clip
sonaba FUERA del grafo de audio. `sound.buses.content.setMuted(true)` no lo
silenciaba; no participaba del audio focus, así que hablaba por encima de un
`MediaPlayer` en vez de cederle el paso; no atenuaba el bus `ui` mientras sonaba;
no se proyectaba a MediaSession; y el slot `sound` de `$prefs` —el interruptor
«sin sonido» de la app, el que se proyecta al DOM como `data-sound`— no lo
alcanzaba nunca. Un `AudioContext` por documento es la razón de ser de ese art, y
el componente no tenía ni una referencia a él. De WCAG 1.4.2 se salvaba de
rebote: el control de pausa que el stack ya debía lo para, por carambola.
Así que `muted` deja de ser prop y se escribe `true` incondicionalmente: la API
ya no puede expresar un fondo audible. Tres razones, de más a menos vinculante.
La doctrina propia del componente —toda capa es `aria-hidden` y la decoración no
es contenido, así que un clip de aquí no puede portar significado que el audio
entregue; una capa audible sería contenido vestido de fondo—. WCAG 1.4.2: una
decoración no puede pedir un consentimiento que nadie le dio. Y la fuga de
`arts/sound`, que es la parte que hace de esto un asunto de framework y no de
gusto. Un clip que DEBE oírse es contenido: `MediaPlayer`, que ya es ciudadano de
`sound.media()` y obtiene focus, ducking y MediaSession gratis, o montarlo por
`Background.Layer` y registrarlo uno mismo.
La alternativa —hacerlo ciudadano con `sound.media(el, { focus: 'duck' })` cuando
el consumidor desmutea— queda rechazada por lo mismo que la hacía atractiva: abre
la puerta a que un fondo compita con el contenido real, y ese territorio es de
`Ambient` o de un reproductor.
Verificado en Chrome con la capa en `video`: `el.muted === true`, `paused: false`,
`currentTime` avanzando, `readyState: 4` —sigue reproduciéndose— y el control de
pausa presente. Queda anotado en el código un detalle que invita a un arreglo
equivocado: `hasAttribute('muted')` lee false mientras la propiedad es true,
porque Svelte fija `muted` como PROPIEDAD sin reflejarlo al atributo. No es un
agujero: la propiedad se escribe en el mismo efecto y dos líneas antes del
`play()`, y no hay nada que pueda colarse entre las dos.
Ningún consumidor pasaba `muted` —el `HeroSite` ya lo decía en prosa,
«DECORATIVE — muted»—, así que el cambio de API no rompe a nadie.
Gates: audit PASS 0/0 · vitest eidos 434/435 (el rojo es `skin-media-player`, el
de siempre) · `check` con los mismos 72 errores preexistentes y ninguno propio ·
prettier limpio.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
73f06a7882 |
uix(background): el velo tenía una escala que no ordenaba, y una tinta que le pone techo
Cierra el eje salvo dos firmas. La escala `strength` dejaba de crecer a mitad de camino: tomaba prestada `--opacity-*`, que nombra cuán opaco es un ELEMENTO, y leída como pesos de velo salía `ghost` 0,30 · `scrim` 0,45 · `overlay` 0,65 · `muted` 0,65 · `subtle` 0,80 — es decir, un consumidor que pedía `subtle` recibía el velo MÁS pesado del conjunto, y `overlay` y `muted` eran el mismo número con dos nombres. Ahora el velo tiene su propia escala de cinco pasos (`--background-scrim-strength-xs`…`-xl`) y la unión pasa a `xs|sm|md|lg|xl`, ordenada por construcción: medido en Chrome, α 0,084 · 0,126 · 0,189 · 0,273 · 0,420, estrictamente creciente. `md` sostiene el 0,45 que shipeó el hero, así que el default pinta exactamente lo que pintaba. Era cambio de API y se hizo mientras el único consumidor era la demo: ningún block nombra `strength`. Y midiendo eso apareció que la tabla de contraste del README era falsa por la mitad, en la dirección incómoda. Estaba calculada sobre una tinta de α 0,66; `--color-overlay` resuelve a `rgba(28, 25, 23, 0.42)`. Los valores reales, ahora compuestos en un canvas y leídos por píxel en vez de calculados: el default da 1,48:1 sobre foto blanca —no 2,10— y el paso más fuerte 2,14 —no 4,42—. Lo que eso destapa no es un número peor sino un TECHO: `xl` gasta la tinta entera y llega a 2,66:1, de modo que ningún paso nuevo puede alcanzar AA, porque el límite es el alpha del token y no la escala. Con tinta opaca los mismos pesos dan 5,45:1 a 0,65 y 9,22:1 a 0,80. Queda como decisión del autor, reescrita con estas cifras, porque es sobre qué ES un scrim y no sobre subir un valor. La rama `@supports not (animation-timeline: view())` queda ejercitada, que era el último hueco de verificación. Chromium ya no puede desactivar scroll-driven —estable, flag de runtime retirado: seis candidatos probados, los seis siguen reportando soporte—, pero el Firefox de Playwright NO lo soporta por sí mismo, así que la rama está viva ahí sin emulación ninguna. Con el fichero de receta real: `--background-progress` 0 → −64px, 0,5 → 0, 1 → +64px, con `animation-name: none` y cero animaciones. Son los mismos extremos que el camino CSS medido en Chrome, o sea que los dos caminos concuerdan; y en Chromium la rama no aplica y escribir la var no mueve nada, que es la exclusión mutua que el README afirmaba sin haberla medido. Sin ejercitar queda un eslabón —que `ScrollProgress` escriba la var extremo a extremo—: desde el shell de este entorno no hay ruta a localhost, y Firefox no alcanza el dev server. El paseo de la checklist A–H, que estaba listado como lectura de F4 y nunca registrado, encontró tres cosas. Faltaba la excepción `A2.3` con su ID (las tres partes llevan `data: []` porque sus attrs son de wrapper y su único estado vive en el Button que compone) y faltaba `## Subset` (color: el conjunto completo, porque en decoración restringir la paleta sería arbitrario; intent: no se acepta). Y una deriva documental: el README decía en TRES sitios que el control de pausa «IS the canonical Toggle» y que dispara `commit-toggle`, cuando D-BG.18 lo cambió a `IconButton` con etiqueta que cambia y sin `aria-pressed` — el evento es `contact-activate` de `button.ts`. Vestigios de la era D-BG.4 que la enmienda no barrió; corregidos, incluido el comentario del propio morfo. La banda de `feature-split` queda firmada POR SECCIÓN, enmendando el plan en sus tres menciones y `PLAN-blocks-quality` §3 columna B, que la pedía por fila: por fila obliga a cada `Row` a poseer el estado de alternancia —su índice y el de sus hermanas— y un block que coordina deja de ser un block que no posee nada. Gates: audit PASS 0/0 · morfo:check (6 de 160 fallan, background no) · eidos-lint invalid 0 y class-hooks 0 · rtl:check 0/180 · smoke del componente PASS · vitest eidos 434/435 (el rojo es `skin-media-player`, el de siempre) · `check` con los mismos 72 errores preexistentes y ninguno en ficheros propios. Tres fantasmas costaron tiempo y son el mismo patrón: leer las vars tras un screenshot devuelve el `write(0,0)` de un `pointerleave`; dar por muerto el `strength` leyendo `backgroundColor` de un scrim GRADUADO, que pinta por `background-image`; y juzgar `fade` y `pause` sin la precondición que necesitan. El instrumento miente antes que el código, y aquí mintió tres veces. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
d2574e8a7c |
uix(background): la línea de tiempo la capturaba un overflow, y el puntero se iba con el scroll
El travel llevaba dos días escrito y sin medir, porque el panel oculto nunca activa una animación scroll-driven declarada en CSS. Con el Chrome del autor delante se pudo medir, y lo que apareció no fue una confirmación: fueron dos defectos que ninguna sonda había estado en posición de ver. La puerta es `document.visibilityState` — con la pestaña oculta la `ViewTimeline` existe, con `source` y `subject` correctos y `playState: 'running'`, y `currentTime` es `null` para siempre; ahí ni un `await requestAnimationFrame` resuelve. El primero estaba en la demo y el límite es del componente. `[data-uix-stage]` declaraba `overflow: hidden`, y eso es un scroll container aunque no pueda scrollear nunca: `view()` se ancló al stage y el progreso quedó clavado en 52,63% en toda posición de scroll, sin error, sin aviso y con un `translate` de aspecto perfectamente razonable. Con `overflow: clip` —recorta igual, respeta el radio, y no es scroll container— el travel aparece: progreso 43,5% → 95,5%, `translate` `0px -8,34px` → `0px 58,19px`, monótono, y los `4rem` completos de `--background-parallax-travel` en el extremo. Entra en el README como quinto límite porque el caso real no es un harness de demos: es el `overflow-x: hidden` con el que cualquier landing contiene su decoración, que convierte al elemento en scroll container en los DOS ejes. El segundo era del componente. El rect del anfitrión se cacheaba en coordenadas de viewport y sólo lo invalidaban `pointerenter` y un resize; el scroll mueve el anfitrión sin disparar ninguno de los dos. Medido con ratón real: un tick de rueda sobre un anfitrión de 288px dejaba `--background-pointer-y` 0,77 fuera de sitio —los 111px de scroll sobre media altura, a la centésima—, o sea fuera del rango −1…1 que la variable promete, con el spotlight despegado del cursor unos 115px y sin recuperarse hasta salir y volver a entrar. Cacheada la caja en coordenadas de DOCUMENTO y normalizando contra `pageX`/`pageY`, que en un evento real ya traen el scroll incorporado: error 0 en los dos movimientos, halo a 1px del píxel pedido, sin lectura de layout en el camino caliente y sin el listener de scroll que este componente existe para no tener. Queda dicho el residuo: un scroller anidado vuelve a desviar, porque `pageY` no lo ve, y se autocura al reentrar. Y lo que era el encargo, medido: 60 fps con mediana de 16,7ms, p95 17,0, máximo 17,1 y 0 de 200 frames por encima de 20ms con el travel vivo —la base con `speed: 0` salió peor, que es ruido de entorno—; y 0ms de reflow forzado en esos mismos 200 frames moviendo los DOS ejes, con el observador de Long Animation Frames que usa `arts/perf`. El cero está validado por mutación: un thrash deliberado de 65ms en la misma página se reporta con 43ms forzados y el script atribuido, porque un instrumento que no ve nada y un cero real se leen igual. Sigue pendiente la rama `@supports not`, que sólo se ejercita en Firefox. Las 174 demos comparten ese stage, así que el cambio se auditó: `affix` idéntico y `sticky` idéntico —cuatro pasadas alternando `clip` y `hidden`, porque la primera miente—, y `anchor-nav` cambia a mejor: su rail `position: sticky` es hermano del scroller interno, con `hidden` no se pegaba nunca y se escapaba por arriba perdiendo media lista, y ahora se mantiene a la vista. Ninguna demo tocada. Dos avisos para quien vuelva. Un `PointerEvent` construido reporta `pageY === clientY`, sin sumar el scroll, así que los eventos sintéticos sirven para DESCUBRIR este defecto pero no para verificarlo: con ellos el arreglo bueno mide −1,389 donde el ratón real da −0,008. Y leer las variables después de un screenshot puede devolver el `write(0, 0)` de un `pointerleave` en vez de la medición; el valor que vale es el del último `pointermove`, trazado. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
06f3c26400 |
docs(background): el handoff del eje, con lo que no pude medir escrito arriba
`CONTINUE-background.md`. F0 → F5 cerradas y commiteadas: el componente existe, tiene demo, está documentado y el tier lo usa. El handoff indexa; la fuente viva sigue siendo `PLAN-background.md` (D-BG.1…18 y la ejecución medida fase por fase). Lo que queda no bloquea el uso: cuatro firmas del autor (la escala `strength` del scrim, que no está ordenada por peso; la foto clara sin ningún peso que llegue a AA; borrar `backdrop/`; y si la banda de `feature-split` va por sección o por fila), una verificación que este entorno no puede dar —el travel real, 60 fps y el detector de reflow necesitan un Chrome visible, y está comprobado que la limitación es del entorno y no del código— y dos tareas que este eje abrió en otros: `Ambient` honrando la pausa, y un puerto de estado para `<video>` que ya tiene dos consumidores. Lleva también las cinco lecciones que costaron medición, para no volver a pagarlas: A30 no es estilo sino supervivencia; `revert-layer` sin `@layer` es `revert`; la demo destapa lo que una sonda no; mide antes de afirmar rendimiento; y la decoración que parece mejor puede ser la ilegible. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |