docs(blocks): registro de F2.1, doctrina de demo del tier y handoff

Documentación al día de lo que se cerró hoy y handoff para retomar mañana.

- `PLAN-blocks.md` §7: **F2.1 `site-header` HECHA** con sus siete commits, la
  fase 0 contra el dossier §P1, el landmark que le faltaba a `NavigationMenu`
  (arreglado en el canon, no parcheado en el block), el hueco del CTA que
  navega y parece botón (registrado, no falseado) y el defecto de framework
  que destapó la demo.
- `theming/changelog.md` §46: las 44 variables de cascada de `Box` dejan de
  heredarse. Un `Section` regalaba su padding a cada descendiente —la galería
  arrastraba ~300px de aire desde F0— y los hijos heredaban anchos y `display`
  ajenos. `@property { inherits: false }`, radio verificado sin regresiones.
  Lección: una variable que un componente escribe para SÍ MISMO debe declararse
  `inherits: false`; si no, deja de ser un prop y se vuelve un contagio.
- `architecture/blocks.md` B-9: la demo de un block se construye sobre el
  harness compartido y el block se enseña A SANGRE — nunca dentro de un marco
  con relleno ni de una caja con scroll, porque eso cambia lo que el block
  hace.
- `src/uix/blocks/README.md`: anatomía de la demo (harness, `{Name}Site`, ruta
  `preview`, `DocRow`, catálogo único, ejes en el shell).
- `CONTINUE-blocks.md` (nuevo): handoff — qué toca (F2.2 `hero`), la plantilla
  de ficheros para copiar, las reglas que ya costaron sangre (a sangre, iframe
  solo para anchos de dispositivo, cada prop un control, nada de backticks en
  `<Text>`, ojo con las variables que heredan), la deuda declarada que es
  decisión del usuario y el estado exacto de los gates.

Gates al parar: `blocks:check` verde · `svelte-check` 73 errores, todos deuda
ajena (0 propios) · `vitest src/uix/eidos` 353/353 · `contracts.test` con los
3 fallos ajenos conocidos.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
alpha-0.1-sec-dom
dev 3 months ago
parent ae7b4f8c3b
commit d38bd63faa

@ -84,7 +84,7 @@ asserts its own detectors against inline fixtures on every run).
| B-6 | Responsive via the framework's mechanisms (responsive props of the layout components, canonical breakpoints). No `matchMedia`/listeners of its own — needing to observe something is the admission rule firing. |
| B-7 | A block owns NO visible string: all text arrives from the app as children/props. An unavoidable string is contract surface → the underlying canon component owns it (`texts:` + langs). |
| B-8 | Correct landmarks: sectioning element + `aria-label`/`aria-labelledby` where landmarks repeat; heading hierarchy coherent and documented in the block README (which level it emits, how to adjust). |
| B-9 | Every block ships `README.md` (**Function · Composition map** — which canon components, which props — **· Decisions · Gaps-with-disposition**) and a live demo page under `web/routes/blocks/{kebab}/`. |
| B-9 | Every block ships `README.md` (**Function · Composition map** — which canon components, which props — **· Decisions · Gaps-with-disposition**) and a live demo page under `web/routes/blocks/{kebab}/`, built on the shared demo shell (`web/routes/blocks/_lib/BlockDemo.svelte`): the block is shown FULL-BLEED on the page — never inside a padded frame or a scroll box, which would change what it does — with the device widths served by its own `preview` route. Anatomy in [`src/uix/blocks/README.md`](../../src/uix/blocks/README.md). |
| B-10 | A block does not import another block. Shared structure is either a canon component or a conscious duplication (recorded in Gaps). Declared exception: the shells (`app-shell`, `docs-shell`) compose F1 pieces and blocks by design — allow-listed in `blocks:check`. |
| B-11 | Motion only through the composed components' `motion` props/presets or `Cascade` for entrance choreography. No `@keyframes`/transitions of its own. |

@ -0,0 +1,82 @@
# CONTINUE — F2 blocks de sitio (handoff 2026-07-23)
Estado al parar: **F1 CERRADA (8/8 componentes canon)** y **F2.1 `site-header`
HECHA** — el primer block del tier, que además fijó la superficie de demo para
los 13 que quedan. Plan maestro: `docs/process/PLAN-blocks.md` (§F2 y el
registro §7). Todo commiteado y pusheado a `gita/alpha-0.1-sec-dom`; último
commit `ae7b4f8c3`.
## Lo siguiente
**F2.2 `hero`** (ficha en `PLAN-blocks.md` §F2.2). No depende de nada. Después,
el orden del plan: feature-grid → pricing → testimonials → faq → stats-band →
cta → newsletter → site-footer → banner → team → contact → content-section.
Cada uno entra por el contrato B (`docs/architecture/blocks.md`) con **fase 0
ligera obligatoria**: mirar el equivalente en ≥2 catálogos del dossier
(`docs/process/RESEARCH-blocks-references.md`) y anotar en el README del block
qué se adopta y qué se descarta.
## La plantilla ya existe — cópiala, no la reinventes
Un block terminado son estos ficheros (ejemplo real: `site-header`):
```text
src/uix/blocks/{kebab}/
├── README.md # Función · Mapa de composición · Decisiones · Gaps
├── index.ts # export del compound + tipos
├── types.ts # props (todo contenido entra por snippets, B-5/B-7)
└── {kebab}.svelte # composición: solo componentes del canon, sin CSS
web/routes/blocks/{kebab}/
├── +page.svelte # BlockDemo + controles vivos + pestañas de doc
├── {Name}Site.svelte # el block dentro de contenido REAL de producto
└── preview/
├── +layout@.svelte # `@` resetea el layout: la vista previa es su página
└── +page.svelte # sirve {Name}Site leyendo la URL
```
Y luego: marcar `shipped: true` en `web/routes/blocks/_lib/catalog.ts` (raíl y
galería leen esa única fuente) y `npm run blocks:check`.
## Reglas que ya costaron sangre (no las re-aprendas)
- **El block se enseña A SANGRE en la página.** Nada entre el block y el borde:
ni marco con relleno, ni caja con scroll, ni cromo pegajoso encima. Medido: un
`Card` desplazaba 21px un header con `offset: 0` (su recipe pinta con
`--card-padding-*`, que `padding={0}` de la capa Box no alcanza), y un
`position: sticky` dentro de un div con scroll es un comportamiento que nadie
vive. **Si un block se ancla a algo, se mide contra lo que se anclará en
producción.**
- **Los anchos de dispositivo (375/768) van por la ruta `preview` en iframe**, y
es opt-in: en dev, dos documentos sin empaquetar a la vez agotan las
conexiones del navegador (`ERR_INSUFFICIENT_RESOURCES` mata las DOS páginas).
- **Cada prop público, un control vivo** en la demo; los ejes de sección (tema,
idioma, dirección, densidad) ya los da el shell, no los repitas.
- **Nada de backticks de markdown dentro de `<Text>`**: se ven literales. Lo que
es código va en `<Code>`.
- **Ojo con las variables de layout que heredan.** `justify` de `Group` se
hereda a los clusters anidados (pon `justify` explícito) y las de `Box` YA no
heredan desde 2026-07-23 (`docs/theming/changelog.md` §46).
- **Un hueco del canon se registra, no se falsea.** Si al componer falta algo,
va a los Gaps del block como candidato a canon y la demo usa lo que hay.
## Deuda declarada (decisiones tuyas pendientes)
1. **CTA que navega y parece botón**: hoy no existe. `Button` no tiene `href`
por decisión propia («`Link` posee la navegación») y `Link` no tiene variante
prominente, así que el «Empezar gratis» de la cabecera es un enlace de texto.
Arreglo honesto = una decisión en el canon (`Link variant="solid"` o
`Button href`). Está en los Gaps de `site-header`.
2. **`hero`, `pricing`, `feature-grid`** tienen brechas ya anotadas en el
dossier §P1 que el plan v1 no cubre (split alternante texto/screenshot, tabla
comparativa de precios, cita en spotlight). Cada fase 0 las presentará como
scope-approval, no se decidirán solas.
## Estado de gates al parar
- `npm run blocks:check` verde (1 block).
- `npm run check` sin errores propios (la deuda restante es ajena).
- `vitest src/uix/eidos` 353/353.
- `contracts.test.ts`: 3 fallos AJENOS conocidos (menubar DOM-write ·
radio-group `data-ready` · claves camelCase de `aura`), de sesiones paralelas.

@ -1043,3 +1043,32 @@ Blocks diferidos: `scheduler` (bloqueado por `chronos`) · `logo-cloud`
anterior. Gates: audit PASS 0E/0W · eidos-lint 47 morfo-backed / 0 invalid ·
svelte-check 0 errores propios · vitest src/uix/eidos 353/353. **F1 = 8/8:
la fase queda CERRADA.** Siguiente: F2 (blocks de sitio, 14).
- 2026-07-23 — **F2.1 `site-header` HECHA · primer block del tier** (commits
`3cfc030b3` block+landmark · `016658a0c` controles · `f98c844ed` fix de Box ·
`50574806d` harness · `33328df7a` cromo de sección · `d574982ab` escenario a
ras · `ae7b4f8c3` vista previa honesta). Composición pura: `Sticky` (solo si
`sticky`, sin wrapper inútil) + `Container` + `Group` + `Box` (el interruptor
ancho/estrecho por `display` responsive, B-6) + `Drawer`; marca, nav,
acciones y navegación móvil entran como snippets del app (B-7: cero cadenas
propias). Fase 0 contra el dossier §P1: adoptado el FLYOUT (la brecha de
paridad) sin reimplementarlo — el app pasa un `NavigationMenu`.
**Encontrado al componer, arreglado en el canon**: `NavigationMenu` no emitía
landmark (su morfo declara `defaultElement: 'nav'` y soma pintaba un `div`).
**Hueco registrado, no falseado**: no existe un CTA que NAVEGUE y parezca
botón (`Button` no tiene `href` por decisión propia, `Link` no tiene variante
prominente) → candidato a canon en los Gaps del block.
**Defecto de framework destapado por la demo**: las 44 variables de cascada
de `Box` heredaban, así que un `Section` regalaba su padding a cada
descendiente (la galería arrastraba ~300px de aire desde F0) →
`@property { inherits: false }`, radio verificado sin regresiones
(`docs/theming/changelog.md` §46).
**Superficie de demo del tier (doctrina nueva, B-9 ampliada)**: harness
compartido en `web/routes/blocks/_lib/` + cromo de sección con los ejes que
mueven el framework (tema, idioma, dirección, densidad) + catálogo único.
El block se enseña **a sangre en la página**, nunca dentro de un marco con
relleno ni de una caja con scroll: medido, un `Card` desplazaba 21px un
header con `offset: 0`, y un `sticky` dentro de un div con scroll es un
comportamiento que nadie vive. Los anchos de dispositivo se sirven desde una
ruta `preview` propia (iframe opt-in: en dev, dos documentos sin empaquetar
agotan las conexiones del navegador).
**Siguiente**: F2.2 `hero` — handoff en `docs/process/CONTINUE-blocks.md`.

@ -1788,6 +1788,35 @@ migración base→seeds**. Doctrina standing actualizada: [`reference.md §40`](
---
**Última revisión**: 2026-07-20 (§45 contraste — Stage 2 cerrado). Si algo en
## 46. Las variables de cascada de `Box` dejan de heredarse (2026-07-23)
**Incidente**: cualquier página construida con las primitivas de layout crecía
cientos de píxeles de aire muerto, y los hijos heredaban anchos y `display`
ajenos (en la demo de blocks, un `<header>` acabó midiendo 32×435 px).
**Causa**: el recipe de `Box` resuelve cada propiedad del modelo de caja con
`var(--box-…, revert-layer)`, y **una custom property hereda por defecto**. Un
`Section` —que es un Box con 64px de padding de bloque— se lo regalaba a TODOS
sus descendientes: `Container`, `Stack`, `Group`, `Card`… Cinco componentes de
layout anidados = cinco veces el padding.
**Corrección**: las 44 variables se registran en `box.css` con
`@property { syntax: '*'; inherits: false }` y sin valor inicial. Así cada una
queda *garantizada-inválida* salvo que la ponga el propio elemento, y las
cadenas `var(--box-…, revert-layer)` resuelven lo que su autor escribió: los
props de ESE Box y, si no, el cascade normal.
**Radio verificado**: `vitest src/uix/eidos` 353/353 · barrido por la galería de
blocks y las demos de button, card, sidebar, nav-tree y table (cero errores de
consola, cero desbordes, alturas sanas) · el CSS generado no cambia — la
corrección vive en el recipe.
**Lección**: una variable de cascada que un componente escribe para SÍ MISMO
tiene que declararse `inherits: false`. Si no, deja de ser un prop y se
convierte en un contagio.
---
**Última revisión**: 2026-07-23 (§46 herencia de las variables de `Box`). Si algo en
este doc no coincide con el código, el código gana — pero abre un issue para que
actualicemos el doc.

@ -22,6 +22,37 @@ src/uix/blocks/{kebab}/
# layout components, no .css file (B contract)
```
## Anatomy of a block DEMO (B-9, second half)
Every block also ships a demo under `web/routes/blocks/{kebab}/`, and they all
have the same shape — written once in `web/routes/blocks/_lib/`:
```text
web/routes/blocks/{kebab}/
├── +page.svelte # the demo: BlockDemo + live controls + doc tabs
├── {Name}Site.svelte # the block inside a page's worth of REAL content
└── preview/
├── +layout@.svelte # `@` resets the layout: the preview is its own page
└── +page.svelte # serves {Name}Site from URL params
```
- `_lib/BlockDemo.svelte` — identity (name · function · fact chips) → the block
**full-bleed on the page** → controls → tabs (Composición · API · A11y ·
Gaps · Notas).
- **The block is never framed.** A stage with padding, a scroll box or a sticky
chrome above it changes what the block does: a header pinned at `offset: 0`
measured 21px off inside a `Card`, and a `position: sticky` inside a scrolling
div is a behaviour nobody experiences. If a block anchors to something, it is
measured against what it will anchor to in production.
- **Device widths (375 / 768) mount the `preview` route in an iframe**, because
a narrow viewport can only be shown by a real document. Opt-in: in dev, two
unbundled documents at once exhaust the browser's connections.
- `_lib/DocRow.svelte` — the two-column row the doc tabs use (the canon `Table`
is a DATA table; it wants a `createTable` instance).
- `_lib/catalog.ts` — the single list the section rail and the gallery read.
- The section's axes (colour mode, language, direction, density) live in the
shell (`web/routes/blocks/+layout@.svelte`), so every demo inherits them.
## Block README template (B-9)
```markdown

@ -44,6 +44,15 @@ Navbars 11 + Flyout 7» · Untitled UI · Flowbite):
this block; skipping the `Sticky` wrapper keeps the DOM honest (no wrapper
that does nothing).
## Demo
`web/routes/blocks/site-header/` — el block **a sangre en la página** (su
`position: sticky` se pega al viewport de verdad; enmarcarlo en una caja con
scroll enseñaría un comportamiento que nadie vive), con control vivo de los
cinco props, y los anchos de dispositivo servidos desde `preview/` como
documento propio. El mini-sitio vive en `SiteHeaderSite.svelte` y lo comparten
las dos superficies.
## Gaps
| Gap | Disposition |

Loading…
Cancel
Save

Powered by TurnKey Linux.