|
|
# RESEARCH — Estudio de referencia del tier blocks (paridad y superación)
|
|
|
|
|
|
> 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
|
|
|
> fuentes oficiales vivas, cada una contrastando el plan contra el panorama
|
|
|
> real y devolviendo brechas + ángulos de superación.
|
|
|
|
|
|
**Estado**: ✅ COMPLETA 2026-07-21 — 6/6 pistas recibidas y sintetizadas.
|
|
|
Enmiendas E-1…E-5 presentadas al usuario (ver §Síntesis; firmas se registran
|
|
|
en `PLAN-blocks.md` §Registro).
|
|
|
|
|
|
## Rúbrica
|
|
|
|
|
|
- **Suelo de paridad**: lo que ≥2 referencias convergen en ofrecer para un
|
|
|
ítem (features, props, variantes, comportamientos). Un ítem nuestro no está
|
|
|
"hecho" por debajo de su suelo salvo decisión explícita registrada.
|
|
|
- **Diferenciadores**: features únicos de una referencia que valga la pena
|
|
|
adoptar (con juicio, no por completismo).
|
|
|
- **Superación**: lo que nuestra arquitectura permite y ninguna referencia
|
|
|
tiene (contrato morfo introspectable, sema/percepción, tokens + density +
|
|
|
RTL + dark de serie, composición real vs copy-paste).
|
|
|
|
|
|
## 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 |
|
|
|
|
|
|
## Hallazgos
|
|
|
|
|
|
### P1 — Catálogos de marketing — ✅ RECIBIDA 2026-07-21 (conteos y nombres de variante en vivo)
|
|
|
|
|
|
#### Panorama
|
|
|
|
|
|
- **shadcn/ui blocks: CERO marketing** (solo sidebar/login/signup/dashboard) — el marketing vive en registries de terceros. Su aporte es el modelo de distribución (registry + CLI + "Open in v0").
|
|
|
- **Tailwind Plus**: ~133 ejemplos de marketing en 16 categorías (Heroes 12 · Features 15 · Pricing 12 · CTA 11 · Stats 8 · Testimonials 8 · FAQs 7 · Footers 7 · Newsletter 6 · Team 9 · Blog 7 · Contact 7 · Content 7 · Logo Clouds 6 · Bento 3 · Headers 8) + elements (Navbars 11 · Flyout 7 · **Banners 13**) — dump estático HTML/React/Vue; **dark = segundo artefacto a la venta**.
|
|
|
- **Flowbite**: ~170 marketing en 28 categorías (Hero 18 con nombres capturados: carousel, phone-mockup+app-store, search-bar+datepicker, video, sign-up-in-hero…; auth y 404/500/maintenance clasificados como marketing).
|
|
|
- **PrimeBlocks** (relanzado, Vue-first, $599/dev): ~168 marketing (Feature 27 · Testimonial 20 · Team 12 · Navbar 12 · CTA 12 · Logo Cloud 10…).
|
|
|
- **Relume** (AI builder, 1.000+/plataforma): la taxonomía más ancha (31 categorías: gallery, events, careers, comparison, timeline, multi-step forms, cookie consent, link pages…); semántica débil ("Layout 249").
|
|
|
- **Untitled UI React**: el **casi-par más cercano** — componentes React reales con props (no dumps), la mayor profundidad por categoría (Hero 44 · Features 42 · Footers 40 · Testimonials 26 · Pricing 22 · FAQ 16 · Metrics 16) + 105 page examples; pero React-only, PRO-gated, sin tokens/RTL/density.
|
|
|
- **daisyUI**: sin blocks (solo templates de pago de página completa) — confirma que ninguna librería class-based shippea secciones componibles.
|
|
|
|
|
|
#### 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. |
|
|
|
|
|
|
#### Categorías de la unión que NO planeamos (decisión explícita pendiente)
|
|
|
|
|
|
≥2 refs shippean: **banners** · logo-cloud (diferido ya) · **team** · blog · **contact** · content/prose-section · bento grid · flyout/mega-menu (elemento) · careers · gallery/portfolio · events · comparison section · timeline section · multi-step forms · cookie consent · popups/promos. Candidatas obvias F2.5+: banner (componente ya existe), team, contact, content-section (composición de `prose`+Section), bento (AutoGrid?).
|
|
|
|
|
|
#### Superación ratificable
|
|
|
|
|
|
Variantes como **props tipadas** vs N dumps duplicados; dark/brand automático por tokens vs segundo artefacto; **gestión de landmarks + política de heading-level** (categoría que la industria NI VENDE — los dumps hardcodean h-levels y rompen jerarquías al componer); comportamiento del canon (Accordion/Drawer/ToggleGroup) vs "Requires Flowbite JS"; count-up con reduced-motion; RTL + density + cero strings horneados (i18n by construction).
|
|
|
|
|
|
### P2 — Aplicación — ✅ RECIBIDA 2026-07-21 (conteos de catálogos vivos + APIs de repo)
|
|
|
|
|
|
#### Panorama (los dos polos y el hueco del medio)
|
|
|
|
|
|
- **Copy-paste sin API** (Tailwind Plus App UI ~40 categorías con conteos —
|
|
|
Tables 19, Input Groups 21, Stacked Lists 15, Command Palettes 8, Empty
|
|
|
States 6…; Flowbite ~155 blocks de app con taxonomía **CRUD-céntrica**
|
|
|
(create/read/update/delete × form/modal/drawer/section — tratan "la
|
|
|
pantalla CRUD" como unidad); Tremor Blocks 303 — señal de GRANULARIDAD:
|
|
|
Filterbar/Table Actions/KPI Cards/Chart Tooltips como categorías de primer
|
|
|
nivel; Mantine UI 123). Cero modelo de composición: 19 tablas = 19
|
|
|
snapshots divergentes sin lifecycle.
|
|
|
- **Config-driven** (AntD ProComponents, Refine): todo props +
|
|
|
escape-hatches `xxxRender` (~10 en ProLayout) → acantilado de
|
|
|
personalización; ProTable = monolito search+table+toolbar infactorizable.
|
|
|
- **shadcn blocks** = scaffold eject-and-own: `dashboard-01` copia 19 deps
|
|
|
de registry + **6 paquetes npm a TU árbol** (tanstack-table, dnd-kit,
|
|
|
recharts, zod…). 16 sidebars porque las variantes no pueden ser props.
|
|
|
- **El hueco**: libertad de markup de Tailwind CON el comportamiento de
|
|
|
ProTable = exactamente la API compuesta de partes con children. Nadie lo
|
|
|
ocupa.
|
|
|
|
|
|
#### 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. |
|
|
|
|
|
|
#### Superación (ratificable) + debilidades ajenas a evitar
|
|
|
|
|
|
Lifecycle real (un fix de Table propaga a data-table/dashboard/kanban vs 19
|
|
|
snapshots); comportamiento heredado con CERO deps nuevas (vs 6 npm de
|
|
|
dashboard-01); el punto medio composición-con-comportamiento; a11y a nivel
|
|
|
block (skip-links, foco toolbar↔tabla↔paginación, live-regions "N
|
|
|
seleccionados"/"todas leídas"/cambio de paso, roving en columnas kanban —
|
|
|
NINGUNA ref lo shippea); theming/density/RTL/i18n del sustrato (todas
|
|
|
hardcodean `dark:` e inglés); coherencia cross-block vía runtime compartido
|
|
|
(sema en danger-zone/card-drop = canal que no existe fuera). **Evitar**:
|
|
|
fork-and-drift (16 sidebars), monolito ProTable, Inferencer desechable, y la
|
|
|
lección Supabase Auth UI: murió por appearance-API cerrada — nuestro auth
|
|
|
como composición sobre handlers del app es la forma durable.
|
|
|
|
|
|
### P3 — F1 ligeros — ✅ RECIBIDA 2026-07-21 (APIs a nivel de prop; AntD 6.5 · Mantine 9.4 · Bootstrap 5.3 · Chakra v3 · Polaris · Atlaskit · Base UI 1.6)
|
|
|
|
|
|
**Hueco headless verificado**: Base UI, Ark, React Aria y Radix Primitives
|
|
|
shippean CERO de los cinco — seríamos los primeros headless+contrato en
|
|
|
sticky y anchor-nav; los únicos comparables son kits estilizados (AntD) o
|
|
|
generadores de sitios.
|
|
|
|
|
|
#### sticky (F1.1)
|
|
|
|
|
|
- **La plataforma ya fijó el vocabulario**: CSS `@container scroll-state(stuck: top|bottom)` (Chrome/Edge 133+; Firefox/Safari NO ≤2026). **Espejar exactamente** en `data-stuck` + `data-edge="top|bottom"` → los selectores eidos traducen mecánicamente al CSS nativo cuando aterrice; seríamos el único contrato stuck CSS-direccionable cross-browser (motor = doble centinela IO bendecido por Chrome, que además cumple nuestra doctrina anti-reflow).
|
|
|
- **Brechas del boceto**: (1) **sin props de offset** — la geometría del centinela está acoplada matemáticamente al inset del sticky (el `-24px` del artículo de Chrome espeja el `top` del header): `offset` no es opcional; (2) **sin scroll-host** (AntD `target`, MDN: sticky se pega al ancestro con overflow más cercano AUNQUE no scrollee — el `root` del IO debe ser ese ancestro o la detección rompe en silencio); (3) "sin eventos v1" queda BAJO suelo (AntD `onChange(affixed)`) — recorte declarable, pero reservar el nombre del evento en el contrato ya; (4) borde bottom = centinela DISTINTO (`threshold:[1]`, por ratio) — es un centinela por borde con configs diferentes.
|
|
|
- **Pitfalls**: `overflow:hidden` en cualquier ancestro mata el sticky en silencio (detector dev-mode con warning); IO recorta por CADA ancestro con overflow; `rootMargin` solo px/% (no em); frontera exacta no dispara (centinelas con altura real, no 1px); `data-stuck` llega un frame tarde (async — solo styling, nunca matemática de layout); Mantine Affix NO es comparable (es un slot fixed en Portal).
|
|
|
|
|
|
#### anchor-nav (F1.6)
|
|
|
|
|
|
- **Suelo convergente**: compensación de offset del heading (`targetOffset` ≠ offset del affix — AntD los separa; SIN él, el click-scroll entierra headings bajo nuestro propio sticky; par CSS: `scroll-margin-top` en targets); anidación con indent (CSS var de profundidad — Mantine `--depth-offset`, nosotros tokenizada); callback de cambio; **mecanismo de re-scan** (Bootstrap `refresh()`, Mantine `reinitializeRef` — nuestro ciclo de registro de partes lo cubre tipado, decirlo); **regla de zona muerta al fondo** (Docusaurus: sin anchor bajo el umbral → el ÚLTIMO activo; su regla de lectura: si el siguiente anchor está bajo la mitad superior → el ANTERIOR es el activo — modela "qué se está leyendo").
|
|
|
- **Política de hash a decidir** (silencio en el boceto): AntD migró a history API (`replace` opt-in existe porque el push del spy contamina el historial); spy → replace o nada, click → puede push; deep-link `#id` al cargar fija el activo inicial.
|
|
|
- **aria-current: liderazgo barato** — solo 1 de 5 refs lo pone (Starlight `"true"`; Docusaurus/Bootstrap/Mantine/AntD = clase o data-attr). Valor spec-preciso: `"location"`; precedente shipped: `"true"`; **NUNCA `"page"`** (es para páginas de un set, no secciones).
|
|
|
- Scroll programático barre el spy por secciones intermedias (flicker) → suprimir spy hasta asentar o closest-wins; targets ocultos (tabs/colapsados) se saltan y re-observan al mostrar.
|
|
|
- Detección: **IO de banda** (Starlight: rootMargin calculado del chrome, recomputado en resize) — Mantine y Docusaurus usan scroll listeners crudos (la clase de bug que nuestra doctrina mata).
|
|
|
|
|
|
#### empty-state (F1.2) y result (F1.3) — un esqueleto, dos componentes
|
|
|
|
|
|
- Anatomía nuestra ≈ paridad exacta con shadcn `Empty`/Chakra v3. **Adoptar lo que nadie combina**: Atlaskit `headingLevel` (1–6, default 4) + `headingSize` DESACOPLADO (la única ref que hace bien la semántica de heading); `buttonGroupLabel` (nombre accesible del grupo de acciones); Polaris: UNA sola acción primaria (verbo+sustantivo), variantes secondary/tertiary; Chakra `size` sm/md/lg — **el tamaño compacto es lo que Table/Select/Command consumirán** (AntD SIMPLE preset + `ConfigProvider.renderEmpty` = inyección global en colecciones; los headless lo tratan como render-slot de colecciones: RAC `renderEmptyState` estampa `[data-empty]`); shadcn `EmptyMedia variant="icon"` → nuestro `data-media-kind`; clamp de ilustración (Atlaskit 160×160).
|
|
|
- **result = solo de AntD** (nadie más lo tiene; Tailwind lo vende como páginas 404). Nuestro enum semántico > su `'404'` stringly-typed, PERO: (1) **falta `warning`** (7º valor de AntD; mapea natural a nuestro intent `risk`) — decidir explícito; (2) default `status` (AntD: `info`) — sin default es contract smell; (3) **AntD NO pinta 403/404/500 de rojo** (son situaciones, no fallos — ilustración neutra reemplaza al icono; nuestro media debe aceptar reemplazo total); (4) foco/anuncio tras navegación SPA (mover foco al título o live region polite — ninguna ref lo hace).
|
|
|
|
|
|
#### callout (F1.4)
|
|
|
|
|
|
- **Nuestros 4 intents NO cubren el vocabulario de facto (5)**: GitHub NOTE/TIP/IMPORTANT/WARNING/CAUTION (y Docusaurus separa note-gris de info-azul). Mapeo: neutral→NOTE · affirm→TIP · risk→WARNING · threat→CAUTION; **IMPORTANT (énfasis, púrpura) no tiene hueco**. Opciones: documentar la pérdida / `color` override sobre intent (modelo Radix: intent=semántica, color=pintura) / 5º variante. No fingir que 4 cubre 5.
|
|
|
- **`role="alert"` estático = bug de shadcn, NO copiar** (MDN: alert solo anuncia en UPDATE — en carga no hace nada — y es assertive). Lo correcto: `role="note"` + `aria-labelledby`→title, con **escalación opt-in** (prop `live`/`role`) para callouts insertados dinámicamente (ahí sema puede sonar — cosa que ninguna ref puede expresar).
|
|
|
- **El title NO es heading en ninguna ref** (GitHub: párrafo estilizado) — y un heading contaminaría el outline que ESCANEA nuestro propio anchor-nav (interacción real intra-suite). **El intent no viaja solo en color/icono**: GitHub lleva LABEL de texto visible — título default por intent = patrón robusto (color-blind + SR).
|
|
|
- Radix como modelo de ejes: `variant soft|surface|outline` × color × size × highContrast, ortogonales. Nesting (Docusaurus): CSS tolerante a callout-en-callout.
|
|
|
|
|
|
### P4 — prose + sidebar — ✅ RECIBIDA 2026-07-21 (verificación a nivel de código fuente)
|
|
|
|
|
|
**Método**: el agente leyó el `sidebar.tsx` real del registry de shadcn
|
|
|
(new-york-v4) y el `styles.js`/`index.js` de @tailwindcss/typography en crudo
|
|
|
— los datos de abajo son de código, no de prosa de docs.
|
|
|
|
|
|
#### prose — suelo de paridad
|
|
|
|
|
|
- **Lista de elementos de paridad** (unión tailwind+GitHub+Mantine):
|
|
|
**h1–h6** (¡tailwind NO cubre h5/h6; GitHub y Mantine sí — cubrirlos!),
|
|
|
`p`, `a` (+ `a code`, `a strong`), `strong/em` (con contextos: dentro de
|
|
|
blockquote/th/h1–h4), `blockquote` (+ comillas de apertura/cierre en
|
|
|
`p:first/last-of-type::before/::after`), `ul/ol/li` + `::marker`
|
|
|
(counters/bullets tokenizados), correcciones de margen de listas anidadas
|
|
|
y `p`-en-`li`, `dl/dt/dd`, `table` completa (thead/tbody/**tfoot**/th/td,
|
|
|
trim de padding en primera/última celda), `code/pre` (+ reset `pre code`,
|
|
|
backticks vía `code::before/::after`), `kbd` (doble box-shadow), `img` /
|
|
|
`picture` (+ reset `picture > img`) / `video` / `figure` / `figcaption`,
|
|
|
`hr`, `lead` (clase, no `:first-of-type`), y **recorte de bordes**
|
|
|
`> :first-child { margin-top:0 }` / `> :last-child { margin-bottom:0 }`
|
|
|
(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.
|
|
|
- **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).
|
|
|
- **El mecanismo `not-prose` (crítico)**: tailwind emite CADA selector como
|
|
|
`:where(.prose el):not(:where([class~="not-prose"], [class~="not-prose"] *))`
|
|
|
— 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
|
|
|
suelo de soporte).
|
|
|
- **Territorio sin reclamar** (superación): comillas de blockquote
|
|
|
hardcodeadas en inglés en TODAS las refs (`\201C…`) → CSS `quotes`
|
|
|
locale-aware vía langs; **exclusión automática de componentes embebidos**
|
|
|
(todo lo que cuelgue de una raíz `data-{component}` se auto-excluye — las
|
|
|
refs no pueden porque no poseen el markup de sus consumidores); dark
|
|
|
gratis vía roles (NO copiar el patrón `-invert`: es un parche por colores
|
|
|
horneados); code-in-heading (`h2 code` con corrección de tamaño — tailwind
|
|
|
lo trae, Fumadocs lo presume).
|
|
|
- **Avisos**: selectores de elemento = categoría "estructural bajo una
|
|
|
parte" del eidos-lint — sancionarlo deliberadamente o el recipe lintará
|
|
|
invalid; propiedades lógicas (`border-inline-start`) desde el día uno;
|
|
|
anchors de heading son capa docs-shell (patrón a11y correcto: link DESPUÉS
|
|
|
del heading, à la Starlight), no capa prose.
|
|
|
|
|
|
#### 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 —
|
|
|
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
|
|
|
dot-notation y Svelte 5 (`bind:open`, snippets para asChild) — el mapeo a
|
|
|
nuestro idioma está probado.
|
|
|
- **Dos ejes, no uno**: estado `data-state="expanded|collapsed"` + modo
|
|
|
`collapsible="offcanvas|icon|none"` (shadcn estampa `data-collapsible`
|
|
|
SOLO estando colapsado — ojo si espejamos nombres). Nuestro `data-rail`
|
|
|
fusionaba ambos ejes → separar. `Rail` además es una PARTE física (franja
|
|
|
de 16px toggle en el borde, tabIndex -1, cursores direccionales), no un
|
|
|
estado.
|
|
|
- **Props/constantes de referencia**: `side` left|right (table stakes),
|
|
|
`variant` sidebar|floating|inset (diferible declarándolo), widths 16rem /
|
|
|
18rem móvil / 3rem icon como TOKENS (no consts TS), cookie
|
|
|
`sidebar_state` 7d con lectura SSR → `defaultOpen` (evita flash de
|
|
|
hidratación), atajo global Cmd/Ctrl+B con `preventDefault` (¡secuestra
|
|
|
la negrita de cualquier editor embebido — el nuestro se scope-a fuera de
|
|
|
contextos editables!), tooltips en modo icon montados SIEMPRE y
|
|
|
suprimidos con `hidden` (no mount/unmount), móvil = Sheet <768px (nuestro
|
|
|
Drawer con props label/description, no hacks sr-only).
|
|
|
- **Exclusiones v1 legales SOLO con costuras**: `open`/`onOpenChange`
|
|
|
controlado + `defaultOpen` + `toggle()` expuesto en el provider — la
|
|
|
persistencia y el atajo viven en app-land únicamente si esas costuras
|
|
|
existen desde v1; si no, es rediseño posterior.
|
|
|
- **A11y — todas las refs suspenden**: shadcn NO tiene landmark `<nav>`, NI
|
|
|
`aria-current` (solo data-active), atajo sin `aria-keyshortcuts`, rail
|
|
|
inalcanzable por teclado. El patrón correcto es APG Disclosure Navigation:
|
|
|
nav landmark + botones `aria-expanded/aria-controls` + links con
|
|
|
`aria-current="page"` + Esc devuelve el foco al trigger. Superación
|
|
|
directa: emitir `aria-current` del MISMO prop que estampa `data-active`.
|
|
|
- **Nadie headless lo tiene** (Radix/Base/Ark/React-Aria confirmado): el
|
|
|
sidebar canónico es invención del tier estilizado — espacio abierto con
|
|
|
shadcn como estándar de facto.
|
|
|
- **Pitfalls**: animar `width` = layout por frame (aceptado por la
|
|
|
industria; nosotros: offcanvas por transform y width-anim solo en icon,
|
|
|
lecturas vía `dom.measure`); submenús en modo icon shadcn los OCULTA
|
|
|
(nuestro DropdownMenu/Popover flotante en rail = superación, es el mejor
|
|
|
comportamiento de AntD Menu); `overscroll-behavior: contain` en Content;
|
|
|
RTL lógico desde el día uno (shadcn necesitó un changelog entero de
|
|
|
retrofit); jitter de `will-change` en raíles finos a DPR≠1 (memoria).
|
|
|
|
|
|
### P5 — Docs — ✅ RECIBIDA 2026-07-21 (Starlight 0.41 · Fumadocs 16.11 · Docusaurus 3.10 · VitePress 1.6/2α · Nextra 4.6 · Mintlify)
|
|
|
|
|
|
#### Suelo del docs-shell (convergencia de los 6)
|
|
|
|
|
|
3 regiones con raíles sticky de scroll independiente + header sticky; sidebar
|
|
|
con ≥3 niveles, grupos colapsables, profundidad default-open, **active trail
|
|
|
auto-expandido**, badges, drawer móvil; TOC h2–h3 configurable con scrollspy,
|
|
|
**móvil = popover/dropdown**, back-to-top; búsqueda = **command palette ⌘K**
|
|
|
con índice estático (Pagefind/MiniSearch/Orama); breadcrumb del árbol (solo
|
|
|
anidado); prev/next del orden del árbol; fila meta (edit-on-GitHub +
|
|
|
last-updated); dark light/dark/system sin flash; slots reservados: banner de
|
|
|
anuncio, sidebar top/bottom, page-actions, versión/idioma. **Fumadocs es el
|
|
|
benchmark y el ÚNICO que shippea el shell como componentes reutilizables**
|
|
|
(DocsLayout/DocsPage con sistema de slots + fumadocs-core headless; anchos
|
|
|
TOKENIZADOS `--fd-sidebar-col`/`--fd-toc-width` — validación de nuestro
|
|
|
enfoque). Diferenciadores adoptables: tabs de sección en sidebar (Fumadocs),
|
|
|
TOC estilo "clerk" (raíl de pulgar), auto-colapso de hermanos (Docusaurus),
|
|
|
**acciones AI** (copy-page-as-Markdown, open-in-Claude/ChatGPT, `llms.txt`,
|
|
|
negociación de contenido `.md` — table stakes 2026 y casi gratis: nuestro
|
|
|
corpus YA es Markdown).
|
|
|
|
|
|
#### Suelo del code-showcase
|
|
|
|
|
|
Preview + código + copy SIEMPRE; barra de **filename con icono**; números de
|
|
|
línea; **marcas de línea/palabra**; **diff** (`++`/`--`); código largo
|
|
|
**colapsable** (shadcn: 3 líneas + gradiente + expandir); **tabs de package
|
|
|
manager** (npm/pnpm/yarn/bun de un solo bloque); **grupos multi-archivo**
|
|
|
con `syncKey` site-wide (Starlight); controles de preview: alineación +
|
|
|
**toggle RTL** (el de shadcn lleva disclaimer "AI-translated" — el nuestro
|
|
|
iría con langs real). ⚠️ La mayoría pertenece al canon `code-block`:
|
|
|
**verificar qué soporta nuestro recipe ANTES de F4.2; cada carencia = gap de
|
|
|
canon flaggeado** (regla de admisión).
|
|
|
|
|
|
#### Props-table: generación y suelo de presentación
|
|
|
|
|
|
- **Suelo de presentación = Ark UI**: secciones POR PARTE (props +
|
|
|
data-attributes + ARIA + keyboard por parte) + tabla de contexto/API.
|
|
|
Nuestro `<PropsTable>` plano queda por debajo — el morfo ya trae el
|
|
|
seccionado; renderizarlo.
|
|
|
- **Generación en refs**: manual (Radix — puro riesgo de drift) ·
|
|
|
build-time docgen → JSON (Mantine, Ark `generate-type-docs.ts`) ·
|
|
|
TS-Compiler-API en build (Fumadocs AutoTypeTable, solo server).
|
|
|
- **Vía Svelte para props TS** (la cuestión abierta de F4.3): **sveld
|
|
|
DESCALIFICADO** (sin resolución semántica — `ResponsiveProp<T>` quedaría
|
|
|
texto opaco); svelte-docgen pre-v1; **la ruta = svelte2tsx + TS Compiler
|
|
|
API en build-time → JSON por componente** (Storybook migró a ella
|
|
|
precisamente por runes; PRs #29423/#28492).
|
|
|
|
|
|
#### Brechas señaladas en NUESTRAS fiches F4/F1 (leyó el plan — 10 puntos)
|
|
|
|
|
|
1. **F4.1 slots insuficientes**: faltan topbar, banner, sidebar top/bottom, page-actions, opciones de breadcrumb, fila meta, slots versión/idioma. D-BLK.6 manda el wiring al app — pero el shell debe RESERVAR los puntos de montaje.
|
|
|
2. **⚠️ COLISIÓN F1.7 vs docs-tree (firmar ANTES de construir F1.7)**: el sidebar de app v1 (grupos+rail) no cubre árbol profundo, active-trail auto-expand, auto-colapso, default-open depth, scroll-active-into-view; y un árbol de 100 páginas como children choca con B-5 → o el componente canónico sidebar/tree es data-driven (precedente: Menubar ya lo es), o hay pieza canónica nueva (`nav-tree`). Decisión de usuario.
|
|
|
3. **⌘K sin decidir**: Command existe; el registro global de atajo está en F5. Todas las refs lo dan día uno. Decidir: listener app-land documentado o adelantar el art `shortcuts`.
|
|
|
4. **TOC móvil sin mecanismo** en F1.6/F4.1 (refs: popover o dropdown) + anchor-nav sin config min/max de nivel.
|
|
|
5. **prose sin heading-anchors**: Starlight 0.34 los hizo core con el patrón a11y correcto (link DESPUÉS del heading + label generada); sin ellos mueren los deep-links del TOC. + iconos de external-link (VitePress).
|
|
|
6. **F4.2 bajo suelo** (lista de arriba) y **los chips de tema/density/dir diferidos son EL FOSO, no un nice-to-have** — ver superación.
|
|
|
7. **F4.3 por-parte** (suelo Ark) + tabla de eventos familia·verbo·intent.
|
|
|
8. **Dirección TS-props**: fijar ya la ruta svelte2tsx build-time.
|
|
|
9. **Cero acciones AI** en los sketches (llms.txt, copy-as-md — casi gratis).
|
|
|
10. **Falta last-updated/edit-link** y la decisión del skip-link (diferida en F2.1) debe aterrizar antes de F4.1.
|
|
|
|
|
|
#### Superación (la más fuerte de las 6 pistas)
|
|
|
|
|
|
**Props tables que no pueden mentir**: `compileMorfo` da partes/attrs/ARIA/
|
|
|
keyboard/eventos EN RUNTIME del mismo objeto que ejecuta el componente —
|
|
|
cero drift por construcción, CI puede exigir tabla completa por componente
|
|
|
PASS; la tabla de keyboard de nadie más ES el plan de teclado real. **Tabla
|
|
|
de eventos semántica** (familia·verbo·intent·canales) que ninguna ref tiene.
|
|
|
**Ejes de tema por demo** (theme/density/contrast/RTL/motion vivos con
|
|
|
tokens reales — imposible para SSG estáticos). **Búsqueda contract-aware**
|
|
|
(Command indexando morfos: "¿quién maneja Escape?", "¿quién estampa
|
|
|
data-stuck?" — Pagefind indexa texto, no contratos). Scrollspy con
|
|
|
disciplina de reflow demostrable por `uix.perf`; sema demoable por evento;
|
|
|
dogfooding = el shell ES el test de aceptación de F4.
|
|
|
|
|
|
### P6 — Svelte — ✅ RECIBIDA 2026-07-21 (inventarios por llms.txt + npm; versiones al día)
|
|
|
|
|
|
#### 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) |
|
|
|
|
|
|
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**.
|
|
|
|
|
|
#### Huecos objetivos del ecosistema (nadie los tiene)
|
|
|
|
|
|
docs-shell · settings/preferences · billing/checkout · empty/error states como SISTEMA · data-table CRUD componible (Flowbite = snippets sueltos) · blocks con i18n/RTL · a11y auditada a nivel sección · blocks sobre tokens no-Tailwind · capa perceptiva. **Nadie combina componentes + theming profundo + blocks**: shadcn tiene blocks sin theming profundo; Skeleton theming sin blocks; Flowbite blocks con theming superficial. Esa es la ventana.
|
|
|
|
|
|
#### Listón de paridad Svelte (para reclamar "la oferta más completa")
|
|
|
|
|
|
1. App-shell: cubrir la matriz de los 16 sidebars de shadcn con **1 block parametrizado + presets, documentando la equivalencia variante-a-variante** (no competir en cardinalidad copy-paste).
|
|
|
2. Auth: login/signup/OTP/forgot (15+ layouts entre shadcn y Flowbite).
|
|
|
3. Dashboard completo + stat/chart cards sueltos; **galería de charts con variantes día uno** (lección LayerChart: shadcn no escribió motor, montó galería).
|
|
|
4. CRUD: tabla avanzada + create/update en modal Y drawer + delete-confirm (el set Flowbite que shadcn no tiene).
|
|
|
5. Marketing: la unión Flowbite+sv-blocks ≈ 20 tipos — nuestra F2 (10) debe contrastarse contra esa unión (banners/cookie/team/blog/contact/onboarding no están en F2 → decisión explícita).
|
|
|
6. Calendar: shadcn 26 blocks; con nuestra riqueza de componentes bastan menos, cubriendo range/presets/time/booked.
|
|
|
|
|
|
#### Amenazas / lecciones arquitectónicas (para decidir, no resolver en silencio)
|
|
|
|
|
|
- **La cultura copy-paste GANÓ la distribución.** Un tier empaquetado necesita historia de **personalización estructural** (partes/snippets por zona — nuestra API composicional ya lo es) y quizá un camino de "eject" block→código propio.
|
|
|
- **Registry + llms.txt + MCP = table stakes 2026**: los blocks deberían ser enumerables/instalables por agentes (encaja con la vía agentiva del proyecto). Candidato a F5/decisión.
|
|
|
- **Zag/Ark = la vara de a11y** cross-framework: conviene poder DEMOSTRAR la nuestra (matriz teclado/ARIA por block, generada del morfo — inédito).
|
|
|
- **Tailwind v4 es el sustrato universal**: zero-dep es diferenciador (sin lock-in) y fricción a la vez — tensión estratégica del usuario.
|
|
|
- Los upgrades propagan en nuestro modelo (composición) vs congelación copy-paste — EL argumento; requiere la historia de personalización de arriba para sostenerse.
|
|
|
|
|
|
## Síntesis (6/6 pistas — 2026-07-21)
|
|
|
|
|
|
Los suelos de paridad por ítem viven en cada sección de pista (arriba); las
|
|
|
fases 0 del plan contrastan contra ellos. Lo transversal:
|
|
|
|
|
|
### Cinco conclusiones
|
|
|
|
|
|
1. **La ventana estratégica es real y está abierta**: nadie — en ningún
|
|
|
ecosistema — combina componentes + theming profundo + blocks con
|
|
|
comportamiento. En Svelte el espacio "sistema integrado" está vacío
|
|
|
(shadcn-svelte: blocks sin theming profundo; Skeleton: theming sin
|
|
|
blocks; Flowbite: blocks con theming superficial). El casi-par global es
|
|
|
Untitled UI React (componentes con props de verdad) — React-only,
|
|
|
PRO-gated, sin tokens/RTL/density. En los 7 componentes F1, los headless
|
|
|
(Radix/Base/Ark/React-Aria) shippean CERO.
|
|
|
2. **El patrón de brechas de nuestro v1 es consistente**: los esqueletos son
|
|
|
correctos; lo que falta es (a) la SEGUNDA variante convergente del
|
|
|
mercado (feature-split, testimonial-spotlight, faq-lista, hero-media,
|
|
|
cta-justified), (b) las costuras de estado (open/onOpenChange/toggle en
|
|
|
sidebar; error/loading slots en auth), y (c) los ejes que el mercado
|
|
|
resuelve con N copias y nosotros con props.
|
|
|
3. **La distribución copy-paste ganó**: registry + llms.txt + MCP son table
|
|
|
stakes 2026. Nuestro contraataque es estructural (upgrades que propagan,
|
|
|
variantes tipadas) pero necesita la historia de personalización
|
|
|
(composición por partes = ya la tenemos) y la enumerabilidad por agentes
|
|
|
(candidata a iniciativa propia).
|
|
|
4. **El liderazgo a11y es barato y sistémico**: las referencias suspenden en
|
|
|
TODO el tablero (sin landmarks ni gestión de heading-level en secciones,
|
|
|
sin `aria-current` en 4 de 5 scrollspies, `role="alert"` estático como
|
|
|
bug shipped, atajos globales sin scope). Nuestra doctrina ya obliga a lo
|
|
|
correcto; solo hay que no copiarles.
|
|
|
5. **Alinearse con la plataforma donde ya estandarizó**: `data-stuck`/
|
|
|
`data-edge` espejando `scroll-state(stuck:)`; `@scope` con donut como
|
|
|
mecanismo moderno de prose (con fallback `:where()`+donut); heading
|
|
|
anchors con el patrón Starlight; `aria-current="location|true"`.
|
|
|
|
|
|
### Enmiendas al plan
|
|
|
|
|
|
**Aplicadas (no cambian alcance)**: regla de trabajo nueva en el plan §4 —
|
|
|
toda fase 0 contrasta contra este dossier ANTES de diseñar; puntero de
|
|
|
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 |
|
|
|
|---|---|---|
|
|
|
| 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) |
|
|
|
|
|
|
### Ángulos de superación ratificables (consolidado)
|
|
|
|
|
|
1. Variantes como props tipadas + upgrades que propagan (vs N dumps).
|
|
|
2. Landmarks + política de heading-level como API del block (la industria NI LO VENDE).
|
|
|
3. A11y por encima de toda referencia: `aria-current` del mismo prop que `data-active`, `role=note`+labelledby, APG focus-return, atajos scoped, live-regions en bulk/steps/inbox.
|
|
|
4. Tokens: dark/brand/density/RTL gratis por sustrato (vs dark como segundo artefacto a la venta).
|
|
|
5. i18n by construction (cero strings horneados; comillas de blockquote locale-aware — territorio sin reclamar).
|
|
|
6. `data-stuck` = polyfill del contrato CSS futuro; prose con auto-exclusión de componentes embebidos (nadie puede: no poseen el markup de sus consumidores).
|
|
|
7. Props tables runtime desde `compileMorfo` que no pueden mentir + tabla de eventos semántica + búsqueda contract-aware ("¿quién maneja Escape?").
|
|
|
8. Ejes de tema por demo (theme/density/contrast/RTL/motion vivos) = el foso del showcase; sema demoable por evento.
|
|
|
9. Count-up con reduced-motion, submenús flotantes en rail, kanban con roving focus y empty por columna — cada uno literalmente imposible o ausente en las refs.
|
|
|
10. Rendimiento demostrable: scrollspy/sticky por IO vía adom con `uix.perf` probando cero forced-reflow (ninguna ref puede afirmarlo).
|