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 }
16 Commits (468a9d1134d697546f1b36231d6773d6324a5faa)
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
468a9d1134 |
style: prettier de una sola vez sobre el repositorio (F5 del cierre, parte 3)
Formato puro: `npm run format` (dos pases; siete ficheros no convergían en el primero) sobre todo lo que .prettierignore no excluye (web/ congelado, .claude/, artefactos generados). Este commit va en .git-blame-ignore-revs. Neutralidad medida fichero a fichero, compilando y minificando con esbuild (y con el compilador de Svelte, cliente y servidor, para .svelte) la versión de HEAD y la formateada: 1 031 ficheros de código idénticos; 9 CSS que solo difieren en espacios pegados a un paréntesis (`var( --x )` frente a `var(--x)`), que CSS no tokeniza como significativos; 510 Markdown sin compilación posible (docs:check los valida). Los dos generados y el guard que dependía del formato se resolvieron en el commit anterior. Verificación sobre este árbol: npm run lint exit 0 · gate entero verde salvo focus-census, arreglado en el commit anterior y re-ejecutado (9/9): check:gate OK (src/ y scripts/ a cero) · docs:check 0/0 · arts/blocks/packs/rtl/ translations/agent · eidos:lint 0 inválidos · apps:check (boot 27/27, 0 errores) · suite 465/466 → 466/466 con el guard corregido · build de la raíz exit 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
3 weeks ago |
|
|
e16c14dec7 |
feat(eidos): el ritmo de revelado sube a config — primitives.motion.staggerViewport, y su regla emigra a la foundation
Firma opción 1 del autor (cableado EidosConfig.motion → CSS, la última entrada de PENDING_PRIVATE_RENAME). Ejecutada con el esquema completo: expediente + dos constructores en serie + adversarial, todos Opus en ultra, coordinación y supervisión de la sesión. El gancho --motion-stagger-each-default era FICCIÓN: nadie lo declaraba y su único consumo (motion.css:42) resolvía siempre al literal 70ms. Ahora: primitives.motion.staggerViewport: '70ms' (tipo + default + validación + contrato + emisión --motion-stagger-viewport en :root, molde floating-gap verbatim) y la regla [data-stagger] > [data-animation-trigger='viewport'] EMIGRA a renderMotionBlocks — donde vive la máquina entera del stagger. Con ello motion.css queda en sus dos gates de opacity (su veredicto structural, más verdadero), la entrada del registro cayó STALE POR MECÁNICA y el registro, a cero, se DESMONTA con acta (un guard que inspecciona el vacío es un falso verde); la historia completa cierra en canon/recipe-contract.md. E14 (70→20ms) queda APLAZADA con nombre: firma de diseño propia, mueve 8 blocks. Adversarial 9/9 CONFIRMADO: neutralidad término a término contra la línea base histórica en Chrome real con el IntersectionObserver disparando (0/0.07/0.14/0.21/0.28/0.35s en /blocks/team/preview) - override 999ms reescribe la cascada - nombre muerto inerte (5s en :root, cero efecto) - <Cascade> intacto con control positivo quirúrgico (solo el hijo estampado toma el token) - cero empates de hoja posibles (tres escritores en .css, especificidad decide) - el guard del espacio cerrado muerde por mutación - 126 tests, censo 73%/66/1152-0-0, docs 0/0. Sus reservas, con acta en changelog §57: el contrato gana DOS filas (--motion-stagger-each entra como (derived) al emitirse — clase preexistente de index/index-rev) - gates divergentes latentes - path del contrato sin pin. Correcciones del adversarial incluidas: dos citas muertas del ledger de blocks anotadas, el presente falso de component-audit.ts, y el absoluto de canon matizado. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
1 month ago |
|
|
6e501f4fdf |
docs(blocks): handoff del día — cinco filas cerradas, y el eje que cambió el método
Deja escrito por dónde entrar mañana, en este orden: §0 EL EJE VIVO — el chasis de sección, que es lo que hay que retomar. Con la frase del autor que lo abrió («lo estás convirtiendo en una mierda a base de parches, ninguna es profesional ni de alcance») y su pregunta, que es la que tenía razón: si no debería haber un componente del que se basen todos los bloques. El censo que la confirmó (16 · 15 · 9 · 8 copias del Header), la firma (que CREZCA Section, no que nazca un hermano), por qué el nombre Block se descartó (ya significa «a todo el ancho» en el canon), los tres commits, las dos trampas que costaron —el Background que se capa dentro de la medida, y la raíz que extiende SectionProps y no HTMLAttributes— y las tres cosas que quedan: los 19 README describen el nido viejo, el contrato del tier no dice todavía que la raíz de un block ES un Section, y nada más. §1 LA COLA, REENCUADRADA — con el aviso de que más de la mitad de lo que sigue archivado bajo un block tiene la causa en el canon (A-48, A-53, A-60, A-62, A-65), así que bajarlas dentro de la frontera del tier fabrica parches. Y el trabajo honesto que sí queda dentro: A-74, A-96 y A-71. A-11 replanteada en su ficha por la misma razón: su arreglo profesional es que el canon publique el CATÁLOGO en runtime de sus escalas —como ya hace EIDOS_VARIANTS— y no siete líneas de exhaustividad repartidas por las demos. Queda medido lo que hoy está derivado: ContainerSize sin xxl, Breakpoint con 3 de 6, Decor con 5 de 8. docs:check 0/0 (813). Los avisos de prettier de los dos ficheros son preexistentes (CRLF), comprobado contra HEAD. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
18165e74ed |
uix(blocks-demos): la vista previa nace con el estado vivo de ActiveUix, no con los defaults
El iframe de la vista previa es OTRO documento con OTRO runtime (BootUix corre
su propio createActiveUix), así que el estado de la sección no cruza el marco:
la rama de 375/768 se abría SIEMPRE en claro, y en 9 demos siempre en LTR, con
la topbar en oscuro y RTL delante (A-29). Dentro del documento los blocks
siempre atendieron a ActiveUix — el shell conduce el framework por
uix.prefs.setIntent y el modeSource de eidos —; lo que faltaba era la única
serialización que existe entre dos runtimes: la URL.
El arreglo va en el ARNÉS, no demo a demo. BlockDemo lee el estado VIVO por la
superficie sancionada del framework — readActiveUixPrefsSlot(uix.prefs,
'direction'|'language')?.get(), las lecturas por dimensión que son rune-backed
(contador $state por dimensión), y eidos.getThemeContext().mode, que lee el
modeSource del shell — y compone la URL final: los props del BLOCK los pone el
demo, los EJES los pone el arnés. El {#key} y el enlace «abrir ↗» pasan a la
URL resuelta, así que mover un toggle global re-monta el iframe con los params
nuevos y el enlace abre lo que estás viendo.
Retirados los 11 controles «dir (solo la vista previa)» de banner, contact,
cta, feature-grid, feature-split, hero, newsletter, site-footer, site-header,
stats-band y team: eran el eje de sección repetido por demo — la regla del
arnés dice que tema, idioma y dirección los da el shell — y no tocaban prefs,
sólo escribían la URL. El shell queda como único dueño de los ejes; una
precedencia demo-gana habría dejado el toggle global inerte en esas páginas.
Dos correcciones a la ficha: testimonials ya derivaba su URL (el literal
desnudo quedó atrás en F2b), y el alcance real era el sistémico que la propia
ficha apuntaba — mode= no viajaba en NINGUNO de los 19 demos y dir= faltaba en
9. La pata lang sigue siendo inerte salvo en contact (blocks.* registrado),
pero viaja igual: es un eje del shell.
Verificado con la topbar REAL de la sección, no con params a mano: en oscuro +
RTL, el iframe de pricing a 375 arranca base-dark + dir=rtl (tinta
oklch(0.9491 0 0)) y el de hero — que tenía control local — hereda igual; en el
estado por defecto los dos vuelven a base-light + ltr. La URL sale con los
props del block primero y mode/dir/lang detrás.
Gates: svelte-check 72/62 antes y después · blocks:check 0/19 · docs:check
0/0. Huella: BlockDemo +31, cada demo −18 (−192/+39 en total).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
2 months ago |
|
|
48bad6c726 |
uix(feature-split): una sección se nombra por su encabezado, y sus filas cuelgan de él
feature-split era el único block de sección del tier sin parte .Header. Sus
filas emitían h2 por defecto, así que una sección de tres filas aportaba tres
h2 hermanos al outline y la sección misma no tenía nombre (A-110). La página
compuesta lo rodeaba a mano: encabezado escrito como primer hijo y un level={3}
repetido fila a fila.
El h2 de las filas no era un descuido: era una decisión ESCRITA en el README
del block —«.Title emits h2 by default (each row is a section-level statement
of its own)… drop it to 3 when the page already put an h2 above the rows»—. Se
deroga en sitio, y no por gusto sino porque el resto del tier declara lo
contrario: FeatureGrid.ItemTitle, las preguntas de faq y Pricing.PlanName ponen
en level 3 lo que se repite, y el h2 lo lleva el Header.
Nace FeatureSplit.Header, calcado de Testimonials.Header (Box con medida +
Motion + Stack) y sin contexto, porque este block no tiene eje align que
heredar. Y .Title baja su default de 2 a 3. La forma queda coherente en los dos
casos, que es lo que la hace preferible a las alternativas: con .Header el h2 es
suyo y las filas cuelgan; sin .Header la app ya puso el h2 encima y las filas
cuelgan igual. La tercera vía —un contexto donde el Header marcase su presencia
y el Title derivara su nivel— daba el mismo resultado a cambio de un hijo
escribiendo en el contexto del padre, que es el patrón que ya costó un bucle de
$effect en este repo. Por un solo bit, no compensa.
Verificado en navegador, outline leído del DOM:
/blocks/feature-split/preview h2 «Del evento crudo a la decisión»
h3 ×3 (las filas) ← antes: h2 ×3, sin nombre
/blocks/landing h2 + h3 ×2, dentro de un outline de página
que ya no salta de nivel
La demo compone su encabezado y LandingSite deja de hacerlo a mano: fuera el
Stack escrito a pelo y fuera los dos level={3}. Header medido en su sitio (x
144, 768 de ancho: la misma medida que la copia de las filas).
Gates: svelte-check 72/62 antes y después · vitest src/uix/blocks 36/36 ·
blocks:check verde (19 blocks, 147 ficheros) · docs:check 0/0.
Primera fila del ledger arreglada DENTRO del tier, por acotación del eje: las
tres anteriores de la sesión (A-111, A-112, A-109) eran todas de canon.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
1e3a67ad39 |
uix(banner): la tira de aviso no es la cabecera del sitio, así que deja de reclamar su landmark
Toda página con cabecera exponía DOS landmarks banner: el <header> del
site-header y la tira de aviso, que estampaba role="banner" a mano. ARIA
reserva ese rol para la cabecera DEL SITIO —identidad, búsqueda del sitio— y
dice que un documento debería marcar como mucho uno (A-109).
Las dos salidas que la ficha proponía estaban mal planteadas, y lo destapó una
revisión adversarial de tres refutadores independientes:
- «Estampar el rol ANTES de los rest props, que es la clase naming donde gana
el consumidor» contradice la doctrina que la propia ficha citaba: role es
clase CONTRATO por nombre de atributo (morfo/types.ts:811-832;
ARIA_NAMING_ATTRS contiene sólo aria-label). El runtime siempre gana ahí, y
con razón: un data-state o un role pisables son un componente que miente.
- Y el orden de estampado NUNCA fue el mecanismo. Medido: el segundo landmark
es el <header> del site-header, que no lleva atributo role ninguno — recibe
banner IMPLÍCITO por ser una <header> fuera de un elemento de sección. Un rol
pisable habría dejado quitarlo caso por caso, empujando el arreglo a cada
app, en vez de arreglarlo por defecto.
- El morfo, además, nunca declaró el rol (aria: []): era un literal del wrapper
de eidos que el README daba por contrato de la parte. Drift morfo↔eidos que
se queda sin objeto.
Lo firmado: la tira deja de reclamar el landmark. defaultElement header→section
en el morfo, fuera el role literal. Un <section> es region SÓLO cuando tiene
nombre, así que el aria-label del consumidor la hace encontrable y su ausencia
la deja como contenido plano — que es exactamente lo que el censo de landmarks
de A-111 ya sancionaba por escrito.
⚠️ SIN nombre por defecto, y esta es la corrección que la revisión me obligó a
hacer sobre mi propia propuesta: yo había planteado un default traducido
(«Aviso»), copiando el patrón de skeleton/spinner. Habría convertido TODA tira
en landmark sin salida — /temas/grafito apila cuatro y habrían salido cuatro
region con el mismo nombre, que es la clase de defecto de A-111 con otro traje.
El precedente de <nav> no transfiere: un <nav> es SIEMPRE landmark y por eso
pide nombre; un <section> lo es PORQUE lo tiene. Quien quiera una tira
encontrable, la nombra.
Derogadas por escrito las dos afirmaciones que decían lo contrario, porque
existían y estaban firmadas: el README del canon («Banner is a landmark for
page-level announcements — site header, persistent notice, system status bar»)
y el «✓ ejemplar» que docs/audit/components/banner.md le puso a ese contrato el
2026-07-07. La otra mitad de aquella decisión —no role="alert", el feedback
transitorio es de Toast— sigue en pie y se dice.
Verificado con el árbol AX de Chrome por CDP, servidor limpio:
/blocks/landing banner ['Aviso del producto','Acme'] → banner ['Acme']
+ region ['Aviso del producto']
/blocks/banner/preview banner ['Principal'] · region ['Aviso del producto']
/temas/grafito banner [] · region [] — las cuatro tiras sin nombre
dejan de ser landmarks, que es lo correcto
Píxel idéntico: la receta selecciona [data-banner], no el elemento — tira a
sangre 1280×54 con el mismo fondo, capturada y mirada. Ningún test, guard, CSS
ni consulta del repo seleccionaba por role=banner ni por el tag.
Gates: svelte-check 72/62 antes y después · vitest eidos+blocks+morfo 692/693
(el rojo es skin-media-player, ajeno y documentado) · blocks:check 0/19 ·
docs:check 0/0 (644) · morfo:check PASS en banner. Prosa re-sincronizada en 11
sitios: los README de canon, block y app-shell, la ficha de auditoría, las tres
demos, landing y su README.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
aced677a15 |
uix(toolbar): la acción principal de un cluster ya puede decir su rango — y el gesto ya suena
`Toolbar.Button` tipaba contra el ButtonProps de SOMA ({id, disabled}): sin
variant, sin color, sin size. Un cluster de acciones donde UNA es la principal
no tenía cómo decirlo desde dentro, y la demo del app-shell dejaba «Nueva»
FUERA del toolbar como workaround documentado (A-112).
La ficha ofrecía dos salidas y mi primera recomendación fue la equivocada —
declarar el toolbar deliberadamente uniforme y consagrar el workaround. El
autor la tumbó, con razón: el eidos toolbar-button.svelte era un PASSTHROUGH
que dejaba el <button> nativo de soma, exactamente la clase de bug que
dialog-trigger (2026-06-19) y card-group-title (2026-06-27) ya pagaron, y el
precedente correcto no es menubar-trigger sino Form.Submit. Dos datos que el
primer análisis no tenía:
- El botón era MUDO. El morfo del toolbar sólo declara commit-toggle (el
group-item), así que pulsar una acción no emitía nada. Medido tras el
cambio: click → contact-activate estampado en el nodo + 8 nodos de audio,
donde antes 0.
- La uniformidad la dan los DEFAULTS, no la ausencia del eje: ghost/neutral y
el size del toolbar por contexto de eidos (toolbar/context.ts, calcado del
precedente ButtonGroup y con su misma razón: la receta del Button lee
data-size EN el nodo, un descendant selector no llega). El tipo estrecha a
SelectionVariant (solid | outline | ghost).
La forma es el Button consumer pattern entero: composición vía child de soma
(con el rename outerChild contra la recursión), y la receta CEDE el nodo —
cero cromo de botón en toolbar.css (regla 5); Link y GroupItem siguen siendo
superficie de la barra y los pinta ella. Antes de ceder, las dos recetas sobre
un mismo nodo producían un híbrido medido en el que solid/primary NO pintaba.
La doctrina que faltaba queda escrita en guides/component-guide.md §4: dos
clases de parte con forma de botón en una barra. ACCIÓN en barra (Toolbar.
Button, Form.Submit, Dialog.Trigger) compone el Button; control de SUPERFICIE
de barra (Menubar.Trigger: File, Edit) lo pinta la barra. El test: ¿significa
lo mismo fuera de la barra? Esa distinción vivía en un comentario de
menubar-trigger; el comentario ahora apunta a la doctrina.
Verificado en navegador (Playwright, servidor limpio): el nodo ES el Button
del canon (data-button + data-variant), solid/primary pinta (fondo primary,
tinta blanca), paridad exacta con el GroupItem por construcción — mismo bundle
--size-*: 36px/36px en md, 30px/30px en sm, con la herencia del size del
toolbar medida en vivo al cambiarlo. La demo del app-shell mueve «Nueva» AL
INTERIOR del cluster (workaround retirado, gap del block cerrado en su README)
y la demo del toolbar gana la acción «New» con controles vivos action variant
/ action color.
Gates: svelte-check 72/62 (base 73/62, sin regresión) · vitest eidos+blocks
470/471 (el rojo es skin-media-player, ajeno y documentado en el handoff) ·
blocks:check 0 errores / 19 blocks · docs:check 0/0. Los avisos de prettier de
los dos README ya fallaban en HEAD.
⚠️ El árbol compartido tiene ya encima trabajo NUEVO de otra sesión
(navigation-menu, PLAN-sidebar, book-deviations): este commit lleva sólo los
14 ficheros del eje A-112, verificados por lista.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
2 months ago |
|
|
cae88ca15b |
uix(navigation-menu): un <ul> no es una región, y por eso el landmark salía sin nombre
El morfo declaraba el nombre por defecto de la navegación en la parte que pinta
el <ul>, con la razón escrita al lado: «the root <nav> delegates its name to the
list it wraps». La delegación no existe. El gesto que un lector de pantalla
ofrece para saltar entre las áreas de una página llega al <nav>, y llegaba a un
<nav> ANÓNIMO — con el nombre del consumidor puesto una capa más adentro, en un
elemento que ninguna navegación por landmarks visita.
Sobrevivió tres semanas de tier porque con UNA navegación en la página un
landmark anónimo es admisible, y todas las demos del componente y todos los
blocks de sitio montan una. Apareció en cuanto el app-shell montó dos ámbitos,
que es lo que un shell hace por definición (A-111). La APG es explícita: si una
página incluye más de un landmark de navegación, cada uno lleva su nombre.
El arreglo NO es el que la ficha proponía. Su paso 1 quería que la lista
heredase el nombre por aria-labelledby; el barrido de los morfos hermanos con
defaultElement 'nav' dijo otra cosa: breadcrumb (<ol>) y nav-tree (<ul>)
declaran aria: [] en su lista y nombran sólo el <nav>. navigation-menu era el
único fuera de esa forma, así que la pareja de nombre —el aria-label con su
suppression prop-falsy ariaLabelledby, y el aria-labelledby por propRef— sube
entera a la parte Provider y la lista se queda con su aria-orientation.
En soma, la fuente ariaLabelledby sube al nivel del runtime (la forma de
breadcrumb) y el wrapper DEJA de sacar aria-label de restProps: viaja con el
resto y gana el merge por política de naming, que es lo que A-85 dejó escrito y
lo que este componente no aplicaba. Se van con ello el opt ariaLabel y el
reenvío condicional que la List hacía a mano.
Y como la regla no estaba escrita en ninguna doctrina —grep de «landmark» en
morfo.md, canon/ y guides/ daba cero—, se escribe: architecture/morfo.md §Step 4
gana «A landmark is named on ITS OWN element», y nace el censo
morfo/landmark-census.test.ts, que falla si una parte con defaultElement 'nav'
no declara un attr de nombre EN SU PARTE. Probado en rojo por mutación tres
veces: deshaciendo el arreglo (navigation-menu.provider), retirando la excepción
(palabras.breadcrumb) y vaciando el catálogo, que es la mutación que impide que
un censo sobre nada pase en verde. El ámbito es 'nav' a propósito: main, header
y footer son únicos por página, y section y form sólo son landmark cuando ya
tienen nombre.
El guard destapó de paso un segundo caso de la misma clase: la parte Breadcrumb
de palabras declara un <nav> con aria: [] y nadie la nombra, ni en soma ni en
eidos. Queda como A-116 con EXCEPCIÓN FIRMADA por el autor: palabras es el eje
de otra sesión y su rama es compartida. La tercera fila del censo obliga a
retirar la excepción el día que esa parte declare un nombre.
Verificado con el nombre COMPUTADO desde el árbol AX de Chrome por CDP, nunca
leyendo el atributo (un aria-label en el DOM prueba el atributo, no el nombre),
con servidor recién arrancado y esperando la puerta de hidratación honesta:
/blocks/app-shell/preview navigation: ['Navegación principal',
'Menú de la aplicación',
'Migas de pan'] (antes: el 2.º null)
/uix/components/navigation-menu (sin label del consumidor)
el <nav> computa 'Principal' — el default del morfo,
traducido, sobre el landmark; el <ul>, sin nombre
Gates: 420/420 en morfo+sema+soma+eidos tocados · component:audit --only
navigation-menu PASS · morfo:check PASS · svelte-check 72/62 antes y después ·
docs:check 0/0. Los avisos de prettier de los ficheros tocados ya fallaban en
HEAD (comprobado con git show HEAD:… | prettier --check); el fichero nuevo va
formateado.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
23667277c1 |
docs(blocks): el shell dejó cinco filas, y una explica por qué nadie la había visto
Handoff reescrito para mañana y las cinco filas que abrió el `app-shell` fichadas en el ledger (A-111…A-115). La puerta de entrada de mañana es la primera. **A-111 — `NavigationMenu` deja su landmark ANÓNIMO.** El `aria-label` del consumidor se reenvía al `<ul>` de la parte `List` y el `<nav>` se queda sin nombre; el morfo lo declara así a propósito («the root delegates its name to the list it wraps»). Pero un `<ul>` no es una región: quien navega por landmarks ve el `<nav>`, y lo ve anónimo. Con UNA navegación en la página es admisible —y por eso lleva tres semanas de tier sin salir—; con dos ámbitos incumple la APG, y un shell de aplicación monta dos por definición. Medido en el preview: `navigation: ['Navegación principal', null, 'Migas de pan']`. La ficha lleva la forma propuesta en cuatro pasos: mover el default de naming de la `List` al `Provider` y que la lista apunte al root con `aria-labelledby`; precedencia de clase «naming» (el consumidor gana si dice algo); un guard que falle si un componente con `defaultElement: 'nav'` no puede recibir nombre en su root; y re-medir el censo. Toca morfo + soma, así que va con el aviso de mirar antes si la sesión del `sidebar` sigue viva ahí. Las otras cuatro: **A-112** `Toolbar.Button` tipa contra el `ButtonProps` de soma y no acepta `variant`/`color` · **A-113** la foundation emite los estilos semánticos y no los aplica a ningún elemento —Times New Roman en los 19 previews—, ARREGLADA en el reset del arnés pero con la pregunta de canon abierta · **A-114** `Sidebar.Inset` impone `overflow: auto` y se lleva el `sticky` de dentro · **A-115** no existe `description-list` y el panel de detalle del shell es el «primer detail-view real» que su propia ficha F5 pone como disparador. El handoff recoge además la corrección de fondo del día (la barra y el raíl son dos ÁMBITOS, no la misma navegación en dos tamaños), las dos averías del arnés que este block destapó, y lo que NO se ejecutó: la app de referencia en modo attach, que sigue siendo el único sitio donde el ecosistema entero se demostraría cableado. docs:check 0/0 (639). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
9333acd661 |
docs(blocks): A-95 deja de ser «bloqueada por una pieza que no existe»
La pieza existe desde ayer. La fila pasa a ARREGLADO con sus cifras: solape 0px
entre el aviso y la cabecera, hit-test dentro de la cabecera, documento = 720 =
el viewport. Lo que la cierra no es un número mejor sino haber quitado la
pregunta que tumbó todas las propuestas anteriores («¿y si tenemos banners a
diferentes alturas?»): el aviso entra en flujo como una fila, así que su altura
es la que sea.
⚠️ Sigue siendo cierto lo que la ficha decía del `Affix`: fuera de un shell, un
aviso con `affix="top"` sigue tapando una cabecera pegada. Es el apilamiento
firmado, no un defecto.
A-109 gana disposición escrita (es del canon: el `role="banner"` va después de
los rest props; dos salidas, y se decide en una sesión del canon).
El handoff cambia de puerta: F3 está abierta y `app-shell` hecho. Queda escrito
lo que la sesión NO llegó a ejecutar — la app de referencia en modo attach, que
sigue siendo el único sitio donde el ecosistema entero se demuestra cableado, y
hoy no lo hace ninguno de los siete shells del repo.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
db22de616c |
docs(blocks): el handoff de mañana, y las cinco filas que dejó componer la página
F2b queda cerrada entera, así que el handoff cambia de puerta: lo primero de mañana es el repaso block a block que pediste, y va sin ficha escrita a propósito — su forma se acuerda antes de empezar (qué orden, qué se mira, y si lo que salga se escribe en el ledger o en el README de cada block). Las cinco cosas que la página compuesta y cookie-consent encontraron entran al ledger como A-106…A-110, medidas y sin tocar: Button acepta ref y no lo reenvía —la forma exacta de A-94—, Switch no tiene parte de etiqueta y todos los consumidores del repo repiten el mismo apaño, Dialog devuelve el foco a un nodo muerto cuando su disparador se ha desmontado, Banner deja DOS landmarks banner en cualquier página con cabecera, y feature-split es el único block de sección sin .Header. Casi las meto como A-100…A-104, que ya estaban ocupadas. El rango libre empezaba en A-106 y el ledger tiene 110 filas, no 96: las cifras que el handoff repetía —«96 filas, 64-19-13»— eran una foto vieja de julio. Las de ahora están contadas sobre el fichero: 70 ARREGLADO · 24 CONFIRMADO · 13 REFUTADO · 3 DATO. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
2 months ago |
|
|
331912cf23 |
blocks(background): seis fondos, y el que parecía mejor era el que no se podía leer
F5 de PLAN-background.md: los seis blocks de D-BG.10 (2) reciben `Background.*`. El hero cayó en F2 y la página compuesta ya existía — no había que construirla, había que verificarla con la decoración puesta. La forma es uniforme y tiene un solo desvío: cada block gana un prop `decor` que monta `<Background.Pattern>` como HIJA de su `Section`, la misma composición que el hero ya probó. El desvío es `cta`, donde la decoración va dentro del PANEL, que es donde está el ojo. **Dos hallazgos que sólo la medición podía dar.** Dentro del panel sólido del `cta`, las dos tramas TINTADAS hunden el titular bajo AA: `glow` lleva la copia blanca de 5,18:1 a **3,06:1** y `mesh` a **2,27:1**. Pintan un lavado claro anclado en `50% 0%` — exactamente donde se asienta el titular. Las de línea toman la tinta de regla, oscurecen, y la copia sube a **14,35:1** mientras la textura sigue leyéndose a 3,19:1 contra el panel. Por eso el default es `rings`, que además irradia del mismo anclaje superior que usaba el glow. Las tintadas se ofrecen igual: el app decide, pero ya con el número delante, en el tipo y en el README. Y una trama tintada dentro del panel era invisible por construcción: `--_background-tint` cae en `--color-primary-solid`, que es EL MISMO valor que pinta el panel — un halo del color del panel sobre el color del panel. El block pasa ahora `color="var(--color-content-on-solid)"`, el token que ya usa para su propia copia; medido después, el tinte resuelve a `#ffffff`. **Los defaults, y por qué dos están apagados.** `cta` `rings` · `stats-band` `grid`, porque una banda de cifras se lee como medida y la rejilla es la trama que lo dice sin decorar por decorar · `testimonials` y `feature-split` `glow`. `site-footer` y `banner` reciben la capacidad APAGADA: la columna B del plan de calidad no pide acabado en ninguno de los dos, y encenderlo por nuestra cuenta sería inventar un aspecto que nadie pidió. **Un desvío de alcance, declarado.** La columna B pedía «`Backdrop` alterno» POR FILA en `feature-split`; se resuelve en la SECCIÓN. Una banda por fila obligaría a cada `Row` a poseer el estado de alternancia, y eso es coordinación — un block que coordina deja de ser un block que no posee nada. Medido en Chrome sobre la página compuesta real: cinco pilas, los cinco anfitriones adoptados (`position: relative` + `isolation: isolate`) sin que ningún block los posicione; la copia mide 15,88:1 sobre las secciones decoradas y 5,18:1 sobre el panel del cta — AA en todas. Sobre lienzo oscuro, medido aparte: 17,06 → 13,23:1 con `glow` y 12,37:1 con `grid`/`dots`. Ledger: seis filas nuevas, A-100…A-105, cada una con su mecanismo y su evidencia. Gates: `blocks:check` 0/18 · `rtl:check` 0/180 · `docs:check` 0/0 en 634 docs · 434/435 (el fallo es el `skin-media-player` de siempre) · `check` 0 errores propios · prettier limpio. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
d02021aee1 |
fix(eidos): dos animadores que no se hablaban y un cluster que no envolvia
Dos hallazgos del contraste doctrinal del tier, ambos de CANON, ambos medidos antes y despues. El tercero (A-67) queda REGISTRADO y sin ejecutar: cae en el eje de sema. ── A-99 · Group no aplicaba NINGUNO de sus dos defaults ────────────────────── Su README los promete en tres sitios («defaults pensados para action rows — `align: center`, `wrap: wrap`»). No pasaba ninguno a `Flex`, que cae a `nowrap` + `stretch`. De ahi que las acciones de `hero` no apilaran a 375px (A-98): 315 de 327px en una linea, con la segunda etiqueta truncandose. ⚠️ CORRECCION A MI PROPIA FICHA. Habia escrito que «el `align` SI se cumple, lo que descarta que el recipe no cargue». Era FALSO: el `center` que medi salia de la demo, que lo pasa explicitamente en su control. `group.css` no declara ni `align-items` ni `flex-wrap` — no habia nada que cargar. Medir un default en una demo que lo pisa no es medir un default. Y de las tres salidas que la ficha proponia, «que lo declare el recipe» es IMPOSIBLE: `flex.svelte` tiene `wrap = 'nowrap'` como default de JS y siempre escribe `--flex-wrap` inline, asi que el fallback de `var(--flex-wrap, nowrap)` no se evalua nunca. Solo ganaria una declaracion dura, que congela el eje. El arreglo usa el mecanismo que el propio fichero ya tenia para `direction` y `effectiveGap`: `align = 'center'` destructurado (sobreescribible — `align` no esta omitido de `FlexProps`) y un `effectiveWrap` interno, `wrap` salvo `attached`, que lo invierte. Espejo exacto de «`attached` implica `gap=0`», y por la misma razon fisica: el recipe cuadra los radios interiores y tira de un margen negativo asumiendo UNA linea, asi que un segmentado envuelto abriria su segunda linea con una esquina plana y el borde recortado. EL CENSO DECIDIO LA FORMA. 77 de 91 usos de `<Group` ya pasan `align`; de los que no, tres blocks lo reenvian con default propio y **`ButtonGroup` es el unico que voltea `Group` a columna en todo el ecosistema** — donde `center` habria dejado los botones verticales en dientes de sierra. Un solo caso no justifica un default condicional, asi que la excepcion vive en su call site. Medido: `hero` a 375x800 pasa a DOS lineas (tops 416,3 / 472,3) y la accion secundaria de 124px truncados a 156,9px, su etiqueta entera. ButtonGroup horizontal `attached` sigue `nowrap`; el vertical, `stretch` con los tres hijos a 301px. El README se corrige entero: negaba `attached` como prop canonico y listaba `grow` como «gap conocido» cuando ambos llevan tiempo implementados — describia la era del baseline `air`. ── A-68 + A-93 · dos observadores que no se hablaban ───────────────────────── `CountUp` trae su propio IntersectionObserver (threshold 0) y `Motion` usa otro (threshold 0.1, rootMargin −10%). Un IO no mira la opacidad, asi que las cifras contaban su recorrido entero detras de `opacity: 0`: por scroll lento la primera aparecia ya al 97,8% de su valor. ⚠️ EL DILEMA QUE PLANTEE ERA FALSO. Dije que escalonar el arranque y aterrizar juntas eran incompatibles — cierto — y de ahi que exponer el `seen` de `Motion` rompiera A-69. No se sigue: el retardo del stagger vive en `animation-delay` (CSS) y los observadores de los cuatro `Motion` disparan en el MISMO instante, porque los stats estan en una fila, a la misma altura. Gatear en el REVELADO da arranque conjunto, duraciones iguales y aterrizaje conjunto intacto. Presente como decision entre dos males algo que tenia una tercera opcion mejor. Tambien descarto la «alternativa minima» que llegue a recomendar (alinear el observador de `CountUp` con el de `Motion`): es acoplamiento por copia — duplica threshold + rootMargin en otro fichero, la deriva que el framework elimina con builders tipados — y cambia los defaults de `CountUp` para todos sus usos sueltos. `Motion` publica ahora su momento visto por contexto (`motion/context.ts`, calcado de `cascade/context.ts`, que ya exponia `open`), y `stats-band-value` lo pasa a `startWhen` ANTES del spread de `countOptions`, para que el app pueda desactivarlo. Cero cambios en `CountUp`, cero observadores propios en el block, B-6 intacto — la costura era justamente lo que faltaba en canon (A-93). Medido con Playwright headless, 1280x800. ⚠️ La primera corrida fue INVALIDA y se descarta: inserto el espaciador DESPUES de cargar, y como `Motion` es `once: true` su observador ya habia disparado con la banda en pantalla. El espaciador tiene que existir en el primer pintado (`addInitScript`). Segunda corrida, con la banda nacida bajo el pliegue: - fuera de pantalla 2,6 s: pending 4, opacidades [0,0,0,0], contadores 0/0/0 - ventana que discrimina (banda a 6px dentro, donde el IO de CountUp dispara y el de Motion no): 0/0/0 en la llegada Y tras 2,6 s. Antes, ahi mismo, corrian (1144/31/4) y acababan practicamente terminados (12.121/330/47) - al revelarse, la primera cifra al 0% — antes 97,8% - A-69 INTACTA: las tres aterrizan a 1781 ms, dispersion 0 ms RESIDUO, y es peor que mi estimacion: las cifras que el stagger revela despues llegan al 16,8% (+70 ms) y 35,4% (+210 ms). Estime «≤10%» y la medicion dice 35,4% — el muelle es sobreamortiguado y cubre mucho recorrido al principio. Es el precio del aterrizaje conjunto que A-69 firmo y no baja sin reabrirla. Queda documentado, no escondido. ── A-67 · registrada, no ejecutada ────────────────────────────────────────── Contrastadas contra `engine.ts` y el canon, las tres puertas de su ficha fallan: el pack no puede distinguir el `collapse` de un intercambio del de un cierre a secas (mismo evento, mismo tipo de nodo; pediria un `:has()` a mano sobre el hermano); el arbitro ya implementa la mitad del «on a tie, the most recent» que un filtro de LLEGADA permite, y la otra mitad exigiria revocar una nota ya agendada, que `EngineSound` no sabe hacer; y un evento unico rompe los `commits`, porque cada uno estampa `data-state` sobre SU item. La puerta correcta no estaba en la lista: un nuance declarado, `emerge-collapse-swap`. La forma es canonica y el provider lo sabe de forma determinista en `setValue`. Cae en el eje de sema, asi que NO se ejecuta aqui — se deja elegida para su sesion. Puertas: svelte-check 72 errores / 59 avisos = linea base exacta · blocks:check 0 sobre 15 blocks · docs:check 0/0 sobre 629 docs. La suite completa deja 8 rojos en 3 ficheros, todos nombrando waveform / skin-media-player / radio-group mas un timeout de orca — ninguno en lo tocado aqui. Prettier solo se queja de ficheros CRLF preexistentes (el diff seria el fichero entero), asi que no se reformatean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
4caf1100a1 |
refactor(blocks)!: el eje de tamano dice de QUIEN es — size deja de significar dos cosas
LA AMBIGUEDAD, medida. En el canon `size` no es ambiguo: es «el eje de tamano de
ESTE componente», y cada uno lo materializa a su manera — padding de bloque en
`Section`, densidad de la tira en `Banner`, escala tipografica en `Text`. La
ambiguedad la creaba el tier: un block no es ninguno de esos componentes, y al
reenviar `size` estaba reenviando el eje de una pieza INTERNA. Desde fuera:
<FeatureGrid size="xl"> cambia el AIRE de la seccion
<SiteBanner size="lg"> cambia la ALTURA de la tira
Misma prop, dos cosas, y una con un valor menos en la escala (`BannerSize` no
tiene `xl`). Un consumidor que aprende el eje en trece blocks se equivoca en el
catorceavo.
EL RENOMBRADO:
- `size` -> `sectionSize` en los 13 que envuelven `Section`
- `container` -> `containerSize` en los 14 que envuelven `Container`
- `size` queda LIBRE y solo lo usa `banner`, donde si es el tamano del block
- `minColumnWidth` -> `minChildWidth` en `site-footer`: el mismo concepto tenia
dos nombres en el tier, y ninguno era el del canon
El patron no se invento: el tier YA nombraba un eje por su pieza (`container`).
`containerSize` + `sectionSize` lo hace explicito y simetrico.
LA REGLA, en `architecture/blocks.md` §Conventions, para que no reaparezca: un
block que reenvia el eje de tamano de una pieza interna lo nombra `{pieza}Size`,
y `size` se reserva para el tamano del block. Corolario: un eje que el canon ya
nombra se reenvia CON SU NOMBRE.
METODO — se renombraron los TIPOS primero y se dejo que `svelte-check` senalara
cada consumidor, en vez de buscar a mano. Cazo los cinco call sites que quedaban
(`CtaSite`, `HeroSite`, `SiteHeaderSite`, `TeamSite` y un uso suelto en
`cta.svelte`). ⚠️ Y cazo tambien un error propio: el primer patron era demasiado
ancho y renombro `size` en SIETE sub-partes donde ese `size` es el del `Card` o
el `Text` que envuelven (`testimonials-item`, `contact-reason`,
`team-member-role`…). Revertidas antes de seguir.
DOCUMENTACION revisada entera: corregidas las tres filas de README que describian
props del block con el nombre viejo (`hero` x2, `site-header`); conservadas las
seis que hablan del `size` de un componente del canon (`Banner`, `Accordion`,
`Heading`), que no cambia. `PLAN-blocks.md` se deja intacto: es bitacora, y
reescribir el registro historico para que cuadre con el presente lo falsearia.
LEDGER — el contraste doctrinal llega a 15/15, y cinco filas nuevas:
- A-95 `banner`: con `affix="top"` el aviso tapa la cabecera pegada (medido:
`elementFromPoint` sobre el header devuelve la tira). NO se arregla aqui: la
pieza que falta es la que POSEE las alturas de pagina — Mantine lo resuelve en
`AppShell`, que declara `header={{ height }}` y desplaza el resto; nuestro
equivalente es el block `app-shell`, sin construir.
- A-96 `banner`: `affixOffset` es prop publica sin control vivo en la demo.
- A-97 `feature-grid`: RETIRADA el mismo dia. Se midio el texto `muted` con el
4,5:1 generico de WCAG y el proyecto tiene OTRA vara — «floors APCA >= 60 ∧
WCAG >= 3», con guard propio, y la tinta secundaria es «marginal sub-4.5:1 by
design». Re-medido con `src/arts/color/apca.ts`: Lc 63,7 y 3,70:1, cumple las
dos. (El primer intento con `apcaLc` devolvio 102666 porque le pase 0..255
donde pide 0..1: un valor fuera de rango no es un hallazgo, es un formato.)
- A-98 `hero` + A-99 `Group`: las acciones no apilan ni envuelven. La causa NO es
del tier — `Group` no aplica el `wrap` que su README promete en TRES sitios:
`group.svelte` nunca se lo pasa a `Flex`, el recipe no lo declara, y
`Omit<FlexProps,'wrap'>` impide compensarlo desde fuera. El `align: center` si
se cumple, lo que descarta que el recipe no cargue.
Y A-25 cae (la demo ya no promete elevacion) y A-47 corrige su disposicion: el
anillo de foco es «a config axis» por doctrina — endurecerlo es una decision de
VALOR en `color.focus.ring`, «never a per-component CSS change», que es
exactamente lo que hacia el intento revertido en `e468e764b`.
Gates: blocks:check verde (15 blocks / 115 ficheros) · svelte-check 72 errores /
59 avisos = linea base exacta, ninguno en blocks · docs:check 0/0 sobre 629 docs.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
948f7cf7e1 |
feat(blocks): el guard vigila la frontera, y siete filas del ledger caen medidas
FASE 5 DEL SANEAMIENTO — `blocks:check` deja de vigilar solo la forma del tier
y pasa a vigilar su frontera, su documentacion y su catalogo. Tres reglas, cada
una con su fixture negativo en el selfTest():
1. Lista blanca de importaciones (B-4). La frontera dura 1 era prosa; ahora es
codigo: $uix, los arts publicos, $libs/forms, svelte y los relativos del
propio block. Los arts se DERIVAN de src/arts/*, no se listan a mano — una
lista escrita se queda atras el dia que aterriza un art. El tier ya cumplia:
cero violaciones al encenderla.
2. README completo (B-9) + declaracion de landmark (B-8). Salieron 7 de 15 sin
ella; escritas leyendo la fuente de cada block, no la plantilla.
3. Ficha en el catalogo de demos (B-9), en los DOS sentidos: un block que el
catalogo no publica es invisible para el rail, y un slug publicado sin block
detras es un enlace muerto.
De paso el escaner deja de leer los comentarios como codigo: los index.ts
documentan su uso con un `// import { Cta } from '$blocks/cta'`, y una lista
blanca que lee prosa habria empezado a acusar a los ejemplos.
Verificado EN ROJO sobre el arbol real, no solo contra los fixtures: un zod y un
$libs/days metidos en hero/types.ts salen con su linea exacta mientras el mismo
import dentro de un comentario NO salta; el catalogo falla en las dos
direcciones; y al romper isAllowedSpec a proposito el self-test aborta el guard
en vez de dar verde.
ARREGLOS DEL CONTRASTE DOCTRINAL — siete filas, cada una con medicion o gate:
- A-88 team: `height="100%"` en el Stack del Member. El div que pinta Motion es
un bloque pelado, asi que el Stack se quedaba a altura de contenido y el
`margin-top:auto` de los enlaces repartia CERO. Medido: la desviacion entre
filas de iconos pasa de 20px a 0, y el margen reparte 20,297px.
- A-94/A-28 (feature-grid, team, testimonials): los tres declaraban su tipo como
`= AutoGridProps` y ponian {...rest} ANTES de las props que fijan, asi que un
consumidor podia escribir align="start" y no obtener nada. Cerrados con
Pick<AutoGridProps, ...>, que conserva los tipos exactos del canon.
- A-84 feature-grid: el eje `align` llega a las partes por contexto, como en
team, en vez de que el typedoc instruya al app a repetirlo. Medido: con
align=center el align-items computado es center en cabecera Y celdas.
- A-19 faq: `marginX="auto"` en el Header — Box no centra solo. Medido: el Box
de 768px resuelve margin-inline 248px/248px donde antes daba 0.
- A-27 testimonials y A-86 pricing: el typedoc decia `soft` donde el codigo hace
`outline`, y el ejemplo de la puerta de entrada no compilaba.
- A-91 banner (fila nueva): el block hereda el eje intent/color que el canon
declara fuera del sistema abierto en su propio typedoc, sin registrarlo.
LEDGER — dos lecciones de instrumento escritas dentro, porque ambas produjeron
hallazgos falsos publicados:
1. El dev server sirve codigo ANTERIOR a HEAD si reusa cache de Vite. Tres filas
(A-36, A-69, A-85) se dieron por vivas estando arregladas. El delator fue
aritmetico: las duraciones medidas eran EXACTAMENTE las que el comentario del
arreglo cita como estado anterior.
2. Contar nodos WebAudio no es medir sonido: la sintesis crea dos osciladores
por earcon, un sound pack de muestras cambia la aritmetica entera, y crear
nodos no es sonar. El contador responde "hubo actividad" y nada mas.
Y el error de metodo del que ambas son sintoma, tambien registrado: se fue a
re-medir A-67 sin leer su ficha, que ya traia los gains del catalogo, el empate
de applyDominance y la regla de CANON.md incumplida — mejor razonado que la nota
que lo sustituia.
Gates: blocks:check verde (15 blocks / 115 ficheros) · svelte-check 69 errores /
57 avisos = linea base exacta, ninguno en lo tocado · docs:check 0/0 · prettier
limpio en todo lo del commit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
866089a407 |
fix(blocks,canon): el ledger dice la verdad y el arco perceptivo vuelve a sonar
La auditoria del tier tenia los veredictos desemparejados de sus hallazgos: un join por POSICION, y el journal del workflow era de sesion. Reproducido en vivo al re-verificar — el journal devuelve los resultados en otro orden que la entrada. Ledger nuevo con 90 ids estables (AUDIT-blocks-ledger.md), union SIEMPRE por id: 41 arreglados, 34 confirmados, 12 refutados con motivo escrito, 3 dato. La tasa real de refutacion es del 13%, no del 28%. De las 12 ALTA de percepcion, 8 tenian la causa raiz en el CANON. Los blocks componen bien; lo que estaba roto era el arco perceptivo. CANON - Link: la prop color era inerte en subtle/plain (inherit a 0-2-0 ganaba a la paleta a 0-1-0), el hover clavaba primary y el active quedaba tapado por el hover de variante. Medido: el enlace del footer pasa de la tinta del padre a la suya. - Card: prometia BoxProps y no los aplicaba — height="100%" era un atributo inerte. El tipo dice la verdad y el eje de tamano se aplica con un helper compartido (buildSizeStyle). Tarjetas al fin de igual alto. - Form.Submit / Form.Reset: componen el Button del canon, asi que la accion principal de un formulario recupera el contact-activate en el gesto. Y el aria-label generico deja de pisar el texto propio (WCAG 2.5.3 para todo consumidor), verificado con el AX tree de Chrome. - Field: dejaba de duplicar commit-submit dentro de un Form — un Enter emitia dos commits con intents contradictorios y dos earcons. - NavigationMenu: commit-select se mueve al Link (navegar es el acto evaluable), el despliegue habla como emerge-open/close en vez de fingir una seleccion por hover, y el pack casa por fin con la parte que recibe el estampado — antes sonaba a la ganancia base, 10x lo disenado. - Badge: el boton de quitar compone IconButton; la altura del chip pasa a ser la del control, asi que md significa lo mismo en todo el sistema. - Fundacion: [data-on] arrastra la propiedad color, no solo las variables. BLOCKS - hero: la CTA secundaria pasa de 1.61:1 a 6.61:1. - contact: separa incomplete de invalid — el camino de error por campo era inalcanzable por construccion — y su frase llega a la AT. - newsletter: coordina (fase 4 del plan). Maquina de cinco estados, palabras propias, Submit y Reason como partes que leen el contexto. - site-header ya no congela la pagina al cruzar el breakpoint; site-footer emite su commit; pricing no se vacia; cta llega a sangre de verdad. DOCTRINA - La frontera dura 1 nombra $libs/forms como puerta sancionada. - El contrato B admite un segundo servicio: el anunciador. Un block que posee las palabras de sus estados tiene que poder decirlas. Gates: blocks:check 15/0 · vitest 20/20 en blocks y 402/402 en eidos+blocks · svelte-check 75/54 (linea base) · docs:check 0/0 · prettier limpio. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |