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 (3bf7930baa3ae26ccb38e7f33d3fb488df73f06a)
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
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 |
|
|
edf0639eea |
blocks(app-shell): la barra y el raíl no son la misma navegación en dos tamaños, son dos ÁMBITOS
Corrección del autor, y era una equivocación de fondo, no de forma. Yo había escrito que un menú en la barra sería «una segunda navegación dentro de un shell cuya navegación es el raíl». Es al revés: - la BARRA lleva la APLICACIÓN — su menú general, las áreas que tiene, y lo que es de la persona y no de la página (buscar, avisos, la cuenta). No cambia cuando el lector se mueve dentro de un contexto; - el RAÍL lleva el CONTEXTO en el que está — este proyecto, esta tabla: sus secciones y sus acciones. Cambias de contexto y el raíl entero cambia; la barra no se mueve. Es el `TopNav` + `SideNav` de Atlassian y el `TopBar` + `Navigation` de Polaris, que estaban en mi propio dossier. Y es también por qué una app publica DOS landmarks `navigation` legítimamente: la APG pide nombre único en los repetidos, y cada uno nombra un ámbito distinto. La demo modelaba mal: el raíl era el menú general. Ahora la barra lleva marca + `NavigationMenu` de la aplicación (Bandeja · Proyectos · Informes) y el raíl es el proyecto Apollo (Tablero · Conversaciones · Archivos + sus Acciones). El rastro se va con ella: su sitio es la cabecera de la PÁGINA, no la barra — cambia con cada vista mientras la barra no, y una barra que lo lleva crece una segunda fila en cuanto la ruta tiene tres niveles, que es lo que se midió antes de moverlo. De `site-header` se queda sólo la razón que era cierta: no es que sobre una navegación, es que la pieza es de un SITIO — `Sticky` por defecto (una barra de app bajo `scroll="main"` no debe pegarse) y `Container` MEDIDA (una barra de app va de borde a borde). Su forma marca · nav · acciones es la correcta. **Y un defecto de canon que sólo aparece con dos navegaciones**: `NavigationMenu` deja su landmark ANÓNIMO — el `aria-label` se reenvía al `<ul>` y el `<nav>` se queda sin nombre (el morfo lo declara así: «the root delegates its name to the list it wraps»). Un `<ul>` no es un landmark. Medido: `navigation: ['Navegación principal', null, 'Migas de pan']`. No se arregla desde fuera —nombrar la lista no nombra la región, y envolver en otro `<nav>` anidaría landmarks—, así que queda VISIBLE y registrado en los Gaps. Guards: `blocks:check` verde · `docs:check` 0/0 · `svelte-check` sin errores propios (de paso, `NavigationMenu.Item` exige `value` y no se lo pasaba). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
1d91c11e89 |
blocks(app-shell): la demo no usaba los componentes que SIGNIFICAN lo que enseñaba
Rehecha entera. Cada pieza es ahora el componente del canon que corresponde al
acto, no una caja con el aspecto adecuado:
- las tres cifras son `Metrics` (label · value · delta), con la dirección
desacoplada de la valencia: en «sin asignar», BAJAR es bueno (`goodTrend`),
que es justo lo que las referencias hacen mal;
- la bandeja y la actividad son `Feed` — `role="feed"`, cada conversación un
`role="article"` con `aria-posinset/setsize` y su título con `aria-level`,
teclado de feed incluido. Medido: 2 feeds, 11 artículos, 11 encabezados;
- la barra superior es `Toolbar` (`role="toolbar"`, roving tabindex: cinco
controles cuestan UNA parada de tabulación) más `Breadcrumb` con `current`;
- la tipografía sale de los estilos SEMÁNTICOS del tema — `Text
style="label|caption|body"`, `Heading level` + `style` — en vez de tamaños
elegidos a ojo, que es lo que había.
**Y la avería que esto destapó, que era del arnés y afectaba a los 19
previews**: la foundation declara los `@font-face` y emite los estilos
(`--style-body-*`…) pero no los aplica a ningún elemento — un `Text` ancla su
familia en su recipe y todo lo demás HEREDA del documento. El arnés de blocks no
anclaba nada, así que `html`, `body`, `[data-sidebar]` y cada fila del raíl
computaban **Times New Roman** al lado de los `Text` en Instrument Sans. El
shell de docs sí lo ancla (`uix.css`); éste no. Anclado en `_lib/reset.css` con
los tokens del tema, no con una familia escrita. De paso, el sangrado de 40px
que el navegador pone a `dd`.
Tres huecos del canon que salieron al componer, registrados en los Gaps:
1. **`Sidebar.MenuButton` es mudo y `NavigationMenu.Link` no** — el mismo acto
con dos respuestas. El README del `Sidebar` firma que «navegar una fila es
nativo y no suena», pero entonces el `NavigationMenu` contradice la firma; y
en un shell la fila casi nunca navega: SELECCIONA la sección. Es de sema, no
del tier.
2. **`Toolbar.Button` no acepta `variant`/`color`** — tipa contra el
`ButtonProps` de soma (`{id, disabled}`), así que un cluster no puede decir
cuál es su acción principal. Aquí va al lado, como en las referencias.
3. **No existe `description-list`** y el panel de detalle es exactamente el
«primer detail-view real» que su ficha F5 pone como disparador.
Guards: `blocks:check` verde · `docs:check` 0/0 · `svelte-check` sin errores
propios (dos props inexistentes corregidas de paso: `Icon.Filter` y el
`variant` del `Toolbar.Button`).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
026e07beaa |
blocks(app-shell): la demo estaba escrita a mano, no con la capa visual
La escribí sin haber leído la doctrina de eidos y salió lo que sale así: `<div
style>` donde hay primitivos, `inline-size: 16rem` clavado, `font-size` en un
`<strong>`, bordes y fondos a pelo, y contenido de relleno («Bloque de contenido
1»). Rehecha contra la tabla «Build contract» de `guides/component-guide.md`.
Ahora **cero literales y cero `style=`** en el fichero: color por token de rol,
espaciado por `--space-*` a través de los props de layout, tipografía por la
escala de `Text`/`Heading`, paneles por `Card`, separaciones por `Separator`,
anchura del aside por `--sidebar-width` (la medida del propio sistema, que
respeta densidad y `--scaling`).
Dos props que no hacían NADA, encontradas al reescribir:
- `Badge intent="affirm"` — `Badge` no tiene `intent`, tiene `color`.
- `AutoGrid minChildWidth="var(--container-width-sm)"` — le pasé un breakpoint
como suelo de pista, así que las tres tarjetas caían en una columna. Ahora
`Grid columns={{ base: 1, sm: 3 }}`, sin literal ninguno.
Y una tercera cosa medida que NO se arregla desde aquí: el aside llevaba
`Surface variant="soft"` y es **inerte** — `oklch(0.9911 0 0)` contra un fondo
de página de la misma claridad, o sea invisible en claro. Es **F15** de
`PLAN-blocks-quality.md` §6, gap de canon ya registrado («soft no acota un
panel, y Surface no tiene borde»). La frontera la pone un `Separator`, que es lo
que sí acota, y el porqué queda escrito en el fichero para que nadie lo
«arregle» con un fondo a mano.
Verificado mirando las capturas, claro y oscuro, 1280 y 375.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
6a53c3dc99 |
blocks(app-shell): la pieza que posee las alturas de la página, y por eso el aviso deja de tapar la cabecera
F3.1 del tier, con su fase 0 firmada delante. El block que faltaba: el que POSEE las regiones de una página de aplicación — sus landmarks, sus enlaces de salto y sus alturas. **A-95 se cierra por construcción, no compensando.** Bajo `scroll="main"` no hay nada fijo: el aviso es una fila, el cromo es una fila y la página es la fila que se desplaza. Medido con la tira puesta: aviso [0,54], cabecera [54,85], **solape 0**, y el hit-test en el centro de la cabecera cae en la cabecera (antes: 49px de solape y el clic en la tira). El eje `scroll` es el que Mantine expone como `mode` y Atlassian como `isFixed` por slot; `body` existe porque `docs-shell` lo necesitará para anclas y TOC. Compone `Sidebar` (sus tres colapsos, su drawer móvil, sus dos landmarks), `SkipLink`, `Sticky`, `Grid`/`Flex`/`Box`. Sin `.css`. Coordina tres booleanos (`navOpen · asideOpen · mobile`) por contexto, sin máquina y sin palabras: nada en un shell puede bloquearse, así que no hay frase que decir. Medido en navegador (Playwright; 1280 y 375, claro y oscuro, LTR y RTL): - landmarks por construcción — 1 banner · 1 main · 2 navigation NOMBRADOS · 2 complementary NOMBRADOS · 1 contentinfo; encabezados 1-2-2-2-2 sin saltos. - `scroll="main"`: documento 720 = viewport, el main scrollea dentro y la cabecera se queda en 0 tras desplazar 900px. `scroll="body"`: el documento scrollea y la cabecera SE PEGA (top 0 a 900px de scroll). - teclado: los tres enlaces de salto son las tres primeras paradas y Enter deja el foco en el `<main>`. - 375: el raíl es drawer, devuelve el foco al trigger con Escape, el aside se pliega y el documento no crece. RTL: sin desbordamiento horizontal, raíl a la derecha, aside a la izquierda. Cuatro averías que sólo aparecieron midiendo, y las cuatro son doctrina ahora: 1. `minHeight` es un SUELO, no un techo: con `100dvh` ahí el documento crecía con el contenido. Es `height`. 2. Un grid que sólo declara filas tiene UNA columna implícita `auto`, que se encoge a su contenido: la página salía en una tira de 32px al lado del raíl. 3. `Box` declara `flex-grow/shrink/basis`, así que pisa el `flex: 1 1 auto` de la receta del inset. Quien está más abajo en el árbol tiene que PEDIR crecer. 4. Un ancestro con `overflow` se lleva el `position: sticky` de dentro. El `overflow: auto` de `Sidebar.Inset` dejaba la barra sin pegar bajo `body` mientras `data-stuck` decía que sí — el atributo dando la razón a una página que no la tenía. Por eso el shell pone su propia caja: gap de canon anotado (el inset debería dejar elegir si es contenedor de scroll). **Y una avería del ARNÉS que este block destapó**: los 19 previews del tier llevaban los 8px de margen por defecto del navegador, así que ningún block se enseñaba a sangre — contra la regla dura del propio tier. Medido: `x: 8, y: 8, w: 1264` en 1280, la página compuesta incluida, cuyo README cita cifras a sangre. Invisible porque todos scrollean; deja de serlo con un shell que llena el viewport. Arreglado en el reset del arnés. De paso, en el canon `skip-link`: el enlace del panel complementario decía «Ir a la barra lateral», que es como todo el mundo llama al raíl de NAVEGACIÓN. En una página que tiene los dos, nombraba al que no era. Ahora «Ir al panel lateral». Guards: `blocks:check` verde con 19 blocks · `docs:check` 0/0 (638) · `svelte-check` 74, ninguno aquí. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |