You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/web/routes/blocks/app-shell/AppShellSite.svelte

561 lines
19 KiB

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
<script lang="ts">
/**
* A real application inside the shell — the only way to see what a shell
* does. A frame with lorem in it would show the grid and hide the point:
* the rail that collapses, the bar that stays put while the page scrolls,
* the aside that folds when the width runs out, and the bypass links that
* appear on the first Tab.
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
*
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
* Two rules this file obeys, both learnt the expensive way on 2026-08-19:
*
* 1. Every piece is the canon component that MEANS what it shows, consumed
* through its props — `Metrics` for a KPI, `Feed` for a stream of
* articles, `Toolbar` for the action cluster, `Breadcrumb` for the trail,
* `Sidebar` for the rail. Not one `<div style>`, not one literal length,
* colour or font size: colour by role token, spacing by `--space-*`
* through the layout props, elevation by `Card`. A hand-rolled panel
* where a primitive exists is the drift the build contract names
* (`guides/component-guide.md` §Build contract).
* 2. Type comes from the theme's SEMANTIC styles, never from a size picked
* by eye: `Heading` takes `level` (the outline) and `style` (which of the
* theme's `--style-h*` treatments), `Text` takes `style="label"` /
* `"caption"` / `"body"`. The theme decides what a label looks like; the
* page decides which things are labels.
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
*/
import { AppShell, type AppShellScroll } from '$blocks/app-shell';
import { Sidebar } from '$uix/eidos/components/sidebar';
import { Avatar } from '$uix/eidos/components/avatar';
import { Badge } from '$uix/eidos/components/badge';
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
import { Banner } from '$uix/eidos/components/banner';
import { Box } from '$uix/eidos/components/box';
import { Breadcrumb } from '$uix/eidos/components/breadcrumb';
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
import { Button } from '$uix/eidos/components/button';
import { Card } from '$uix/eidos/components/card';
import { Container } from '$uix/eidos/components/container';
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
import { Feed } from '$uix/eidos/components/feed';
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
import { Flex } from '$uix/eidos/components/flex';
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
import { Grid } from '$uix/eidos/components/grid';
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
import { Group } from '$uix/eidos/components/group';
import { Heading } from '$uix/eidos/components/heading';
import * as Icon from '$uix/eidos/components/icon';
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
import { Metrics } from '$uix/eidos/components/metrics';
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
import { NavigationMenu } from '$uix/eidos/components/navigation-menu';
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
import { Separator } from '$uix/eidos/components/separator';
import { Stack } from '$uix/eidos/components/stack';
import { Text } from '$uix/eidos/components/text';
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
import { Toolbar } from '$uix/eidos/components/toolbar';
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
import type { SidebarCollapsible } from '$uix/eidos/components/sidebar';
let {
scroll = 'main' as AppShellScroll,
collapsible = 'icon' as SidebarCollapsible,
rail = true,
aside = true,
footer = true,
banner = false
}: {
scroll?: AppShellScroll;
collapsible?: SidebarCollapsible;
rail?: boolean;
aside?: boolean;
footer?: boolean;
banner?: boolean;
} = $props();
let navOpen = $state(true);
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
let active = $state('/apollo/inbox');
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
let noticeUp = $state(true);
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
const kpis = [
{ label: 'Abiertos', value: 128, delta: '12%', trend: 'up' as const, note: 'esta semana' },
{
label: 'Sin asignar',
value: 17,
delta: '4',
trend: 'down' as const,
goodTrend: 'down' as const,
note: 'desde ayer'
},
{
label: 'Cerrados hoy',
value: 42,
delta: '2',
trend: 'up' as const,
note: 'sobre el objetivo'
}
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
];
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
const conversations = [
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
{
id: 'ada',
from: 'Ada Lovelace',
initials: 'AL',
subject: 'Notas de la máquina analítica, v2',
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
summary: 'Adjunto la segunda pasada. Los diagramas de las tarjetas siguen sin cuadrar.',
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
when: '09:41',
unread: true
},
{
id: 'grace',
from: 'Grace Hopper',
initials: 'GH',
subject: 'El compilador pasa en verde',
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
summary: 'Toda la batería en verde por primera vez. Falta el caso de la coma decimal.',
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
when: '08:12',
unread: true
},
{
id: 'karen',
from: 'Karen Spärck Jones',
initials: 'KS',
subject: 'Pesos de ranking para el índice',
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
summary: 'Propongo IDF sobre el corpus entero, no por colección.',
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
when: 'Ayer',
unread: false
},
{
id: 'radia',
from: 'Radia Perlman',
initials: 'RP',
subject: 'El árbol de expansión, revisitado',
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
summary: 'Un poema, y una prueba de que converge.',
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
when: 'Ayer',
unread: false
},
{
id: 'barbara',
from: 'Barbara Liskov',
initials: 'BL',
subject: 'Sustitución en la API nueva',
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
summary: 'Si un subtipo no puede sustituir al tipo, no es un subtipo.',
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
when: 'Lunes',
unread: false
}
];
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
const activity = [
{
id: 1,
who: 'Grace H.',
what: 'cerró la conversación «Compilador» como resuelta',
when: 'hace 12 min'
},
{ id: 2, who: 'Ada L.', what: 'reasignó «Máquina analítica» a Radia P.', when: 'hace 40 min' },
{ id: 3, who: 'Karen S.', what: 'añadió la etiqueta «índice»', when: 'hace 2 h' },
{
id: 4,
who: 'Barbara L.',
what: 'comentó en «Sustitución en la API nueva»',
when: 'hace 3 h'
},
{ id: 5, who: 'Radia P.', what: 'abrió una conversación nueva', when: 'ayer' },
{ id: 6, who: 'Sistema', what: 'importó 14 conversaciones del buzón compartido', when: 'ayer' }
];
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
/**
* THE APPLICATION's menu — the bar. It names the areas the product has, and
* it does not change when the reader moves inside one of them.
*/
const appMenu = [
{ href: '/inbox', label: 'Bandeja' },
{ href: '/projects', label: 'Proyectos' },
{ href: '/reports', label: 'Informes' }
];
/**
* THE CONTEXT's rail — the sections and actions of the project the reader
* has open. Change project and this whole list changes; the bar above does
* not move. That is the split the shell exists to hold (Atlassian's TopNav +
* SideNav, Polaris' TopBar + Navigation).
*/
const contextNav = [
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
{
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
group: 'Apollo',
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
items: [
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
{ href: '/apollo/board', label: 'Tablero', icon: Icon.LayoutGrid },
{ href: '/apollo/inbox', label: 'Conversaciones', icon: Icon.Inbox, badge: '5' },
{ href: '/apollo/files', label: 'Archivos', icon: Icon.Folder }
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
]
},
{
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
group: 'Acciones',
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
items: [
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
{ href: '/apollo/new', label: 'Nueva conversación', icon: Icon.Plus },
{ href: '/apollo/members', label: 'Miembros', icon: Icon.Users },
{ href: '/apollo/settings', label: 'Ajustes del proyecto', icon: Icon.Settings }
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
]
}
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
];
</script>
{#snippet notice()}
{#if banner && noticeUp}
<Banner intent="primary" variant="solid" aria-label="Aviso de producto">
<Container width="100%" paddingX={0}>
<Group gap={3} align="center" justify="space-between">
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
<Text style="body" size="sm">La facturación cambia el 1 de septiembre.</Text>
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
<Banner.Close onclick={() => (noticeUp = false)} />
</Group>
</Container>
</Banner>
{/if}
{/snippet}
{#snippet navContent()}
<!-- No `Sidebar.Panel` here: the shell owns it (it is the landmark its skip
link jumps to). The app fills the panel, it does not re-open it. -->
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
<!--
The rail's head names the CONTEXT, not the product: the product is the
bar's, and repeating it here would spend the one line that tells the
reader where they are.
-->
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
<Sidebar.Header>
<Group gap={2} align="center">
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
<Icon.Folder size="sm" />
<Stack gap={0}>
<Text style="label">Apollo</Text>
<Text style="caption">Proyecto</Text>
</Stack>
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
</Group>
</Sidebar.Header>
<Sidebar.Content>
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
{#each contextNav as section (section.group)}
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
<Sidebar.Group>
<Sidebar.GroupLabel>{section.group}</Sidebar.GroupLabel>
<Sidebar.Menu>
{#each section.items as item (item.href)}
<Sidebar.MenuItem>
<Sidebar.MenuButton
href={`#${item.href}`}
active={active === item.href}
tooltip={item.label}
onclick={(e: MouseEvent) => {
e.preventDefault();
active = item.href;
}}
>
<item.icon size="sm" />
<span>{item.label}</span>
{#if item.badge}
<Sidebar.MenuBadge>{item.badge}</Sidebar.MenuBadge>
{/if}
</Sidebar.MenuButton>
</Sidebar.MenuItem>
{/each}
</Sidebar.Menu>
</Sidebar.Group>
{/each}
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
</Sidebar.Content>
<Sidebar.Footer>
<Group gap={2} align="center">
<Avatar size="xs"><Avatar.Fallback>JV</Avatar.Fallback></Avatar>
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
<Text style="caption">Juan V.</Text>
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
</Group>
</Sidebar.Footer>
{/snippet}
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
<!--
The top bar. Two semantic regions, not one row of buttons: the TRAIL on the
start side (`Breadcrumb` — a `<nav>` naming where the reader is) and the
ACTION CLUSTER on the end side (`Toolbar` — `role="toolbar"`, roving tabindex,
arrow keys, so five controls cost one tab stop). The rail trigger sits before
the trail because it is chrome, not an action on this page.
-->
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
{#snippet header()}
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
<Stack gap={0}>
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
<Box paddingX={4} paddingY={2}>
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
<Group gap={3} align="center" justify="space-between">
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
<Group gap={4} align="center">
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
{#if rail}
<Sidebar.Trigger><Icon.PanelLeft size="sm" /></Sidebar.Trigger>
{/if}
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
<Group gap={2} align="center">
<Icon.Sparkles size="sm" />
<Text style="label">Acme</Text>
</Group>
<!--
THE APPLICATION's menu — a `NavigationMenu`, its own `<nav>`
landmark, at a different scope from the rail's.
⚠️ Its landmark ships ANONYMOUS and that is not this file's
doing: the `aria-label` below reaches the `<ul>`, never the
`<nav>` — the morfo delegates the root's name to the list it
wraps. A `<ul>` is not a landmark, and this page has three
navigations, so the APG's unique-name rule is unmet.
Registered in the block's Gaps and left VISIBLE (measured:
`navigation: ['Navegación principal', null, 'Migas de pan']`)
rather than faked with a second `<nav>` around it, which
would nest one landmark inside another.
-->
<NavigationMenu aria-label="Menú de la aplicación">
<NavigationMenu.List>
{#each appMenu as entry (entry.href)}
<!-- `value` is required: it is the item's identity for the
open-menu state, even on an item that only links. -->
<NavigationMenu.Item value={entry.href}>
<NavigationMenu.Link
href={`#${entry.href}`}
active={entry.href === '/projects'}
onclick={(e: MouseEvent) => e.preventDefault()}
>
{entry.label}
</NavigationMenu.Link>
</NavigationMenu.Item>
{/each}
</NavigationMenu.List>
</NavigationMenu>
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
</Group>
<Group gap={2} align="center">
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
<Toolbar aria-label="Acciones de la página" size="sm" variant="ghost">
<Toolbar.Button>
<Icon.Search size="sm" />
Buscar
</Toolbar.Button>
<Toolbar.Button>
<Icon.Bell size="sm" />
Avisos
</Toolbar.Button>
<Toolbar.Separator />
<Toolbar.Button aria-label="Cuenta de Juan V.">
<Avatar size="xs"><Avatar.Fallback>JV</Avatar.Fallback></Avatar>
</Toolbar.Button>
</Toolbar>
<!--
The primary action stays OUTSIDE the toolbar, and that is a
finding rather than a preference: `ToolbarButtonProps` types to
SOMA's `ButtonProps` (`{ id, disabled }`), so a toolbar button
takes no `variant`/`color` — the treatment belongs to the
Toolbar root and every item wears it. A cluster where one action
is the principal one has no way to say so from inside. Registered
in the block's Gaps; here the canon `Button` sits beside the
cluster, which is also what the references do.
-->
<Button size="sm">
<Icon.Plus size="sm" />
Nueva
</Button>
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
</Group>
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
</Group>
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
</Box>
<Separator decorative />
</Stack>
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
{/snippet}
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
<!--
The detail panel. Its content is a DESCRIPTION LIST — term / detail pairs —
and the canon has no component for that yet (`description-list` sits in the
tier's F5 backlog, waiting for «the first real detail view»; this is it, and
it is registered in the block's Gaps). So the semantics come from the
elements that carry them, `dl` / `dt` / `dd`, and the type from the theme's
`label` and `body` styles — the honest form of a gap: name it, and use the
platform's own meaning while it waits, not a stack of divs pretending.
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
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
Two more measured decisions, both the system's: the width is
`--sidebar-width` (the system's own rail measure, so the two flanks agree),
and the boundary is a `Separator` — `Surface variant="soft"` was inert here,
`oklch(0.9911 0 0)` against a page ground of the same lightness (F15, a
registered canon gap: soft does not bound a panel).
-->
{#snippet asideContent()}
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
<Flex align="stretch" height="100%">
<Separator orientation="vertical" decorative />
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
<Box width="var(--sidebar-width)" padding={4}>
<Stack gap={4}>
<Stack gap={1}>
<Text style="caption">Conversación</Text>
<Heading level={2} style="h4">Detalle</Heading>
</Stack>
<Separator decorative />
<dl>
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
<Stack gap={4}>
<Stack gap={1}>
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
<dt><Text style="label" color="muted">Estado</Text></dt>
<dd><Badge size="sm" color="affirm" variant="soft">Abierta</Badge></dd>
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
</Stack>
<Stack gap={1}>
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
<dt><Text style="label" color="muted">Asignada a</Text></dt>
<dd>
<Group gap={2} align="center">
<Avatar size="xs"><Avatar.Fallback>GH</Avatar.Fallback></Avatar>
<Text style="body" size="sm">Grace H.</Text>
</Group>
</dd>
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
</Stack>
<Stack gap={1}>
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
<dt><Text style="label" color="muted">Etiquetas</Text></dt>
<dd>
<Group gap={2} align="center">
<Badge size="sm" variant="soft">infra</Badge>
<Badge size="sm" variant="soft">v2</Badge>
</Group>
</dd>
</Stack>
<Stack gap={1}>
<dt><Text style="label" color="muted">Última actividad</Text></dt>
<dd><Text style="body" size="sm">hace 12 min</Text></dd>
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
</Stack>
</Stack>
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
</dl>
</Stack>
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
</Box>
</Flex>
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
{/snippet}
{#snippet pageFooter()}
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
<Stack gap={0}>
<Separator decorative />
<Box paddingX={4} paddingY={3}>
<Group gap={3} align="center" justify="space-between">
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
<Text style="caption">© 2026 Acme</Text>
<Text style="caption">v0.1</Text>
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
</Group>
</Box>
</Stack>
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
{/snippet}
<AppShell
{scroll}
bind:navOpen
nav={{ collapsible, mobileBreakpoint: 'md' }}
aside={{ breakpoint: 'lg' }}
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
navLabel="Proyecto Apollo"
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
asideLabel="Detalle de la conversación"
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
banner={banner ? notice : undefined}
navContent={rail ? navContent : undefined}
{header}
asideContent={aside ? asideContent : undefined}
footer={footer ? pageFooter : undefined}
>
<Box padding={5}>
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
<Stack gap={6}>
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
<!--
The page's own header: an `h1` at the theme's `h3` treatment. `level`
is the outline (this is the page's one h1); `style` is the theme's
register for chrome-sized titles — the display serif of `h1`/`h2`
belongs to a hero, not to a bandeja.
-->
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
<!--
The trail belongs to the PAGE, not to the application's bar: it
says where this view sits inside the context, and it changes with
every view while the bar does not. Atlassian, Toolpad and GitHub
all put it here for the same reason — a bar that carries the trail
grows a second row the moment the path is three deep, which is
what this demo measured before moving it.
-->
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
<Group gap={3} align="end" justify="space-between">
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
<Stack gap={2}>
<Breadcrumb size="sm">
<Breadcrumb.List>
<Breadcrumb.Item>
<Breadcrumb.Link href="#/projects">Proyectos</Breadcrumb.Link>
</Breadcrumb.Item>
<Breadcrumb.Separator />
<Breadcrumb.Item>
<Breadcrumb.Link href="#/apollo/board">Apollo</Breadcrumb.Link>
</Breadcrumb.Item>
<Breadcrumb.Separator />
<Breadcrumb.Item>
<Breadcrumb.Link current>Conversaciones</Breadcrumb.Link>
</Breadcrumb.Item>
</Breadcrumb.List>
</Breadcrumb>
<!-- No eyebrow: the trail above already says where this is, and
a caption repeating it is a line that earns nothing. -->
<Heading level={1} style="h3">Conversaciones</Heading>
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
</Stack>
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
<Toolbar aria-label="Vista" size="sm" variant="outline">
<Toolbar.Button>
<Icon.ListFilter size="sm" />
Filtrar
</Toolbar.Button>
<Toolbar.Button>
<Icon.ArrowUpDown size="sm" />
Ordenar
</Toolbar.Button>
</Toolbar>
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
</Group>
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
<!-- KPIs are `Metrics` — label / value / delta with the direction
decoupled from the valence: «sin asignar» FALLING is good. -->
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
<Grid columns={{ base: 1, sm: 3 }} gap={4}>
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
{#each kpis as kpi (kpi.label)}
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
<Card variant="outline">
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
<Metrics size="md">
<Metrics.Label>{kpi.label}</Metrics.Label>
<Metrics.Value value={kpi.value}>{kpi.value}</Metrics.Value>
<Metrics.Trend>
<Metrics.Delta trend={kpi.trend} goodTrend={kpi.goodTrend} value={kpi.delta} />
<Metrics.Description>{kpi.note}</Metrics.Description>
</Metrics.Trend>
</Metrics>
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
</Card>
{/each}
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
</Grid>
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
<!-- The inbox is a `Feed`: `role="feed"`, each conversation an
`article` with its position in the set, PageUp/PageDown between
them — the APG pattern, not a stack of rows. -->
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
<Stack gap={3}>
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
<Heading level={2} style="h4">Conversaciones</Heading>
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
<Card variant="outline">
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
<Feed
aria-label="Conversaciones de la bandeja"
totalItems={conversations.length}
size="md"
>
{#each conversations as c, i (c.id)}
<Feed.Article posinset={i + 1}>
<Group gap={3} align="start" justify="space-between">
<Group gap={3} align="start">
<Avatar size="sm"><Avatar.Fallback>{c.initials}</Avatar.Fallback></Avatar>
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
<Stack gap={1}>
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
<Feed.ArticleTitle level={3}>
<Text style="label">{c.from}</Text>
{#if c.unread}
<Badge size="xs" color="primary" variant="soft">nuevo</Badge>
{/if}
</Feed.ArticleTitle>
<Text style="body" size="sm">{c.subject}</Text>
<Feed.ArticleDescription>
<Text style="caption">{c.summary}</Text>
</Feed.ArticleDescription>
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
</Stack>
</Group>
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
<Text style="caption" numeric="tabular">{c.when}</Text>
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
</Group>
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
</Feed.Article>
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
{/each}
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
</Feed>
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
</Card>
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
</Stack>
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
<!-- Enough page to make the scroll model visible: with `scroll="main"`
the bar above stays while this moves; with `body` the whole document
goes and the bar pins. Also a `Feed`, because that is what an
activity stream is. -->
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
<Stack gap={3}>
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
<Heading level={2} style="h4">Actividad</Heading>
<Feed aria-label="Actividad reciente" totalItems={activity.length} size="sm">
{#each activity as a, i (a.id)}
<Feed.Article posinset={i + 1}>
<Card variant="soft">
<Group gap={3} align="start" justify="space-between">
<Stack gap={1}>
<Feed.ArticleTitle level={3}>
<Text style="label">{a.who}</Text>
</Feed.ArticleTitle>
<Feed.ArticleDescription>
<Text style="body" size="sm">{a.what}</Text>
</Feed.ArticleDescription>
</Stack>
<Text style="caption" numeric="tabular">{a.when}</Text>
</Group>
</Card>
</Feed.Article>
{/each}
</Feed>
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
</Stack>
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
</Stack>
</Box>
</AppShell>

Powered by TurnKey Linux.