F2.11. El block más FINO del tier a propósito: cuando el canon ya tiene la pieza,
el block es colocación y nada más. Reenvía la superficie entera del `Banner` con
`Omit<BannerProps, 'children'>` —sin re-declarar `intent`/`variant`/`size` ni
estrecharlos—, lo mete en columna con `Container` (`width="100%"` +
`paddingX={0}`: a sangre por fuera, en columna por dentro), envuelve la fila con
`Wrap` en vez de aplastarla, y cablea `Banner.Close` solo si llega `onDismiss`.
La visibilidad NO es suya: es la decisión que ya tomó el componente
(«composición, no un booleano `dismissible`»), así que el app envuelve en su
`{#if}`. Un block que guardara ese estado sería una segunda fuente de verdad.
Tampoco emite sema: el canon declara cero eventos para `Banner` a propósito, y un
aviso persistente sin descarte es tan legítimo como uno descartable.
La demo lo enseña **encima del `site-header` de verdad**, con página para
desplazarse. Un aviso dentro de un recuadro no se parece a un aviso.
## El hallazgo que la fase 0 anticipó
`Banner` estampa `role="banner"` DESPUÉS de sus rest props, así que no se puede
relajar a una región normal. Con el `site-header` en la misma página —que es la
única colocación que shipean las referencias— quedan **dos landmarks `banner`**.
Medido en la vista previa: dos. Lo único que puede hacer un app hoy es nombrarlos
con `aria-label`, y eso hace la demo.
## Y una lección de método que costó una sesión
Reporté tres «defectos del canon» —`aria-label` ausente en `Banner.Close`, `type`
desaparecido de todo `Button`, `IconButton` tragándose el `onclick`— y **los tres
eran falsos**. La causa era la misma: medir demasiado pronto.
Los `data-variant` / `data-size` los escribe el componente de eidos al renderizar,
así que están desde el primer frame; `type`, `aria-label` y los handlers los
aplica la **runtime del morfo en un efecto posterior**. A ~1s: `type` ausente en
3 de 3 botones y ningún clic disparando. A ~6s: `type="button"`,
`aria-label="Descartar"` y el descarte funcionando.
La espera válida es un atributo que solo pueda haber puesto la runtime
—`waitForFunction(() => boton.hasAttribute('type'))`—, no que el nodo exista ni
que se vea. La regla queda afinada en `CONTINUE-blocks.md`, que ya avisaba de
esperar la condición y no el reloj: fallé al elegir la condición.
De paso, un aviso de soma que sí era real y era mío: `Tabs.List` sin nombre
accesible en `BlockDemo.svelte`. Afectaba a las 12 demos del tier.
Verificado en navegador: los 4 intents, los 3 tratamientos, `descartable` sí/no
(con `no` el botón deja de renderizarse, no se oculta), claro/oscuro/RTL y 420px,
la tira siempre por encima de la cabecera, cero desbordamiento horizontal, el
descarte con la página recolocándose, y los controles de la demo moviendo la
vista previa en línea. Gates: `blocks:check` verde (12 blocks) · `svelte-check`
sin errores propios · `docs:check` sin errores míos · prettier limpio.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>