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/docs/process/CONTINUE-blocks.md

12 KiB

CONTINUE — F2 blocks de sitio (handoff, act. 2026-07-31)

Estado: F1 CERRADA (8/8) · F2 12 de 15. Hechos: site-header · hero · feature-grid · feature-split · pricing · testimonials · faq · stats-band · cta · newsletter · site-footer · banner. Quedan 3: team (§F2.12) · contact (§F2.13) · content-section (§F2.14).

(El plan fijó 14; feature-split se añadió como block hermano con scope-approval, así que el tier tiene 15 y el denominador honesto es 15.)

Todo commiteado y pusheado a gita/alpha-0.1-sec-dom.

Dónde vive cada cosa:

  • Plan y bitácora por block: docs/process/PLAN-blocks.md (fichas §F2.x + registro cronológico al final, con el detalle de cada cierre).
  • Contrato del tier: docs/architecture/blocks.md (B-1..B-11, D-BLK).
  • Suelo de paridad: docs/process/RESEARCH-blocks-references.md (el dossier).
  • Deuda de canon congelada: docs/process/PLAN-blocks-quality.md §6.
  • Decisiones por block: el README de cada uno (src/uix/blocks/{kebab}/).

Lo siguiente: F2.12 team

Ficha en PLAN-blocks.md §F2.12: Section + AutoGrid + Avatar + Heading + Text + Group de IconButton sociales por miembro. Del dossier: 9 en Tailwind Plus. .Member se repite → parte compound (la regla de forma, abajo).

Fase 0 obligatoria: leer el README del Avatar (fallback de iniciales, tamaños, anillo) antes de componer, mirar el equivalente en ≥2 catálogos y anotar en el README del block qué se adopta y qué se descarta.

Después: contact → content-section.

F2.11 banner HECHO — el block más fino del tier a propósito: reenvía la superficie del Banner del canon con Omit<BannerProps, 'children'>, lo mete en columna, envuelve con Wrap y cablea Banner.Close solo si llega onDismiss (la visibilidad es del app, decisión del componente). Hallazgo real: Banner estampa role="banner" después de sus rest props, así que con el site-header en la misma página quedan dos landmarks banner; solo se puede paliar nombrándolos.

⚠️ Ahí reporté tres «defectos del canon» que eran FALSOS (aria-label de Banner.Close, type de Button, onclick de IconButton). Los tres eran la misma medición prematura — ver la regla de verificación afinada más abajo. Si vuelves a ver algo así, la primera hipótesis es el reloj, no el canon.


La regla de forma del tier (no la re-decidas)

Compound solo si las partes:

  • SE REPITEN — el app mapea sobre N (feature-grid.Item, testimonials.Item, pricing.Plan, faq.Item, stats-band.Stat, site-footer.Column), o
  • COORDINAN — se hablan por contexto (pricing.Switch ↔ PlanPrice).

Si no hacen ninguna de las dos: slots de snippet en la raíz (hero, cta, newsletter, y la marca / alta / social / legal / extra de site-footer). El plan original dibujaba varias de esas como partes; la desviación está registrada en cada README.


Los 8 hallazgos de canon que dejó el tier (congelados)

Restricción del usuario en vigor: no tocar componentes ni librerías fuera de src/uix/blocks/ y web/routes/blocks/. Están todos medidos en navegador y listados en PLAN-blocks-quality.md §6 (F15–F22) — más los de motion (A1, A2, A4, C7, C8, C10, D12, E14) de la pasada de calidad.

# Qué
F15 Surface variant='soft' no acota un panel: su track queda a 0.002 de luminancia del fondo de página en claro, y Surface no tiene borde. Card outline sí acota pero no acepta gradient → «panel sosegado con borde» no tiene primitivo.
F16 La ranura contrast de la paleta es blanca en todo escalón sólido, así que un lienzo de luminancia media deja el cuerpo bajo AA: primary 5.18 · indigo 5.21 · plum 4.75 pasan; neutral 3.32 · secondary/slate 3.30 · teal 3.07 fallan en claro.
F17 Text align es inerte por defecto: renderiza un span y text-align no hace nada sobre caja inline. Hay que pedir as="p".
F18 La fundación de eidos no trae reset de modelo de caja y lo asume del app. Bajo content-box, [data-field-control] (inline-size:100% + padding) mide 30px más que su contenedor. Resuelto en app-land: web/routes/blocks/_lib/reset.css.
F19 onValidSubmit/onInvalidSubmit son no-op silenciosos si se pasa un form ya construido: el componente solo los reenvía al createForm que hace él mismo. El handler va SIEMPRE en createForm.
F20 Los mensajes de SIUM son idlangref (#?sium.errors.email|…): resolver con uix.langs.t(issue.message, issue.params). La demo de docs del Form los parte a mano y por eso siempre salen en inglés.
F21 Los primitivos de layout no pueden cambiar de elemento: Text/Heading aceptan as, pero Box —y Stack/Flex/Grid/Group/Wrap/Container/Section— es un <div> fijo. Una columna de enlaces no puede ser ul/li.
F22 Un Select controlado muestra el VALOR crudo hasta abrirse una vez: las etiquetas las registran los Select.Item al montarse y el Content portaleado está cerrado. Rodeo: el child de Select.Value.

Y dos huecos anteriores que siguen abiertos: Box/Surface flex/grow no hacen crecer a un hijo flex (se rodea con tracks 1fr de Grid) y Group no apila (usa Flex direction={{ base: 'column', sm: 'row' }}; hero todavía compone sus acciones con Group).


La plantilla ya existe — cópiala, no la reinventes

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.
  • Verifica esperando la CONDICIÓN, nunca un timeout fijo. Con Form+Field+ SIUM el dev server tarda ~2,5s en hidratar y un screenshot temprano fotografía el panel todavía invisible (data-animation-pending puesto). Esperar a que ese atributo desaparezca es la diferencia entre verificar y reportar un bug que no existe.
  • Y la condición tiene que ser algo que escriba la RUNTIME del morfo, no el render. Los data-variant / data-size los pone el componente de eidos al renderizar, así que están desde el primer frame; type, aria-label y los handlers los aplica la runtime en un efecto POSTERIOR. Esperar a que exista el nodo (o a que se vea) no basta: a ~1s medí type ausente en TODOS los Button del árbol y ningún onclick disparando, y a ~6s los mismos botones tenían type="button", aria-label="Descartar" y el clic funcionando. Reporté tres «defectos del canon» que no existían. La espera correcta es waitForFunction(() => boton.hasAttribute('type')) — un atributo que solo puede haber puesto la runtime.
  • Playwright headless SÍ sirve para foco y teclado. El pane suspendido no (congela rAF y pierde activeElement), pero un page.keyboard.type() real dispara :placeholder-shown y :focus-within: así se verificó que la etiqueta flotante del Field sube al borde. page.fill() usa el setter nativo y NO los dispara. Un script del scratchpad debe importar file:///G:/dev/svelte/vicen/node_modules/playwright/index.mjs (no resuelve playwright por ruta relativa).
  • 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.
  • Un slot que se apaga se pasa como undefined, no se renderiza vacío. Declara el snippet arriba y pásalo por prop (signup={activo ? banda : undefined}): así el {#if} del block quita también su hueco. Y cuidado con el sombreado: {#snippet signup()} pisa un prop llamado signup dentro del componente.
  • 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.
  • Los números de layout se miden. Tres defaults de site-footer (container, la razón de la rejilla, el gap de columnas) salieron mal a la primera y solo el navegador lo dijo. Si un default decide cuántas cosas caben en una fila, se comprueba con el contenido real.

Deuda del arnés de demos (mía, sin tocar)

  • La galería (web/routes/blocks/+layout@.svelte) arranca UIX en línea en vez de usar _lib/BootUix.svelte, que es lo que usan los previews: la misma cadena escrita dos veces, y el motivo de que reset.css haya que importarlo en los dos sitios.
  • En BlockDemo, a ~1400px, el conmutador de anchos de dispositivo se solapa con el párrafo que lo precede. Idéntico en los 11 blocks → es del arnés, no de un block.

Deuda a decisión tuya

Siguen sin decidir, cada una entra por el contrato B con fase 0 ligera (mirar ≥2 catálogos del dossier y anotar adoptado/descartado en el README):

  1. Tabla de comparación de features×planes en pricing (recurre en las refs; sería un hermano pricing-table).
  2. Cita en spotlight en testimonials (la variante modal de TODAS las refs; el grid es minoría, 2 de 8 en TW).
  3. Lista estática 2/3 columnas en faq (6 de 7 en TW no son acordeón).
  4. form-in-hero (slot de alta al boletín dentro del hero).

Resueltas ya: feature-split/alternante (F2.3b, con Mockup) · hero con fondo cover (layout background) · CTA que navega y parece botón (el child de Button entrega un snippet content; Button sigue sin href y Link sigue poseyendo la navegación — doctrina en el README de eidos Button §«CTA que navega»).


Estado de gates al parar

  • npm run blocks:check verde (11 blocks).
  • svelte-check: sin errores propios (73 en el repo, todos ajenos: file-upload, etc.).
  • prettier: limpio en mis ficheros del tier.
  • vitest src/uix/contracts.test.ts: 3 fallos AJENOS (35 pasan) — escrituras DOM directas en soma, data-attrs hardcodeados y las claves camelCase de aura (components.aura.announce.returnedStopped, …). Ninguno apunta a blocks/.

Powered by TurnKey Linux.