6.7 KiB
CONTINUE — F2 blocks de sitio (handoff, act. 2026-07-30)
Estado al parar: F1 CERRADA (8/8) y F2 9/14 — site-header · hero ·
feature-grid · feature-split · pricing · testimonials · faq · stats-band · cta. Encima de eso se dio
una pasada de calidad (PLAN-blocks-quality.md): movimiento en los 7 blocks,
vídeo, intents semánticos y el arco contact → commit; más tres arreglos de
canon (Grid align, herencia de --flex-*/--grid-*, archetype del
Accordion.Item) y dos primitivas nuevas (Mockup, Backdrop).
Una auditoría multi-agente posterior confirmó 32 hallazgos sobre esa pasada.
Lo que caía dentro del tier está corregido; la deuda fuera de blocks/ está
listada en PLAN-blocks-quality.md §6 y NO se ha tocado (restricción del
usuario: no tocar componentes ni librerías fuera de los blocks).
Todo commiteado y pusheado a gita/alpha-0.1-sec-dom.
Lo siguiente
F2.9 newsletter (ficha en PLAN-blocks.md §F2.9). Es el cta más un
formulario: Form + Field (email) + Button de submit. Lee el README de
Form antes de tocar nada — la validación la pone el sistema, el block no
inventa la suya, y el handler de submit es del app. Ojo al arco semántico: el
submit es commit con intent, no un contact como el clic del cta. Después:
site-footer → banner → team → contact → content-section.
F2.8 cta HECHO — slots de snippet, center + justified, un solo
Motion (el panel llega entero: un CTA es una sola afirmación). Dejó tres
hallazgos de canon medidos, no a ojo, que están en los gaps de su README y en
PLAN-blocks-quality.md §6: (1) Surface soft no acota panel — 0.002 L del
fondo en claro — y no tiene borde, así que se quitó el prop variant;
(2) la ranura contrast es blanca en todo escalón sólido, así que los lienzos
de luminancia media dejan el cuerpo por debajo de AA (neutral 3.32 · teal
3.07 vs primary 5.18); (3) Text align es inerte sobre el span por defecto
— hay que pedir as="p". Y Group no apila: las acciones van en Flex direction={{ base: 'column', sm: 'row' }}, cosa que hero aún no hace.
Deuda a decisión tuya
Feature-split / alternante — HECHO (F2.3b): se construyó como block
hermano, con Mockup para la media. La brecha nº1 del dossier queda cerrada.
Siguen sin decidir: tabla de comparación en pricing · cita en spotlight en
testimonials · lista estática 2/3 columnas en faq · form-in-hero.
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):
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
Carddesplazaba 21px un header conoffset: 0(su recipe pinta con--card-padding-*, quepadding={0}de la capa Box no alcanza), y unposition: stickydentro 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
previewen iframe, y es opt-in: en dev, dos documentos sin empaquetar a la vez agotan las conexiones del navegador (ERR_INSUFFICIENT_RESOURCESmata 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.
justifydeGroupse hereda a los clusters anidados (ponjustifyexplícito) y las deBoxYA 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)
CTA que navega y parece botón— RESUELTO 2026-07-23 por composición, no por prop nueva: elchild(asChild) deButtonentrega ahora un snippetcontent, así que<a href {...props}>{@render content()}</a>recibe la pintura sólida COMPLETA (icono · etiqueta · endIcon · spinner) y soma deja de estampartypeen un elemento que no es suyo.Buttonsigue sinhrefyLinksigue poseyendo la navegación. Doctrina: README de eidos Button §«CTA que navega». Úsalo tal cual en elhero(su CTA primario es exactamente esto).pricing,feature-gridtienen brechas del dossier §P1 que el plan v1 no cubre (tabla comparativa de precios, split/alternante texto-screenshot). Cada fase 0 las presenta como scope-approval, no se deciden solas. (La dehero—fondo cover— ya se decidió y está hecha: layoutbackground.)Box/Surfaceflex/growno crecen un hijo flex (encontrado enhero):<Surface flex={1}>se quedó a 0-width (flex: 0 1 auto, el--box-flexno surtió efecto); ningún componente shipped usa la prop. La demo usóGrid(tracks1fr). Revisar el cableado end-to-end de--box-flex/--box-grow.
Estado de gates al parar
npm run blocks:checkverde (8 blocks).svelte-checksin errores propios (la deuda restante es ajena).vitest src/uix/eidos: 3 fallos AJENOS —components/barcode/sin trackear (solo README) rompe el contrato de API de componentes. No es nuestro.contracts.test.ts: 3 fallos AJENOS conocidos (menubar DOM-write · radio-groupdata-ready· claves camelCase deaura).