docs(blocks): la ficha del shell cabía en cinco líneas porque no miraba el ecosistema

Fase 0 de F3.1 `app-shell`, firmada. La ficha original se escribió antes de que
existieran el canon `Sidebar` y la capa `affix`, y el autor preguntó lo que
había que preguntar: si su alcance tenía delante `active-app` y `active-uix`
enteros. No los tenía.

Las cuatro decisiones que se firman, y lo que las obligó:

- **Q1 — el block NO cablea servicios.** La integración del ecosistema se
  demuestra en una app de referencia en app-land, no dentro del block. Un shell
  que leyera `App.session` para dibujar el menú de usuario sería una raíz de
  composición disfrazada: funcionaría, y metería el arranque del ecosistema
  dentro de una pieza que ha de poder caer en cualquier app.
- **Q2 — dos modelos de scroll por prop.** `main` (rejilla 100dvh, sin fixed,
  sin números) es lo que cierra A-95 por construcción; `body` es lo que
  `docs-shell` necesitará para anclas y TOC pegada.
- **Q3 — cabecera dentro del inset.** El provider del `Sidebar` ES la fila
  flex, así que su `Trigger` sólo vive dentro. Una cabecera a todo lo ancho
  exigiría partir ese provider: queda como gap de canon para v2.
- **Q4 — canon `skip-link` + uno por región montada** (técnica G124).

De paso, tres cosas que estaban mal escritas y ahora lo dicen:

- El dossier afirmaba que **ninguna referencia trae skip-link** y que era
  superación nuestra. Es falso: Polaris lo trae en `Frame` y Atlassian genera
  un menú entero. La superación real es generarlos del mismo contrato que
  estampa el landmark. Y el icon-rail no es de Mantine (su `collapsed` es
  booleano); es de shadcn, AntD, Toolpad y Atlassian. El dossier tampoco miró
  nunca el `navigation-system` actual de Atlassian, y el `page-layout` que sí
  miró está deprecado.
- **D-BLK.6 ha derivado**: la celda dice «ni prefs ni langs ni eidos» y la
  doctrina vigente (`blocks.md` §Services) sancionó dos servicios en julio y
  agosto. Queda enmendada con lo que sigue siendo cierto: ningún block CREA ni
  cablea servicios.
- **E-5 está desfasada**: `Command.Dialog` ya trae el atajo (`shortcut`,
  `mod+k` por defecto). El listener a mano de F4.1 no hay que escribirlo.
- **F3.2 `auth`**: la ficha dice cuatro vistas y el dominio tiene seis. Falta
  `update-password`, que no es un lujo — es donde aterriza el enlace de
  recuperación, y sin ella el camino de `recover` no termina.

`blocks.md` gana un párrafo «Shells» bajo §Conventions: qué poseen (geometría,
landmarks, alturas) y dónde está la línea (la raíz de composición es de la app).

Verificado en código antes de escribirlo: `Sidebar.Inset` acepta `child`, `Box`
expone minHeight/position/overflow y `Grid` templateRows —la rejilla del shell
no necesita CSS propio—, y `Sticky` expone `root`.

docs:check 0 errores, 0 avisos (635 docs).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
alpha-0.1-background
dev 2 months ago
parent 9fd54c80fa
commit f3bf6a0299

@ -151,6 +151,24 @@ asserts its own detectors against inline fixtures on every run).
announcer on the page. Anything beyond these two is the app's, or the
admission rule firing.
- **Shells (`app-shell`, `docs-shell`) — geometría, no cableado.** A shell is
the block that owns a page's REGIONS: their landmarks, their skip links and
their heights. That last one is why the tier needs it at all — an affixed
notice and a pinned header fight for the same edge, and no component can
arbitrate because none of them owns the page (ledger A-95). What a shell owns:
the grid, the scroll model, the landmark set, and the state that its own
regions share (a collapsed nav, a folded aside). What a shell does NOT own,
and this is the line that matters: **the composition root**. Creating
`ActiveApp`, attaching `ActiveUix`, mounting `<Uix>`, wiring `modeSource` /
`densitySource` into `ActiveEidos`, projecting prefs onto the document,
persisting them — all of that is the application's, exactly as it is for every
other block (§Services above; only composition roots create services). A shell
that read `App.session` to draw a user menu would be a composition root
wearing a block's clothes: it would work, and it would put the ecosystem's
boot inside a piece meant to be droppable into any app. The seam is the same
one every block uses — snippets and props — and the proof that it suffices is
an application built on it, not a paragraph.
- **Prop naming — `size` es del BLOCK, nunca de una pieza interna**: un block que
reenvía el eje de tamaño de algo que envuelve lo nombra **`{pieza}Size`**
(`containerSize` para la medida del `Container`, `sectionSize` para el aire del

@ -84,15 +84,15 @@ quedó enmendada** respecto a la propuesta original del plan (que proponía
`src/blocks/`); el resto se firmó tal como estaba propuesto. D-BLK.3/4/5
derivan de doctrina ya vigente y se firmaron por no-objeción.
| # | Decisión | FIRMADO |
| ----------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **D-BLK.1** | Ubicación y alias | **`src/uix/blocks/`** + alias `$blocks` (decisión de usuario: el tier es UI y vive junto a las capas). La prueba de encapsulación se conserva íntegra: borrar `src/uix/blocks/` deja `npm run check` verde y NADA del canon (`morfo/soma/sema/eidos/active-uix/langs`) lo importa — blocks es un tier bajo `src/uix/`, no una quinta capa. |
| **D-BLK.2** | Estilos | **Layout-components-first**: el layout se hace componiendo `Container/Section/Stack/Flex/Grid/AutoGrid/Wrap/Group/Separator/AspectRatio/Surface` y sus props. Un block NO trae `.css` propio; un `<style>` scoped puntual exige justificación en su README y nunca selecciona internals de componentes compuestos. |
| **D-BLK.3** | API | Componente compuesto con partes anidadas: `<SiteHeader>` / `<SiteHeader.Nav>` / `<SiteHeader.Actions>`; contenido SIEMPRE por children, nunca árboles de datos (`items={...}` solo donde el componente canónico compuesto ya es data-driven). (Derivada de la regla compositional-not-data-driven ya vigente.) |
| **D-BLK.4** | Demos | `web/routes/blocks/{kebab}/+page.svelte` + galería índice en `web/routes/blocks/`. (La ruta `web/routes/alpha/` sigue TERMINADA y prohibida — no tocarla.) |
| **D-BLK.5** | Idiomas/strings | Un block no posee NINGÚN string visible: todo texto llega del app como children/props. Si un string parece inevitable, es superficie de contrato → lo posee el componente canónico subyacente (vía `texts:` del morfo + langs). (Consecuencia mecánica de B-1: sin morfo no hay `texts:`.) |
| **D-BLK.6** | Servicios | **v1 sin servicios**: los blocks NO consumen `uix.prefs`/langs/eidos directamente; el cableado (tema/idioma, submit de auth, transporte) llega como handlers/props del app. Revisable si ≥2 blocks demuestran necesidad real (misma vara que la 2-de-3). |
| **D-BLK.7** | Naming F1 | `sticky` · `anchor-nav` · `empty-state` · `result` · `callout` · `prose` · `sidebar` — confirmados. (Los matices de naming siguen revisables en la fase 0 de cada uno, como toda fase 0.) |
| # | Decisión | FIRMADO |
| ----------- | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **D-BLK.1** | Ubicación y alias | **`src/uix/blocks/`** + alias `$blocks` (decisión de usuario: el tier es UI y vive junto a las capas). La prueba de encapsulación se conserva íntegra: borrar `src/uix/blocks/` deja `npm run check` verde y NADA del canon (`morfo/soma/sema/eidos/active-uix/langs`) lo importa — blocks es un tier bajo `src/uix/`, no una quinta capa. |
| **D-BLK.2** | Estilos | **Layout-components-first**: el layout se hace componiendo `Container/Section/Stack/Flex/Grid/AutoGrid/Wrap/Group/Separator/AspectRatio/Surface` y sus props. Un block NO trae `.css` propio; un `<style>` scoped puntual exige justificación en su README y nunca selecciona internals de componentes compuestos. |
| **D-BLK.3** | API | Componente compuesto con partes anidadas: `<SiteHeader>` / `<SiteHeader.Nav>` / `<SiteHeader.Actions>`; contenido SIEMPRE por children, nunca árboles de datos (`items={...}` solo donde el componente canónico compuesto ya es data-driven). (Derivada de la regla compositional-not-data-driven ya vigente.) |
| **D-BLK.4** | Demos | `web/routes/blocks/{kebab}/+page.svelte` + galería índice en `web/routes/blocks/`. (La ruta `web/routes/alpha/` sigue TERMINADA y prohibida — no tocarla.) |
| **D-BLK.5** | Idiomas/strings | Un block no posee NINGÚN string visible: todo texto llega del app como children/props. Si un string parece inevitable, es superficie de contrato → lo posee el componente canónico subyacente (vía `texts:` del morfo + langs). (Consecuencia mecánica de B-1: sin morfo no hay `texts:`.) |
| **D-BLK.6** | Servicios | **v1 sin servicios**: los blocks NO consumen `uix.prefs`/langs/eidos directamente; el cableado (tema/idioma, submit de auth, transporte) llega como handlers/props del app. Revisable si ≥2 blocks demuestran necesidad real (misma vara que la 2-de-3). **ENMENDADA por la práctica — la doctrina vigente es `architecture/blocks.md` §Services, no esta celda**: el traductor entró el 2026-07-31 (un block posee las palabras de SUS estados) y el anunciador el 2026-08-06 (poseerlas sin poder decirlas es medio trabajo), los dos por `ActiveEidos.require()`; y el breakpoint del sistema (`dom.isAtLeast`) es consumo obligado por B-6, que prohíbe el `matchMedia` propio — `site-header` lo hace desde su construcción. Lo que la celda sigue diciendo bien, y no se ha movido: **ningún block crea servicios ni los cablea por su cuenta** (tema, sesión, permisos, transporte, persistencia). Reafirmado en la fase 0 de F3.1 (2026-08-18) contra la tentación más fuerte que ha tenido el tier: un shell que dibujara el menú de usuario leyendo `App.session`. |
| **D-BLK.7** | Naming F1 | `sticky` · `anchor-nav` · `empty-state` · `result` · `callout` · `prose` · `sidebar` — confirmados. (Los matices de naming siguen revisables en la fase 0 de cada uno, como toda fase 0.) |
Cualquier enmienda futura a una D-BLK se registra aquí con fecha ANTES de
seguir construyendo (regla dura: los desvíos de alcance se declaran, nunca en
@ -1112,19 +1112,124 @@ que son las dos con fase 0 OBLIGADA y más profunda (leer `soma/components/table
- `$libs/datagrid` y `soma/components/drag-drop` antes de diseñar el API).
### F3.1 `app-shell`
- **Función**: esqueleto de aplicación: sidebar + topbar + contenido (+ aside
opcional).
- **Compone**: `Sidebar` (F1.7) + `Sticky` + `Container`/`Grid` +
`ScrollArea` + `Group` + slots.
- **API**: `<AppShell>` + `.Sidebar` + `.Topbar` + `.Content` + `.Aside`.
- **Landmark**: `header/nav/main/aside` + skip-link (según decisión F2.1);
jerarquía documentada.
- **v1**: sidebar colapsable + topbar afijada + main con scroll propio;
móvil = sidebar en drawer (lo trae F1.7).
- **Excepción B-10 declarada**: los shells componen otros elementos de F1 y
blocks — allowlist en `blocks-check`.
### F3.1 `app-shell` — fase 0 FIRMADA 2026-08-18
> La ficha original (cinco líneas) se escribió antes de que existieran el canon
> `Sidebar` y la capa `affix`, y no miraba el ecosistema. Ésta la sustituye. Lo
> que la motivó: el autor preguntó si el alcance del shell tenía delante
> `active-app` y `active-uix` enteros. No los tenía.
- **Función**: la geometría de una página de aplicación — el esqueleto que
POSEE las regiones, sus landmarks y sus alturas, y por tanto el único sitio
donde el aviso, la cabecera pegada y el contenido dejan de pelearse.
- **Compone**: `Sidebar` (F1.7, que ya trae `<aside>`+`<nav>`+`<main>`, el
drawer móvil y los tres modos de colapso) + `Box`/`Grid` para la rejilla +
`Sticky` (con `root`, el scroll-host) + el nuevo canon `SkipLink` + el
`Container` de la medida. **NO** `ScrollArea` en v1: el scroll del main es
`overflow` de una caja, y montar barras propias sobre la región que scrollea
toda la app es una decisión visual que nadie ha pedido (Gap).
- **API**:
```svelte
<AppShell scroll="main|body" nav={{ collapsible, side, mobileBreakpoint }}
bind:navOpen aside={{ breakpoint }} bind:asideOpen>
<AppShell.Banner> <!-- aviso EN FLUJO, altura intrínseca -->
<AppShell.Nav> <!-- compone Sidebar; la app rellena las filas -->
<AppShell.Header> <!-- <header> DENTRO del inset (decisión Q3) -->
<AppShell.Main> <!-- <main id> — destino del skip-link -->
<AppShell.Aside> <!-- <aside aria-label>, plegable por breakpoint -->
<AppShell.Footer> <!-- <footer>, opcional -->
</AppShell>
```
- **Landmark**: 1 `banner` (`<header>`) · 1 `main` · el `navigation` del
`Sidebar` · `complementary` ×2 NOMBRADOS (panel del Sidebar + Aside) · 1
`contentinfo`. Sin heading propio: un shell no es una sección.
- **v1**: los dos modelos de scroll, el colapso del nav por las costuras del
`Sidebar`, el aside plegable, y los skip-links generados de las regiones
MONTADAS.
- **Excepción B-10 declarada**: `SHELL_ALLOWLIST` ya existe en
`blocks-check.ts:44` y está probada por fixture — el shell puede componer
`banner`, `site-footer`… sin tocar el guard.
#### Las cuatro decisiones firmadas (2026-08-18)
| # | Decisión | Firmado | Por qué |
| ------ | ------------------------- | ----------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Q1** | Frontera de servicios | **el block NO cablea servicios** | La doctrina queda intacta (`blocks.md` §Services + D-BLK.6). La integración del ecosistema se demuestra donde de verdad vive: en una **app de referencia** en app-land (abajo). Un block que leyera `App.session` sería una raíz de composición disfrazada. |
| **Q2** | Modelo de scroll | **`scroll="main"` y `scroll="body"`, por prop** | `main` (rejilla `100dvh`, sin `fixed`, sin números) es lo que cierra **A-95** por construcción; `body` es lo que `docs-shell` (F4.1) necesita para anclas, TOC pegada y la barra de URL móvil. Es el eje que Mantine expone como `mode` y Atlassian como `isFixed` por slot. |
| **Q3** | Colocación de la cabecera | **`alt`: `<header>` dentro del inset** | El provider del `Sidebar` **ES** la fila flex (`soma/components/sidebar/components/sidebar.svelte` renderiza el `<div data-sidebar>`), así que el `Trigger` sólo vive dentro de ella. Una cabecera a todo lo ancho POR ENCIMA del nav exigiría partir ese provider — cambio de canon que v1 no paga. Es lo que hacen shadcn y Mantine `layout='alt'`. |
| **Q4** | Skip-links | **canon `SkipLink` + uno por región montada** | Técnica **G124** de la WCAG 2.4.1 («links at the top of the page to each area of the content»), que es lo que Atlassian genera. Se emiten de las regiones que EXISTEN, no de una lista escrita: el mismo contrato que estampa el landmark estampa su enlace. |
Consecuencias registradas: el eje `layout` del `Sidebar` (provider ≠ fila)
queda como **gap de canon para v2**, y lo pedirá `docs-shell` si quiere topbar
a todo el ancho. `ScrollArea` sobre el main queda como Gap del block.
#### Comprobado en código antes de escribir esto (2026-08-18)
- `Sidebar.Inset` acepta **`child`** — el shell lo renderiza como caja y mete
dentro `<header>` + `<main>`, así que el `<main>` no acaba envolviendo a la
cabecera. Sin esto, Q3 no era ejecutable.
- `Box` expone `minHeight` / `position` / `overflow` (`LayoutLengthValue =
number | string`, así que `100dvh` pasa crudo) y `Grid` expone
`templateRows` / `templateColumns`: **la rejilla del shell no necesita CSS
propio** — D-BLK.2 se cumple sin excepción, como en `hero`.
- `Sticky` expone `root` (el scroll-host): bajo `scroll="main"` la cabecera se
pega contra el main, no contra el viewport.
- ⚠️ `Sticky.offset` y `AnchorNav.topOffset` son **números px**. Bajo
`scroll="body"` un raíl que deba anclarse a la altura de la cabecera necesita
ese número; el shell publica la altura como token, y **quien la consuma en px
es F4.1** — allí se decide si `offset` gana longitudes CSS (Gap ya declarado
en el README de `sticky`).
#### El suelo de referencia (contrastado 2026-08-18, fuentes leídas)
Mantine `AppShell` · shadcn `Sidebar`+blocks · AntD `Layout`/`ProLayout` ·
Polaris `Frame`/`TopBar` · Atlassian (`page-layout` DEPRECADO +
`navigation-system` actual) · Toolpad `DashboardLayout` · Tailwind Plus
(Stacked 9 · Sidebar 8 · Multi-Column 6).
- **Suelo (≥3 refs)**: propiedad de la altura de cabecera · colapso
offcanvas/icono/none · drawer móvil · breakpoint como prop · aside/panel
derecho · padding/inset · layouts alt/multi-columna · landmarks por
construcción · RTL. De esos, **el `Sidebar` ya nos da cuatro**; el shell debe
los otros cinco.
- **Suelo-2**: skip-links · persistencia · atajo · ranura de ribbon ·
**el modelo de scroll como API** · geometría como CSS vars (Mantine 8
`--app-shell-*`; Atlassian 7 con fallback).
- **Superación posible**: los landmarks y sus skip-links salen del MISMO
contrato (nadie lo hace: Polaris pide un `ref`, Atlassian pide `id` +
`skipLinkTitle` a mano); dos `complementary` nombrados; RTL lógico; tokens,
densidad y modo de serie.
- ⚠️ **Riesgo, y hay que mirarlo de frente**: **Skeleton RETIRÓ su `AppShell`
en v3** — «the number of issues introduced by it far outweigh its potential
gains» (a11y en navegadores móviles con `100vh`, historia de sticky header
confusa). Por eso `scroll` es explícito, la altura va en `100dvh` y en móvil
no se fuerza el scroll propio del main.
#### Correcciones al dossier que esta fase 0 obliga
El dossier (`RESEARCH-blocks-references.md`) se corrige en el mismo pase: su
fila de `app-shell` afirmaba que **ninguna referencia trae skip-link** (Polaris
y Atlassian sí) y atribuía el modo icon-rail a Mantine (no lo tiene). Detalle y
fecha, en el propio dossier.
#### La app de referencia (donde SÍ se integra el ecosistema)
`web/routes/blocks/app/`, publicada en el catálogo con `kind: 'page'` como la
landing. Es el «Cierre F3» adelantado y, sobre todo, **la primera raíz de
composición en modo attach del repo**: `createActiveApp` → `attachActiveUix` →
`<Uix>` → `Soma.create()` → `ActiveEidos.create({ modeSource, densitySource })`
→ `createActivePrefsDomProjection` (en attach NO se proyecta sola) →
`createPrefsStorageBridge` → `setActiveApp` · `setBus` · `setPermsContext` ·
`applyStandardOrca`.
Hoy **ningún** shell del repo hace nada de eso: los siete arrancan
`createActiveUix` standalone, ninguno usa el canon `Sidebar`, ninguno tiene
skip-link, y cada uno reescribe a mano su `localStorage` y su `Set<listener>`
de modo. La app de referencia existe para que el cableado se escriba UNA vez y
para que cada punto que duela quede documentado como candidato (un helper de
fuentes visuales, una guía de raíz de app), nunca resuelto en silencio.
### F3.2 `auth`
@ -1141,6 +1246,16 @@ que son las dos con fase 0 OBLIGADA y más profunda (leer `soma/components/table
block no conoce transporte ni credenciales. La validación es del sistema
`Form` con schemas del app.
- **Demo**: los 4 kinds, estados invalid/submitting, claro/oscuro.
- **⚠️ Abierto para su fase 0 (anotado 2026-08-18)**: la ficha dice CUATRO
vistas y el dominio tiene SEIS. El `ViewType` de Supabase —la enumeración
más limpia del oficio— es `sign_in · sign_up · magic_link ·
forgotten_password · update_password · verify_otp`: nos faltan **magic-link**
y **update-password**, y esta última no es un lujo (es la pantalla a la que
aterriza el enlace de recuperación; sin ella el camino de `recover` no
termina). Se decide en su fase 0, no aquí. Dos hilos de canon la esperan y
hay que mirarlos antes: `Form`/SIUM con `progressive` no expone mensajes en
un envío inválido, y el provider de `ProofOfHuman` escribe su propio
`status`, así que el veredicto del app no se sostiene.
### F3.3 `data-table`
@ -1253,7 +1368,12 @@ del sitio); estos blocks son su vehículo de UI.
(`Group` de dos `Card`/`Link`).
- **E-5 firmada**: el atajo ⌘K del trigger de búsqueda = listener app-land
documentado en el README del block; el art `shortcuts` conserva su
disparador F5 (≥2 consumidores reales).
disparador F5 (≥2 consumidores reales). ⚠️ **DESFASADA (visto en la fase 0
de F3.1, 2026-08-18)**: `Command.Dialog` **ya trae el atajo**
(`shortcut`, por defecto `'mod+k'`, con `open` bindable y `onOpenChange`;
`eidos/components/command/types.ts:41-52,96-99`). El listener a mano no hay
que escribirlo: se pasa el prop, o se pone a `null` para apagarlo. Lo que
sigue vigente de E-5 es lo otro: el art `shortcuts` no nace por esto.
- **API**: `<DocsShell>` + `.Nav` + `.Article` (prose) + `.Toc` + `.Search`
- `.PrevNext`.
- **v1**: 3 columnas desktop → TOC colapsada y sidebar-drawer en móvil.

@ -2,8 +2,8 @@
> Proceso, no doctrina. Alimenta las fases 0 de `PLAN-blocks.md`: cada fiche
> de F1/F2/F3/F4 verifica contra este dossier en vez de investigar de cero.
> Encargo del usuario (2026-07-21): *"estar al menos a la par y cuanto menos
> superarlo"*. Método: 6 pistas de investigación web en paralelo sobre
> Encargo del usuario (2026-07-21): _"estar al menos a la par y cuanto menos
> superarlo"_. Método: 6 pistas de investigación web en paralelo sobre
> fuentes oficiales vivas, cada una contrastando el plan contra el panorama
> real y devolviendo brechas + ángulos de superación.
@ -24,14 +24,14 @@ en `PLAN-blocks.md` §Registro).
## Pistas
| # | Pista | Cubre | Estado |
|---|---|---|---|
| P1 | Catálogos de blocks de marketing | shadcn blocks · Tailwind Plus · Flowbite · PrimeBlocks · Relume · Untitled UI · daisyUI → F2 | lanzada |
| P2 | Blocks de aplicación + app-shell | Tailwind Plus App UI · shadcn dashboard/sidebar/login · Mantine AppShell · Tremor · AntD ProComponents · Refine · auth UIs → F3 | lanzada |
| P3 | Refs F1 (5 ligeros) | affix/anchor/empty/result/callout en AntD · Mantine · Radix Themes · Chakra · Polaris · GitHub/Docusaurus admonitions → F1.1–F1.4, F1.6 | lanzada |
| P4 | Refs F1 (2 pesados) | prose (@tailwindcss/typography · Mantine · Primer) · sidebar (shadcn EXHAUSTIVO · AntD Sider · Mantine) → F1.5, F1.7 | lanzada |
| P5 | Docs shells + docs blocks | Starlight · Fumadocs · Docusaurus · VitePress · Nextra · Mintlify · showcase/props-tables → F4 | lanzada |
| P6 | Ecosistema Svelte competidor | shadcn-svelte · Bits · Melt · Skeleton · Flowbite-Svelte · svelte-ux · nuevos Svelte-5 → el listón "mejor de Svelte" | lanzada |
| # | Pista | Cubre | Estado |
| --- | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| P1 | Catálogos de blocks de marketing | shadcn blocks · Tailwind Plus · Flowbite · PrimeBlocks · Relume · Untitled UI · daisyUI → F2 | lanzada |
| P2 | Blocks de aplicación + app-shell | Tailwind Plus App UI · shadcn dashboard/sidebar/login · Mantine AppShell · Tremor · AntD ProComponents · Refine · auth UIs → F3 | lanzada |
| P3 | Refs F1 (5 ligeros) | affix/anchor/empty/result/callout en AntD · Mantine · Radix Themes · Chakra · Polaris · GitHub/Docusaurus admonitions → F1.1–F1.4, F1.6 | lanzada |
| P4 | Refs F1 (2 pesados) | prose (@tailwindcss/typography · Mantine · Primer) · sidebar (shadcn EXHAUSTIVO · AntD Sider · Mantine) → F1.5, F1.7 | lanzada |
| P5 | Docs shells + docs blocks | Starlight · Fumadocs · Docusaurus · VitePress · Nextra · Mintlify · showcase/props-tables → F4 | lanzada |
| P6 | Ecosistema Svelte competidor | shadcn-svelte · Bits · Melt · Skeleton · Flowbite-Svelte · svelte-ux · nuevos Svelte-5 → el listón "mejor de Svelte" | lanzada |
## Hallazgos
@ -49,19 +49,19 @@ en `PLAN-blocks.md` §Registro).
#### Suelo por bloque F2 (brechas concretas de nuestro v1)
| Block | Suelo/recurrencias que nos faltan |
|---|---|
| hero | El esqueleto center\|split es correcto; la brecha es **tratamiento de media**, no conteo de layouts: presets de media (screenshot enmarcado / phone mockup / background-image cover), slot **form-in-hero** (email sign-up), **logo-strip** bajo las acciones, video embed. |
| feature-grid | **LA brecha #1 de F2**: el **split/alternante texto-vs-screenshot con lista de features** lo shippean las 5 refs y es la sección nº1 tras el hero; nuestro v1 solo tiene el grid de iconos. |
| testimonials | Falta la **cita única en spotlight** (logo + quote + avatar + autor) — la variante modal en TODAS las refs; el grid es minoría (2 de 8 en TW). |
| faq | Falta la **lista estática 2/3-columnas** (mayoría del formato: 6 de 7 en TW no son acordeón) + la cola "Still have questions?" (CTA a soporte). |
| pricing | Recurren y no tenemos: **tabla de comparación de features** y variante **single-price**. Toggle + featured = paridad OK. |
| site-header | Sin **flyout/dropdown de navegación** no hay paridad (todas lo traen; nuestro NavigationMenu ya lo da — es cuestión de scope del block); y falta el hermano **announcement banner** (13 TW · 16 Untitled — nuestro componente Banner existe: candidato a block `banner` o slot del header). |
| stats-band | Paridad OK; variantes recurrentes adoptables: split-with-image, timeline/stepped. **Ninguna ref puede shippear count-up** (markup estático) — nuestra superación literal. |
| cta | Paridad con centered; añadir arreglo **justified** (texto izq., botones dcha.) y variante split-with-media. |
| newsletter | Añadir slot de **nota de privacidad** bajo el field (convergente). |
| site-footer | Añadir slots: **newsletter-form en footer** (3 de 7 TW) y **language selector** (encaja con nuestro langs). |
| (ecosistema) | Todas las refs shippean **páginas compuestas** como artefacto de prueba (TW 10 · Untitled 105) — nuestra página de integración F2 cumple ese rol; considerar promocionarla a demo pública. |
| Block | Suelo/recurrencias que nos faltan |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| hero | El esqueleto center\|split es correcto; la brecha es **tratamiento de media**, no conteo de layouts: presets de media (screenshot enmarcado / phone mockup / background-image cover), slot **form-in-hero** (email sign-up), **logo-strip** bajo las acciones, video embed. |
| feature-grid | **LA brecha #1 de F2**: el **split/alternante texto-vs-screenshot con lista de features** lo shippean las 5 refs y es la sección nº1 tras el hero; nuestro v1 solo tiene el grid de iconos. |
| testimonials | Falta la **cita única en spotlight** (logo + quote + avatar + autor) — la variante modal en TODAS las refs; el grid es minoría (2 de 8 en TW). |
| faq | Falta la **lista estática 2/3-columnas** (mayoría del formato: 6 de 7 en TW no son acordeón) + la cola "Still have questions?" (CTA a soporte). |
| pricing | Recurren y no tenemos: **tabla de comparación de features** y variante **single-price**. Toggle + featured = paridad OK. |
| site-header | Sin **flyout/dropdown de navegación** no hay paridad (todas lo traen; nuestro NavigationMenu ya lo da — es cuestión de scope del block); y falta el hermano **announcement banner** (13 TW · 16 Untitled — nuestro componente Banner existe: candidato a block `banner` o slot del header). |
| stats-band | Paridad OK; variantes recurrentes adoptables: split-with-image, timeline/stepped. **Ninguna ref puede shippear count-up** (markup estático) — nuestra superación literal. |
| cta | Paridad con centered; añadir arreglo **justified** (texto izq., botones dcha.) y variante split-with-media. |
| newsletter | Añadir slot de **nota de privacidad** bajo el field (convergente). |
| site-footer | Añadir slots: **newsletter-form en footer** (3 de 7 TW) y **language selector** (encaja con nuestro langs). |
| (ecosistema) | Todas las refs shippean **páginas compuestas** como artefacto de prueba (TW 10 · Untitled 105) — nuestra página de integración F2 cumple ese rol; considerar promocionarla a demo pública. |
#### Categorías de la unión que NO planeamos (decisión explícita pendiente)
@ -95,18 +95,18 @@ Variantes como **props tipadas** vs N dumps duplicados; dark/brand automático p
#### Suelos de paridad por bloque F3 (brechas concretas de nuestro v1)
| Block | Suelo que nos falta en v1 (decidir: subir alcance o registrar excepción) |
|---|---|
| app-shell | **Colapso desktop a icon-rail** (además de móvil→drawer; lo dan shadcn/Mantine/AntD); variante **stacked/topbar-only** (el primer fork en TODAS las refs); tamaños de slot responsive (`{base,sm,lg}` Mantine); scroll-hide del header; geometría de chrome como CSS vars (`--app-shell-*-offset` Mantine) para que el contenido se posicione contra ella. Skip-link: NINGUNA ref lo trae — superación nuestra. |
| auth | Slot de **social providers**; slot de **error/mensaje** top-of-card (ProComponents `message`); prefill; estado de **carga/mounting** (Clerk `fallback`); la unión de vistas de Supabase es 6, no 4: falta **magic-link** y **update-password** (su `ViewType` = sign_in·sign_up·magic_link·forgotten_password·update_password·verify_otp — la enumeración más limpia del dominio). Nuestro OTP card SUPERA el suelo copy-paste (nadie lo shippea como block). |
| data-table | El cuarteto estándar de toolbar ProTable = reload + **density** + column-setting + **fullscreen** (density/fullscreen a decidir explícito — density además es concern de framework en nuestro caso); **persistencia de layout de columnas** (`columnsState.persistenceKey`); **clear-selection** en la barra bulk (el triple `{selectedRowKeys, selectedRows, onCleanSelected}`); historia móvil de filtros (**faceted-search en drawer**, Flowbite). |
| dashboard | Stat cards con **delta de tendencia** (up/down + tono) y **spark-chart embebido** (`chartPlacement` left/right/bottom); **selector de periodo** en panels de chart; arreglo fijo v1 OK (todas las refs lo fijan). Curiosidad adoptable: `StatisticCard.Group` con `Operation` ("=" "+" entre KPIs para expresar fórmulas). |
| settings | Completo salvo: descripción por fila como slot formal; **feedback de éxito tras guardar** (Flowbite "Success Message" es categoría propia de 5). |
| user-menu | Completo (tema/idioma SUPERA las refs — son auth-céntricas); falta `showName` (variante avatar+nombre) y la historia de **colocación en rail colapsado** (patrón nav-user de shadcn: flip a dropdown-up en pie de sidebar). |
| notifications | ¡Ya estamos SOBRE el suelo — ninguna ref shippea el inbox completo como block nombrado! Añadir explícito: timestamps + distinción visual unread. |
| wizard | Gate por paso **async** (promesa que resuelve false bloquea avance — StepsForm); `current` controlado para saltos del app; **stepper compacto móvil**; semántica de merge de valores entre pasos definida. |
| error-page | Añadir variante **maintenance** (Flowbite la trata como página de primera); 403 nuestro SUPERA el suelo (casi ausente en refs). |
| kanban | Slots de metadata en card (avatar/badge/due-date) + **add-card por columna**; toolbar de group-by/filtros = standout no-suelo (diferir dejando slot). Empty-state por columna: ninguna ref lo tiene — superación. shadcn NO tiene kanban block. |
| Block | Suelo que nos falta en v1 (decidir: subir alcance o registrar excepción) |
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| app-shell | **Colapso desktop a icon-rail** (además de móvil→drawer; lo dan shadcn/Mantine/AntD); variante **stacked/topbar-only** (el primer fork en TODAS las refs); tamaños de slot responsive (`{base,sm,lg}` Mantine); scroll-hide del header; geometría de chrome como CSS vars (`--app-shell-*-offset` Mantine) para que el contenido se posicione contra ella. ~~Skip-link: NINGUNA ref lo trae — superación nuestra.~~ **[3 correcciones — fase 0 de F3.1, 2026-08-18, leídas las fuentes]** (1) **El skip-link SÍ lo traen dos referencias**: Polaris `Frame` tiene `skipToContentTarget` con enlace propio, y Atlassian genera un **menú entero** de skip-links a partir de los slots montados (`id` + `skipLinkTitle`/`skipLinkLabel`, `skipLinksLabel` = «Skip to:», Escape cierra y mueve el foco). La superación nuestra no es tenerlos: es **generarlos del mismo contrato que estampa el landmark**, sin que el consumidor escriba un `id`. (2) **El icon-rail no es de Mantine** — su `collapsed: {mobile, desktop}` es booleano, esconder o mostrar; los que sí lo tienen son shadcn (`collapsible='icon'`, 3rem), AntD (`collapsedWidth: 80`), Toolpad (mini variant) y Atlassian (colapsado + flyout al pasar el ratón). (3) Esta pista **nunca miró el `navigation-system` actual de Atlassian**, y `page-layout` —el que sí miró— está DEPRECADO. Lo nuevo añade suelo real: atajo `Ctrl+[` opt-in que se ignora bajo un modal, redimensionado con ratón **y teclado**, flyout que se queda abierto mientras haya capas abiertas dentro, y `useExpandSideNav` (la costura exacta que pide el `tour` de F5). |
| auth | Slot de **social providers**; slot de **error/mensaje** top-of-card (ProComponents `message`); prefill; estado de **carga/mounting** (Clerk `fallback`); la unión de vistas de Supabase es 6, no 4: falta **magic-link** y **update-password** (su `ViewType` = sign_in·sign_up·magic_link·forgotten_password·update_password·verify_otp — la enumeración más limpia del dominio). Nuestro OTP card SUPERA el suelo copy-paste (nadie lo shippea como block). |
| data-table | El cuarteto estándar de toolbar ProTable = reload + **density** + column-setting + **fullscreen** (density/fullscreen a decidir explícito — density además es concern de framework en nuestro caso); **persistencia de layout de columnas** (`columnsState.persistenceKey`); **clear-selection** en la barra bulk (el triple `{selectedRowKeys, selectedRows, onCleanSelected}`); historia móvil de filtros (**faceted-search en drawer**, Flowbite). |
| dashboard | Stat cards con **delta de tendencia** (up/down + tono) y **spark-chart embebido** (`chartPlacement` left/right/bottom); **selector de periodo** en panels de chart; arreglo fijo v1 OK (todas las refs lo fijan). Curiosidad adoptable: `StatisticCard.Group` con `Operation` ("=" "+" entre KPIs para expresar fórmulas). |
| settings | Completo salvo: descripción por fila como slot formal; **feedback de éxito tras guardar** (Flowbite "Success Message" es categoría propia de 5). |
| user-menu | Completo (tema/idioma SUPERA las refs — son auth-céntricas); falta `showName` (variante avatar+nombre) y la historia de **colocación en rail colapsado** (patrón nav-user de shadcn: flip a dropdown-up en pie de sidebar). |
| notifications | ¡Ya estamos SOBRE el suelo — ninguna ref shippea el inbox completo como block nombrado! Añadir explícito: timestamps + distinción visual unread. |
| wizard | Gate por paso **async** (promesa que resuelve false bloquea avance — StepsForm); `current` controlado para saltos del app; **stepper compacto móvil**; semántica de merge de valores entre pasos definida. |
| error-page | Añadir variante **maintenance** (Flowbite la trata como página de primera); 403 nuestro SUPERA el suelo (casi ausente en refs). |
| kanban | Slots de metadata en card (avatar/badge/due-date) + **add-card por columna**; toolbar de group-by/filtros = standout no-suelo (diferir dejando slot). Empty-state por columna: ninguna ref lo tiene — superación. shadcn NO tiene kanban block. |
#### Superación (ratificable) + debilidades ajenas a evitar
@ -178,8 +178,7 @@ generadores de sitios.
(composición limpia dentro de cards). **Tier GFM a decidir explícito**:
task lists, footnotes, alerts, `details/summary`, `mark`, `sub/sup`.
- **Medida**: 65ch embebido por tamaño + escape (`max-w-none` equivalente).
**Tamaños**: tailwind re-deriva la escala COMPLETA 5 veces (sm 14 → 2xl
24) — nuestra fiche debe declarar qué eje (size/density) mapea esto.
**Tamaños**: tailwind re-deriva la escala COMPLETA 5 veces (sm 14 → 2xl 24) — nuestra fiche debe declarar qué eje (size/density) mapea esto.
- **Overflow de tablas**: la respuesta sin wrapper es la de GitHub —
`table { display:block; width:max-content; max-width:100%; overflow:auto }`
(tailwind no tiene ninguna: las tablas desbordan la columna).
@ -188,7 +187,7 @@ generadores de sitios.
— especificidad CERO (cualquier clase gana) + exclusión "donut" del nodo y
descendientes. Limitación admitida: no se puede re-anidar prose dentro del
donut. Alternativa moderna: `@scope ([data-prose]) to ([data-prose-ignore])`
(Baseline *newly available* desde finales de 2025 — evaluar contra nuestro
(Baseline _newly available_ desde finales de 2025 — evaluar contra nuestro
suelo de soporte).
- **Territorio sin reclamar** (superación): comillas de blockquote
hardcodeadas en inglés en TODAS las refs (`\201C…`) → CSS `quotes`
@ -207,10 +206,10 @@ generadores de sitios.
#### sidebar — suelo de paridad
- **shadcn = 23 partes + hook** (`Provider · Sidebar · Trigger · Rail ·
Inset · Input · Header · Footer · Separator · Content · Group ·
GroupLabel · GroupAction · GroupContent · Menu(ul) · MenuItem(li) ·
MenuButton · MenuAction · MenuBadge · MenuSkeleton · MenuSub(ul) ·
MenuSubItem · MenuSubButton(a) · useSidebar`). **Nuestro boceto tiene 8 —
Inset · Input · Header · Footer · Separator · Content · Group ·
GroupLabel · GroupAction · GroupContent · Menu(ul) · MenuItem(li) ·
MenuButton · MenuAction · MenuBadge · MenuSkeleton · MenuSub(ul) ·
MenuSubItem · MenuSubButton(a) · useSidebar`). **Nuestro boceto tiene 8 —
demasiado grueso**: `item` funde MenuItem (li posicionador) + MenuButton
(interactivo, data-active, host del tooltip), y sin ese split no hay dónde
colgar action/badge/skeleton/sub-nivel. shadcn-svelte lo porta 1:1 con
@ -331,11 +330,11 @@ dogfooding = el shell ES el test de aceptación de F4.
#### El paisaje de blocks en Svelte (los 3 actores reales)
| Oferta | Qué shippea | Modelo |
|---|---|---|
| **shadcn-svelte** (CLI 1.4.2, 82k dl/wk; sobre Bits UI 2 = 821k dl/wk) | **58 blocks first-party, SOLO app**: 16 sidebars · 15 auth (login/signup/OTP) · 26 calendar · 1 dashboard · 7 familias de charts (sobre LayerChart, 180k dl/wk). **CERO marketing sections** (igual que upstream) | copy-paste vía registry CLI (`registry.json`), `llms.txt`, visual builder `/create` |
| **Flowbite Svelte Blocks** v2.1.0 (MIT, Sv5+TW4) | El único con **marketing (~29 tipos: hero, pricing, CTA, FAQ, testimonials, newsletter, footer, 404/500/maintenance, cookie, onboarding…) + application (~20: CRUD en modals/drawers, advanced tables, faceted search, sidenav)** | paquete npm; estética datada; theming superficial |
| **sv-blocks** (port Tailark) | **150+ marketing blocks** para shadcn-svelte + 60 "Veil Kit" | jsrepo CLI + **MCP server** (instalables desde Cursor/Windsurf) |
| Oferta | Qué shippea | Modelo |
| ---------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| **shadcn-svelte** (CLI 1.4.2, 82k dl/wk; sobre Bits UI 2 = 821k dl/wk) | **58 blocks first-party, SOLO app**: 16 sidebars · 15 auth (login/signup/OTP) · 26 calendar · 1 dashboard · 7 familias de charts (sobre LayerChart, 180k dl/wk). **CERO marketing sections** (igual que upstream) | copy-paste vía registry CLI (`registry.json`), `llms.txt`, visual builder `/create` |
| **Flowbite Svelte Blocks** v2.1.0 (MIT, Sv5+TW4) | El único con **marketing (~29 tipos: hero, pricing, CTA, FAQ, testimonials, newsletter, footer, 404/500/maintenance, cookie, onboarding…) + application (~20: CRUD en modals/drawers, advanced tables, faceted search, sidenav)** | paquete npm; estética datada; theming superficial |
| **sv-blocks** (port Tailark) | **150+ marketing blocks** para shadcn-svelte + 60 "Veil Kit" | jsrepo CLI + **MCP server** (instalables desde Cursor/Windsurf) |
Resto: Skeleton v5 (2026-07-17; theming profundo, CERO blocks), Melt original congelada (~16 meses), melt next 0.44 pre-1.0, **Ark UI Svelte 5.22** (45+ headless, org Chakra, cadencia semanal — vigilar), HeroUI sigue React-only ("Svelte planeado sin fecha"), plantillas admin = repos, no blocks componibles. Todo el paisaje asume **Tailwind v4 + copy-paste**.
@ -404,13 +403,13 @@ colisión en la fiche F1.7 (sidebar-app vs árbol-docs / B-5).
**A decisión del usuario (cambian alcance — presentadas 2026-07-21)**:
| # | Decisión | Recomendación |
|---|---|---|
| # | Decisión | Recomendación |
| --- | --------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| E-1 | Colisión F1.7: el árbol de navegación docs (anidación profunda, active-trail, data-driven) no cabe en el sidebar-app v1 y choca con B-5 | Componente canónico NUEVO `nav-tree` data-driven (precedente: Menubar ya es data-driven); sidebar queda app-céntrico; F4.1 compone nav-tree |
| E-2 | Filosofía de alcance v1: ¿subir los v1 al suelo de paridad investigado, o construir fino y pasar una ola v1.1 de paridad? | Suelo de paridad = v1 (el encargo fue "a la par y cuanto menos superarlo"); las fases 0 dimensionan cada subida con el dossier |
| E-3 | Categorías de la unión no planeadas: banner · team · contact · content-section (· bento · gallery · cookie-consent…) | Añadir a F2 las 4 baratas (banner compone el componente existente; content-section = prose+Section); resto a F5 con disparador |
| E-4 | Distribución registry/llms.txt/MCP de blocks | Registrar como iniciativa PROPIA en next-features (no engorda este plan); llms.txt del corpus docs = barato y encaja con F4 |
| E-5 | ⌘K global (F4/command): ¿adelantar el art `shortcuts` o listener app-land? | Listener app-land documentado en el README del block; el art `shortcuts` mantiene su disparador F5 (≥2 consumidores) |
| E-2 | Filosofía de alcance v1: ¿subir los v1 al suelo de paridad investigado, o construir fino y pasar una ola v1.1 de paridad? | Suelo de paridad = v1 (el encargo fue "a la par y cuanto menos superarlo"); las fases 0 dimensionan cada subida con el dossier |
| E-3 | Categorías de la unión no planeadas: banner · team · contact · content-section (· bento · gallery · cookie-consent…) | Añadir a F2 las 4 baratas (banner compone el componente existente; content-section = prose+Section); resto a F5 con disparador |
| E-4 | Distribución registry/llms.txt/MCP de blocks | Registrar como iniciativa PROPIA en next-features (no engorda este plan); llms.txt del corpus docs = barato y encaja con F4 |
| E-5 | ⌘K global (F4/command): ¿adelantar el art `shortcuts` o listener app-land? | Listener app-land documentado en el README del block; el art `shortcuts` mantiene su disparador F5 (≥2 consumidores) |
### Ángulos de superación ratificables (consolidado)

Loading…
Cancel
Save

Powered by TurnKey Linux.