From 9bc031bcca978e3384ae6e55a343669c631de616 Mon Sep 17 00:00:00 2001 From: Joan Leon Date: Wed, 8 Apr 2026 10:01:27 +0200 Subject: [PATCH 1/2] refine loading snippets consistency --- IMPROVEMENT-PLAN.md | 387 ++++++++++++++++++ scripts/generate-skills.js | 7 - scripts/install-global.js | 2 +- scripts/install-skills.js | 2 +- snippets/Loading/Back-Forward-Cache.js | 2 +- .../Loading/CSS-Media-Queries-Analysis.js | 6 +- .../Loading/Client-Side-Redirect-Detection.js | 2 - snippets/Loading/Content-Visibility.js | 22 +- snippets/Loading/Critical-CSS-Detection.js | 2 +- .../Find-Above-The-Fold-Lazy-Loaded-Images.js | 36 +- ...Find-Images-With-Lazy-and-Fetchpriority.js | 12 +- ...y-Loaded-Images-outside-of-the-viewport.js | 23 +- ...reloaded-Loaded-and-used-above-the-fold.js | 4 - snippets/Loading/Inline-CSS-Info-and-Size.js | 4 +- .../Loading/Inline-Script-Info-and-Size.js | 4 +- .../Loading/JS-Execution-Time-Breakdown.js | 2 - snippets/Loading/Resource-Hints-Validation.js | 4 +- .../Loading/SSR-Hydration-Data-Analysis.js | 2 +- snippets/Loading/Service-Worker-Analysis.js | 4 +- snippets/Loading/TTFB-Resources.js | 12 +- .../Validate-Preload-Async-Defer-Scripts.js | 4 - 21 files changed, 485 insertions(+), 58 deletions(-) create mode 100644 IMPROVEMENT-PLAN.md diff --git a/IMPROVEMENT-PLAN.md b/IMPROVEMENT-PLAN.md new file mode 100644 index 0000000..28f9dba --- /dev/null +++ b/IMPROVEMENT-PLAN.md @@ -0,0 +1,387 @@ +# WebPerf Snippets — Plan de Mejoras Consolidado + +> Fusión de análisis realizados el 2026-03-30 por Claude, Codex y Gemini CLI. +> Los ítems marcados con ★ son consenso entre al menos dos de los tres análisis. + +--- + +## Resumen ejecutivo + +El proyecto tiene una base sólida: 47 snippets bien organizados, documentación visual con diagramas Mermaid, soporte de Agent Skills y paridad entre fuentes, páginas y artefactos generados. Las mejoras se agrupan en cinco vectores: + +1. **Robustez** — Evitar inconsistencias entre fuentes y artefactos generados +2. **Consistencia editorial** — Homogeneizar la profundidad de cada página +3. **Descubribilidad** — Mejorar el onboarding y la navegación por contexto +4. **Expansión** — Nuevos snippets para APIs modernas +5. **Medios y visuales** — Hacer el output comprensible a primera vista + +--- + +## Fase 1 — Robustez y mantenibilidad ★ + +**Objetivo:** Reducir el riesgo de inconsistencias entre contenido fuente y artefactos generados. + +### 1.1 Script de consistencia ★ + +Crear `scripts/check-consistency.js` expuesto como: + +```json +"check:consistency": "node scripts/check-consistency.js" +``` + +Debe validar: +- Cada snippet tiene página MDX asociada (o excepción explícita) +- Cada página tiene snippet asociado (o está marcada como página editorial) +- `_meta.json` está alineado con los `.mdx` existentes +- `skills/` y `dist/` no están desactualizados respecto a `snippets/` y `pages/` +- Los conteos publicados en `README.md` y `SKILLS.md` no han quedado obsoletos + +### 1.2 Check de artefactos generados ★ + +```json +"generate-skills:check": "node scripts/generate-skills.js && git diff --exit-code -- skills dist" +``` + +Detecta si alguien modifica fuentes sin regenerar salidas. Corrige también la desalineación actual entre `package.json` (v1.2.0) y `skills/webperf/SKILL.md` (v1.1.0). + +### 1.3 Modelo explícito para páginas editoriales + +`Get-Your-Head-in-Order.mdx` es contenido editorial válido, pero no está formalizado. Opciones: + +- Frontmatter `type: guide` para páginas sin snippet asociado +- Lista de excepciones explícitas en el check de consistencia + +Recomendación: usar frontmatter — es autodocumentado y legible por automatizaciones. + +### 1.4 CI para pull requests ★ + +Workflow de GitHub Actions en cada PR: + +```yaml +- npm ci +- npm run lint +- npm run build +- npm run check:consistency +- npm run generate-skills:check +``` + +Es la mejora con mejor retorno inmediato: evita que errores de consistencia lleguen a `main`. + +--- + +## Fase 2 — Documentación y experiencia de contribución ★ + +**Objetivo:** Reducir la curva de entrada para nuevos contribuidores y consumidores. + +### 2.1 Plantilla estándar por página ★ + +Esqueleto mínimo para cada snippet: + +``` +1. Descripción breve (2-3 párrafos) +2. Tabla de umbrales (Good / Needs Improvement / Poor) — donde aplique +3. Diagrama Mermaid (flujo o secuencia) +4. Snippet ejecutable con botón de copia +5. Explicación del output (qué significa cada campo) +6. Causas comunes y qué hacer si el resultado es malo +7. Ejemplo de integración RUM (al menos 1) +8. Tabla de compatibilidad de navegadores +9. Further Reading +``` + +Páginas prioritarias a enriquecer: + +| Página | Qué le falta | +|--------|-------------| +| `LCP-Trail.mdx` | Tabla de campos, diagrama, sección de interpretación | +| `LCP.mdx` | Ejemplo de integración RUM | +| `FCP.mdx` | Diagrama de fases, tabla de causas comunes | +| `LongTask.mdx` | Migración FID→INP, comparativa con LoAF | +| `Scroll-Performance.mdx` | Tabla de umbrales, ejemplos de causas | +| `Content-Visibility.mdx` | Diagrama before/after, casos de uso | +| `Back-Forward-Cache.mdx` | Tabla de razones de fallo más comunes | + +### 2.2 Tabla de compatibilidad de APIs ★ + +En cada página, tabla de soporte real: + +| API | Chrome | Firefox | Safari | Edge | +|-----|--------|---------|--------|------| +| PerformanceObserver | ✅ 52 | ✅ 57 | ✅ 11 | ✅ 79 | +| Long Animation Frames | ✅ 123 | ❌ | ❌ | ✅ 123 | + +Actualmente solo `INP.mdx` tiene esta tabla. Evita frustración cuando un snippet no devuelve datos. + +### 2.3 Documento de arquitectura + +Crear `docs/ARCHITECTURE.md` con: + +- Flujo: fuente → docs → skills → dist +- Qué archivos son fuente de verdad vs. generados +- Cuándo ejecutar `generate-skills` +- Cómo se relacionan `pages/`, `snippets/`, `skills/` y `dist/` + +### 2.4 Guía de release + +Crear `docs/RELEASING.md`: + +- Cómo subir versión +- Cómo regenerar skills +- Cómo validar que `SKILL.md` usa la versión correcta +- Qué assets genera el workflow de release + +### 2.5 Plantilla para nuevos snippets ★ + +Archivos `templates/snippet.js` y `templates/snippet-page.mdx` con la estructura mínima esperada. Futuro comando: + +```json +"new:snippet": "node scripts/new-snippet.js" +``` + +Evita errores de naming, estructura y quality bar en cada aportación nueva. + +### 2.6 Páginas índice por categoría + +Añadir páginas de entrada por categoría: + +- `CoreWebVitals/index.mdx` — Diagrama relacional LCP / INP / CLS. Cuándo afecta cada uno al score de PageSpeed +- `Loading/index.mdx` — Jerarquía TTFB → FCP → LCP. Flujo de decisión: "¿qué medir primero?" +- `Interaction/index.mdx` — INP como métrica principal, LoAF como herramienta de diagnóstico +- `Media/index.mdx` — Impacto de imágenes y vídeos en LCP y CLS + +### 2.7 Guía de inicio rápido por perfil + +En `index.mdx` o como página separada: + +``` +¿Eres...? +├── Developer → LCP Sub-Parts + Resource Hints +├── Analista → Core Web Vitals + TTFB +├── DevOps → TTFB + Service Worker Analysis +└── Diseñador → CLS + Image Element Audit +``` + +### 2.8 Glosario técnico + +Crear `pages/glossary.mdx`: +BPP, LoAF, hadRecentInput, TAO, TTFB sub-parts, Core Web Vitals vs. Lighthouse metrics, RUM vs. Lab data. + +--- + +## Fase 3 — Medios y documentación visual ★ + +**Objetivo:** Hacer el output comprensible a primera vista, especialmente para usuarios que llegan desde buscadores o IA. + +### 3.1 Capturas de output en DevTools ★ + +Para los snippets principales (`LCP`, `CLS`, `INP`, `TTFB`, `FCP`, `Long-Animation-Frames`, `Image-Element-Audit`): + +- Captura del output en consola (imagen o GIF animado) +- Breve interpretación del resultado +- Ejemplo de output "bueno" vs. "problemático" + +Formato recomendado: imágenes en Cloudinary integradas con `CldImage`. La infraestructura ya existe. + +### 3.2 Sección RUM integration en páginas clave ★ + +Replicar el patrón de `INP.mdx` (GA4, DataDog, New Relic) en: +`LCP.mdx`, `CLS.mdx`, `TTFB.mdx`, `Long-Animation-Frames.mdx` + +### 3.3 Videos cortos por workflow ★ + +Priorizar vídeos de 30–90 segundos orientados a intención concreta: + +- "Cómo depurar TTFB lento" +- "Cómo detectar render-blocking resources" +- "Cómo investigar un LCP basado en imagen" +- "Cómo usar WebPerf Skills con un agente" +- "Cómo leer el output de INP" + +Un problema, una demo, una conclusión. La infraestructura Cloudinary ya está disponible. + +### 3.4 Playbooks / casos de uso reales + +Crear `pages/playbooks/`: + +- Auditar una landing lenta +- Depurar terceros que bloquean render +- Investigar mala experiencia móvil en red 3G +- Analizar una página Next.js con exceso de hydration data + +Conecta el catálogo técnico con problemas reales y mejora la descubribilidad desde buscadores. + +### 3.5 Botón de "Demo Live" + +Implementar páginas de prueba diseñadas para fallar en una métrica concreta (imagen pesada sin optimizar para LCP, shifts de layout para CLS) vinculadas desde su snippet. Los usuarios pueden abrir la página y ejecutar el snippet al instante. + +--- + +## Fase 4 — Tooling interno + +**Objetivo:** Reducir trabajo manual y convertir el repo en un sistema más autosuficiente. + +### 4.1 `scripts/new-snippet.js` + +Genera automáticamente: +- Archivo en `snippets//` +- Página en `pages//` +- Entrada en `_meta.json` + +### 4.2 Registro de snippets como fuente de verdad ★ + +Centralizar metadatos en `snippets-registry.json` o expandir `lib/snippets-registry.js` para que sea generado: +- Imports, títulos, descripciones base, URLs +- Genera `_meta.json` y tabla de contenidos en `README.md` +- Proporciona API interna para Agent Skills + +### 4.3 `scripts/docs-stats.js` + +Genera estadísticas del proyecto: +- Snippets por categoría +- Páginas editoriales vs. páginas con snippet +- Últimas incorporaciones +- Cobertura de tablas de compatibilidad y capturas + +Útil para refrescar README o una página "Project Stats". + +### 4.4 Smoke tests para scripts críticos + +Tests básicos de filesystem y outputs esperados para: +`generate-skills.js`, `install-skills.js`, `install-global.js`, `install-from-release.js`, futuro `check-consistency.js` + +### 4.5 Badges de salud en README + +- Última release +- Estado del CI +- Número de snippets +- Licencia + +--- + +## Fase 5 — Nuevos snippets + +**Objetivo:** Cubrir APIs modernas donde el proyecto puede ganar diferenciación. + +### 5.1 Core Web Vitals + +| Snippet | API clave | Chrome mín. | +|---------|-----------|-------------| +| `INP-Attribution.js` | PerformanceObserver + LoAF | 123 | +| `CLS-Source-Attribution.js` | LayoutShift attribution | 84 | +| `LCP-Initiator-Chain.js` | Resource Timing + LoAF | 123 | + +### 5.2 Loading ★ + +| Snippet | API clave | Chrome mín. | +|---------|-----------|-------------| +| `Speculation-Rules-Audit.js` ★ | document.ruleSets | 109 | +| `Fetch-Priority-Audit.js` | Resource Timing priority | 102 | +| `Early-Hints-Detection.js` ★ | Resource Timing | 103 | +| `Third-Party-Impact-Score.js` ★ | LoAF + Resource Timing | 123 | +| `HTTP-Cache-Audit.js` ★ | Resource Timing cache | 52 | +| `Compression-Audit.js` | Resource Timing encoding | 52 | +| `Module-Preload-Audit.js` | Resource Timing + ES modules | 66 | + +### 5.3 Interaction + +| Snippet | API clave | Chrome mín. | +|---------|-----------|-------------| +| `INP-Long-Tasks-Correlation.js` | LoAF + INP | 123 | +| `Pointer-Event-Latency.js` | Event Timing | 76 | +| `Soft-Navigation-Tracking.js` ★ | Soft Navigations API (exp.) | 123 | + +### 5.4 Media + +| Snippet | API clave | Chrome mín. | +|---------|-----------|-------------| +| `Image-Format-Audit.js` | Resource Timing + DOM | 52 | +| `Responsive-Images-Audit.js` | DOM inspection | 52 | +| `Font-Display-Audit.js` ★ | CSS OM + Resource Timing | 52 | +| `Font-Face-Detailed-Timing.js` ★ | FontFaceSet API | 35 | +| `Image-CDN-Policy-Audit.js` | Resource Timing + DOM | 52 | + +### 5.5 Nueva categoría: Security & Privacy Performance + +| Snippet | API clave | Chrome mín. | +|---------|-----------|-------------| +| `Permissions-Policy-Audit.js` | document.featurePolicy | 74 | +| `Cross-Origin-Isolation-Audit.js` | crossOriginIsolated | 87 | + +### 5.6 Nueva categoría: Resilience & Offline + +| Snippet | API clave | Chrome mín. | +|---------|-----------|-------------| +| `Service-Worker-Cache-Strategy.js` | SW Cache API | 40 | +| `Offline-Fallback-Audit.js` | SW + Fetch | 40 | + +--- + +## Fase 6 — Mejoras en snippets existentes + +| Script | Mejora | +|--------|--------| +| `TTFB.js` | Añadir parsing de `Server-Timing` header | +| `First-And-Third-Party-Script-Info.js` | Correlacionar con LoAF para indicar impacto en LCP/INP | +| `Resource-Hints.js` | Verificar si preloads y prefetches se usaron realmente | +| `Fonts-Preloaded-Loaded-and-used-above-the-fold.js` | Detectar font subsetting con `unicode-range` | +| `Long-Animation-Frames.js` | Mini timeline ASCII en consola para identificar patrones | + +--- + +## Fase 7 — Internacionalización + +Dado que el autor y parte de la comunidad de WebPerf es hispanohablante, traducir la documentación al español aportaría valor diferencial. Configurar Nextra para `/en/` y `/es/` y traducir guías y explicaciones de métricas en fases. + +--- + +## Priorización + +### Alta prioridad — impacto inmediato + +1. ★ Corregir desalineación de versión `package.json` vs `SKILL.md` +2. ★ `check-consistency.js` + `generate-skills:check` +3. ★ CI para pull requests (lint + build + consistency + artifacts) +4. ★ Capturas de output en consola para los 7 snippets principales +5. ★ Enriquecer páginas con poco contenido (LCP-Trail, FCP, LongTask, Scroll-Performance) +6. ★ `Speculation-Rules-Audit.js` — API moderna, alta demanda en 2025-2026 +7. ★ `Third-Party-Impact-Score.js` — pregunta más frecuente en auditorías + +### Media prioridad — mejora la calidad global + +8. Páginas índice por categoría +9. ★ RUM integration en LCP, CLS, TTFB +10. ★ `HTTP-Cache-Audit.js` y `Fetch-Priority-Audit.js` +11. ★ Plantilla para nuevos snippets + `new-snippet.js` +12. `docs/ARCHITECTURE.md` y `docs/RELEASING.md` +13. ★ Tabla de compatibilidad de APIs en cada página +14. Glosario técnico +15. Guía de inicio rápido por perfil + +### Baja prioridad — madurez del proyecto + +16. Testing de snippets (smoke tests + Playwright) +17. Nueva categoría Security & Privacy Performance +18. Nueva categoría Resilience & Offline +19. Videos cortos por workflow +20. Playbooks de casos de uso reales +21. Botón de "Demo Live" +22. Internacionalización (español) +23. Snippet output schema (TypeScript types) + +--- + +## Quick wins muy concretos + +Acciones de menos de 1 hora cada una: + +- [ ] Corregir versión en `skills/webperf/SKILL.md` (1.1.0 → 1.2.0) +- [ ] Añadir frontmatter `type: guide` a `Get-Your-Head-in-Order.mdx` +- [ ] Añadir `npm run check:consistency` al script section de `package.json` +- [ ] Añadir sección "Start here" en `README.md` +- [ ] Añadir badges (release, CI status, snippet count) en `README.md` +- [ ] Añadir tabla de compatibilidad a `LCP.mdx`, `CLS.mdx`, `TTFB.mdx` +- [ ] Añadir al menos 3 capturas de consola en snippets clave + +--- + +*Fusión consolidada — 2026-04-05* diff --git a/scripts/generate-skills.js b/scripts/generate-skills.js index e166b95..97a4b07 100755 --- a/scripts/generate-skills.js +++ b/scripts/generate-skills.js @@ -218,13 +218,6 @@ function extractThresholds(content) { return '' } -// Strip internal relative links (e.g. /CoreWebVitals/LCP-Sub-Parts) from markdown link text -function cleanLinks(text) { - return text - .replace(/\[([^\]]+)\]\(\/[^)]+\)/g, '$1') // remove internal links, keep text - .replace(/\[([^\]]+)\]\((https?:[^)]+)\)/g, '[$1]($2)') // keep external links -} - // Build metadata for a single snippet JS file function buildSnippetMeta(category, snippetFile) { const basename = path.basename(snippetFile, '.js') diff --git a/scripts/install-global.js b/scripts/install-global.js index 41e2cf5..ea2f276 100755 --- a/scripts/install-global.js +++ b/scripts/install-global.js @@ -25,7 +25,7 @@ function main() { console.log('1️⃣ Generating skills...') try { execSync('node scripts/generate-skills.js', { cwd: ROOT, stdio: 'inherit' }) - } catch (error) { + } catch { console.error('❌ Failed to generate skills') process.exit(1) } diff --git a/scripts/install-skills.js b/scripts/install-skills.js index 0f0e94b..337595e 100755 --- a/scripts/install-skills.js +++ b/scripts/install-skills.js @@ -26,7 +26,7 @@ function main() { console.log('1️⃣ Generating skills...') try { execSync('node scripts/generate-skills.js', { cwd: ROOT, stdio: 'inherit' }) - } catch (error) { + } catch { console.error('❌ Failed to generate skills') process.exit(1) } diff --git a/snippets/Loading/Back-Forward-Cache.js b/snippets/Loading/Back-Forward-Cache.js index 271e00c..0aec038 100644 --- a/snippets/Loading/Back-Forward-Cache.js +++ b/snippets/Loading/Back-Forward-Cache.js @@ -412,7 +412,7 @@ // Initialize checkRestoration(); - const eligibility = testEligibility(); + testEligibility(); // Display after a short delay to ensure pageshow event fires setTimeout(() => { diff --git a/snippets/Loading/CSS-Media-Queries-Analysis.js b/snippets/Loading/CSS-Media-Queries-Analysis.js index 81c114c..57dfe6d 100644 --- a/snippets/Loading/CSS-Media-Queries-Analysis.js +++ b/snippets/Loading/CSS-Media-Queries-Analysis.js @@ -142,14 +142,14 @@ async function analyzeCSSMediaQueries(minWidth = 768) { } }); } - } catch (e) { + } catch { // If CORS blocked, try to fetch the CSS if (sheet.href) { try { const response = await fetch(sheet.href); const cssText = await response.text(); parseMediaQueriesFromCSS(cssText, sheet.href, false); - } catch (fetchError) { + } catch { // Silently count CORS blocked files corsBlockedCount++; } @@ -485,6 +485,8 @@ async function analyzeCSSPerformanceImpact(minWidth = 768) { }; } +window.analyzeCSSPerformanceImpact = analyzeCSSPerformanceImpact; + // Run with default breakpoint (768px) (async () => { const result = await analyzeCSSMediaQueries(); diff --git a/snippets/Loading/Client-Side-Redirect-Detection.js b/snippets/Loading/Client-Side-Redirect-Detection.js index 86cdd12..2be467a 100644 --- a/snippets/Loading/Client-Side-Redirect-Detection.js +++ b/snippets/Loading/Client-Side-Redirect-Detection.js @@ -207,8 +207,6 @@ // Recommendations const hasDocumentNavigation = documentNavigations.length > 0; const hasSameOriginRedirect = sameOrigin && referrerPath !== currentPath && referrerPath !== ''; - const hasHighImpact = hasDocumentNavigation && documentNavigations.reduce((sum, nav) => sum + nav.duration, 0) > 1000; - if (hasDocumentNavigation || serverRedirects > 0) { console.log(''); console.log('%c💡 Recommendations:', 'font-weight: bold;'); diff --git a/snippets/Loading/Content-Visibility.js b/snippets/Loading/Content-Visibility.js index c87bc95..73195f6 100644 --- a/snippets/Loading/Content-Visibility.js +++ b/snippets/Loading/Content-Visibility.js @@ -36,7 +36,7 @@ function detectContentVisibility() { if (el.id) break; node = el.parentNode; } - } catch (err) { + } catch { // Do nothing... } return sel; @@ -188,7 +188,7 @@ function analyzeContentVisibilityOpportunities(options = {}) { if (el.id) break; node = el.parentNode; } - } catch (err) {} + } catch {} return sel; } @@ -271,7 +271,13 @@ function analyzeContentVisibilityOpportunities(options = {}) { console.log(""); // Show table without element reference - const tableData = opportunities.slice(0, 20).map(({ element, ...rest }) => rest); + const tableData = opportunities.slice(0, 20).map((opportunity) => ({ + selector: opportunity.selector, + height: opportunity.height, + distanceFromViewport: opportunity.distanceFromViewport, + childElements: opportunity.childElements, + estimatedSavings: opportunity.estimatedSavings, + })); console.table(tableData); if (opportunities.length > 20) { @@ -304,13 +310,21 @@ function analyzeContentVisibilityOpportunities(options = {}) { console.groupEnd(); return { - opportunities: opportunities.map(({ element, ...rest }) => rest), + opportunities: opportunities.map((opportunity) => ({ + selector: opportunity.selector, + height: opportunity.height, + distanceFromViewport: opportunity.distanceFromViewport, + childElements: opportunity.childElements, + estimatedSavings: opportunity.estimatedSavings, + })), totalElements: opportunities.length, highImpact: opportunities.filter((o) => o.estimatedSavings.startsWith("High")).length, elements: opportunities.map((o) => o.element), }; } +window.analyzeContentVisibilityOpportunities = analyzeContentVisibilityOpportunities; + // Run detection (() => { const cvResults = detectContentVisibility(); diff --git a/snippets/Loading/Critical-CSS-Detection.js b/snippets/Loading/Critical-CSS-Detection.js index ed039d0..ba6fc00 100644 --- a/snippets/Loading/Critical-CSS-Detection.js +++ b/snippets/Loading/Critical-CSS-Detection.js @@ -57,7 +57,7 @@ (s) => s.href === href ); if (sheet && sheet.cssRules) ruleCount = sheet.cssRules.length; - } catch (_) { + } catch { corsBlocked = true; } diff --git a/snippets/Loading/Find-Above-The-Fold-Lazy-Loaded-Images.js b/snippets/Loading/Find-Above-The-Fold-Lazy-Loaded-Images.js index 71490ac..1eb7583 100644 --- a/snippets/Loading/Find-Above-The-Fold-Lazy-Loaded-Images.js +++ b/snippets/Loading/Find-Above-The-Fold-Lazy-Loaded-Images.js @@ -19,7 +19,7 @@ if (el.id) break; node = el.parentNode; } - } catch (err) {} + } catch {} return sel; } @@ -54,7 +54,7 @@ if (entry && entry.transferSize) { return (entry.transferSize / 1024).toFixed(1) + " KB"; } - } catch (err) {} + } catch {} return null; } @@ -189,10 +189,19 @@ // Table of all problematic images console.group("📋 Lazy Loaded Images in Viewport"); - const tableData = results.lazyImages.map(({ element, area, position, ...rest }) => ({ - ...rest, - top: position.top + "px", - isLcpCandidate: rest.isLcpCandidate ? "⚠️ YES" : "no" + const tableData = results.lazyImages.map((image) => ({ + selector: image.selector, + lazyType: image.lazyType, + dimensions: image.dimensions, + top: image.position.top + "px", + distanceFromEdge: image.distanceFromEdge, + src: image.src, + srcset: image.srcset, + sizes: image.sizes, + alt: image.alt, + fetchPriority: image.fetchPriority, + fileSize: image.fileSize, + isLcpCandidate: image.isLcpCandidate ? "⚠️ YES" : "no", })); console.table(tableData); console.groupEnd(); @@ -233,7 +242,20 @@ withDataSrc: results.summary.withDataSrc, lcpAffected: results.summary.lcpAffected, }, - items: results.lazyImages.map(({ element, area, ...rest }) => rest), + items: results.lazyImages.map((image) => ({ + selector: image.selector, + lazyType: image.lazyType, + dimensions: image.dimensions, + position: image.position, + distanceFromEdge: image.distanceFromEdge, + src: image.src, + srcset: image.srcset, + sizes: image.sizes, + alt: image.alt, + fetchPriority: image.fetchPriority, + fileSize: image.fileSize, + isLcpCandidate: image.isLcpCandidate, + })), issues: [ ...(results.summary.lcpAffected ? [{ severity: "error", message: 'LCP candidate image has lazy loading — remove loading="lazy" and add fetchpriority="high"' }] : []), ...(results.lazyImages.filter(i => !i.isLcpCandidate).length > 0 ? [{ severity: "warning", message: `${results.lazyImages.filter(i => !i.isLcpCandidate).length} above-fold image(s) have lazy loading — remove loading="lazy"` }] : []), diff --git a/snippets/Loading/Find-Images-With-Lazy-and-Fetchpriority.js b/snippets/Loading/Find-Images-With-Lazy-and-Fetchpriority.js index c3b1fd4..b0b86f8 100644 --- a/snippets/Loading/Find-Images-With-Lazy-and-Fetchpriority.js +++ b/snippets/Loading/Find-Images-With-Lazy-and-Fetchpriority.js @@ -19,7 +19,7 @@ if (el.id) break; node = el.parentNode; } - } catch (err) {} + } catch {} return sel; } @@ -87,12 +87,18 @@ }); console.log("%c📋 Conflicting Elements:", "font-weight: bold;"); - console.table(tableData.map(({ element, ...rest }) => rest)); + console.table(tableData.map((item) => ({ + selector: item.selector, + dimensions: item.dimensions, + inViewport: item.inViewport, + isLcpCandidate: item.isLcpCandidate, + src: item.src, + }))); // Elements for inspection console.log(""); console.log("%c🔎 Elements for inspection:", "font-weight: bold;"); - tableData.forEach(({ element, selector, isLcpCandidate }, i) => { + tableData.forEach(({ element, isLcpCandidate }, i) => { const marker = isLcpCandidate === "⚠️ Yes" ? " 🚨 LCP" : ""; console.log(`${i + 1}.${marker}`, element); }); diff --git a/snippets/Loading/Find-non-Lazy-Loaded-Images-outside-of-the-viewport.js b/snippets/Loading/Find-non-Lazy-Loaded-Images-outside-of-the-viewport.js index a03c41c..880a08c 100644 --- a/snippets/Loading/Find-non-Lazy-Loaded-Images-outside-of-the-viewport.js +++ b/snippets/Loading/Find-non-Lazy-Loaded-Images-outside-of-the-viewport.js @@ -19,7 +19,7 @@ if (el.id) break; node = el.parentNode; } - } catch (err) {} + } catch {} return sel; } @@ -58,7 +58,7 @@ if (parent.matches(selector)) { return { hidden: true, reason: selector, container: getSelector(parent) }; } - } catch (e) {} + } catch {} } parent = parent.parentElement; } @@ -193,7 +193,14 @@ // Below the fold images if (results.belowFold.length > 0) { console.group(`📍 Below The Fold (${results.belowFold.length} images)`); - const tableData = results.belowFold.slice(0, 20).map(({ element, fullSrc, ...rest }) => rest); + const tableData = results.belowFold.slice(0, 20).map((img) => ({ + selector: img.selector, + src: img.src, + dimensions: img.dimensions, + size: img.size, + sizeFormatted: img.sizeFormatted, + distanceFromViewport: img.distanceFromViewport, + })); console.table(tableData); if (results.belowFold.length > 20) { console.log(`... and ${results.belowFold.length - 20} more images`); @@ -207,7 +214,15 @@ console.group(`🔒 In Hidden Containers (${results.hiddenContainers.length} images)`); console.log("Images in tabs, modals, carousels, or other hidden elements:"); console.log(""); - const tableData = results.hiddenContainers.slice(0, 15).map(({ element, fullSrc, distanceFromViewport, ...rest }) => rest); + const tableData = results.hiddenContainers.slice(0, 15).map((img) => ({ + selector: img.selector, + src: img.src, + dimensions: img.dimensions, + size: img.size, + sizeFormatted: img.sizeFormatted, + hiddenReason: img.hiddenReason, + container: img.container, + })); console.table(tableData); if (results.hiddenContainers.length > 15) { console.log(`... and ${results.hiddenContainers.length - 15} more images`); diff --git a/snippets/Loading/Fonts-Preloaded-Loaded-and-used-above-the-fold.js b/snippets/Loading/Fonts-Preloaded-Loaded-and-used-above-the-fold.js index 762096e..33976e1 100644 --- a/snippets/Loading/Fonts-Preloaded-Loaded-and-used-above-the-fold.js +++ b/snippets/Loading/Fonts-Preloaded-Loaded-and-used-above-the-fold.js @@ -103,10 +103,6 @@ normalizeFontFamily(f.name.replace(/\.(woff2?|ttf|otf|eot)$/i, "")) ); - const loadedFamilies = uniqueLoadedFonts.map((f) => - normalizeFontFamily(f.family) - ); - const usedFamilies = usedFonts.map((f) => normalizeFontFamily(f.family)); // Fonts preloaded but not used above the fold diff --git a/snippets/Loading/Inline-CSS-Info-and-Size.js b/snippets/Loading/Inline-CSS-Info-and-Size.js index 9eb2018..d4fdd6d 100644 --- a/snippets/Loading/Inline-CSS-Info-and-Size.js +++ b/snippets/Loading/Inline-CSS-Info-and-Size.js @@ -75,8 +75,8 @@ contentMap.get(normalized).push(s.index); }); const duplicates = Array.from(contentMap.entries()) - .filter(([_, indices]) => indices.length > 1) - .map(([_, indices]) => indices); + .filter(([, indices]) => indices.length > 1) + .map(([, indices]) => indices); // Rating based on total size let rating, ratingColor; diff --git a/snippets/Loading/Inline-Script-Info-and-Size.js b/snippets/Loading/Inline-Script-Info-and-Size.js index 8a68e59..a7c7158 100644 --- a/snippets/Loading/Inline-Script-Info-and-Size.js +++ b/snippets/Loading/Inline-Script-Info-and-Size.js @@ -105,8 +105,8 @@ } }); const duplicates = Array.from(contentMap.entries()) - .filter(([_, indices]) => indices.length > 1) - .map(([_, indices]) => indices); + .filter(([, indices]) => indices.length > 1) + .map(([, indices]) => indices); // Rating let rating, ratingColor; diff --git a/snippets/Loading/JS-Execution-Time-Breakdown.js b/snippets/Loading/JS-Execution-Time-Breakdown.js index 64c069e..87e018e 100644 --- a/snippets/Loading/JS-Execution-Time-Breakdown.js +++ b/snippets/Loading/JS-Execution-Time-Breakdown.js @@ -21,8 +21,6 @@ const domInteractive = nav?.domInteractive || 0; const domContentLoaded = nav?.domContentLoadedEventEnd || 0; const loadEvent = nav?.loadEventEnd || 0; - const fetchStart = nav?.fetchStart || 0; - // 2. Script resource timing (download phase) const scriptResources = performance .getEntriesByType("resource") diff --git a/snippets/Loading/Resource-Hints-Validation.js b/snippets/Loading/Resource-Hints-Validation.js index b1655d7..6046a6c 100644 --- a/snippets/Loading/Resource-Hints-Validation.js +++ b/snippets/Loading/Resource-Hints-Validation.js @@ -395,8 +395,8 @@ .filter(([domain, count]) => count >= 2 && !hintedDomains.has(domain)) .sort((a, b) => b[1] - a[1]); - const topPreconnects = missingHints.filter(([_, count]) => count >= 5).slice(0, 3); - const topDnsPrefetch = missingHints.filter(([_, count]) => count >= 2 && count < 5).slice(0, 5); + const topPreconnects = missingHints.filter(([, count]) => count >= 5).slice(0, 3); + const topDnsPrefetch = missingHints.filter(([, count]) => count >= 2 && count < 5).slice(0, 5); const shownHints = [...topPreconnects, ...topDnsPrefetch]; if (missingHints.length > 0) { diff --git a/snippets/Loading/SSR-Hydration-Data-Analysis.js b/snippets/Loading/SSR-Hydration-Data-Analysis.js index 4c7940c..3472b00 100644 --- a/snippets/Loading/SSR-Hydration-Data-Analysis.js +++ b/snippets/Loading/SSR-Hydration-Data-Analysis.js @@ -269,7 +269,7 @@ console.log(""); console.log("%c📦 Raw data object:", "font-weight: bold;"); console.log(data); - } catch (e) { + } catch { console.log("Could not parse JSON content"); } } diff --git a/snippets/Loading/Service-Worker-Analysis.js b/snippets/Loading/Service-Worker-Analysis.js index b72e436..3dbd26f 100644 --- a/snippets/Loading/Service-Worker-Analysis.js +++ b/snippets/Loading/Service-Worker-Analysis.js @@ -76,7 +76,7 @@ ' 💡 Enable with: registration.navigationPreload.enable()' ); } - } catch (_) { + } catch { // Access may be restricted } } @@ -183,7 +183,7 @@ } console.log(` Total entries: ${totalEntries}`); } - } catch (_) { + } catch { // Cross-origin restrictions may prevent cache access } } diff --git a/snippets/Loading/TTFB-Resources.js b/snippets/Loading/TTFB-Resources.js index 26f5869..63afabe 100644 --- a/snippets/Loading/TTFB-Resources.js +++ b/snippets/Loading/TTFB-Resources.js @@ -51,12 +51,12 @@ // Table (sorted by TTFB, slowest first) console.log(""); console.log("%cResources (sorted by TTFB, slowest first):", "font-weight: bold;"); - const tableData = resourcesData.slice(0, 25).map(({ fullUrl, ...rest }) => ({ - "TTFB (ms)": rest.ttfb.toFixed(0), - "Duration (ms)": rest.duration.toFixed(0), - Type: rest.type, - "3rd Party": rest.thirdParty ? "Yes" : "", - Resource: rest.resource, + const tableData = resourcesData.slice(0, 25).map((resource) => ({ + "TTFB (ms)": resource.ttfb.toFixed(0), + "Duration (ms)": resource.duration.toFixed(0), + Type: resource.type, + "3rd Party": resource.thirdParty ? "Yes" : "", + Resource: resource.resource, })); console.table(tableData); diff --git a/snippets/Loading/Validate-Preload-Async-Defer-Scripts.js b/snippets/Loading/Validate-Preload-Async-Defer-Scripts.js index fd86d53..3cd4ff4 100644 --- a/snippets/Loading/Validate-Preload-Async-Defer-Scripts.js +++ b/snippets/Loading/Validate-Preload-Async-Defer-Scripts.js @@ -95,17 +95,13 @@ // Determine natural priority let naturalPriority = "Medium/High"; - let executionPriority = "Low"; if (isAsync) { naturalPriority = "Lowest/Low"; - executionPriority = "High"; } else if (isDefer) { naturalPriority = "Lowest/Low"; - executionPriority = "VeryLow"; } else if (inHead) { naturalPriority = "Medium/High"; - executionPriority = "VeryHigh"; } // Check for anti-pattern: preload + async/defer From c8cfbd85d11e2988ffbe4b66bf4de79c91cf4df6 Mon Sep 17 00:00:00 2001 From: Joan Leon Date: Thu, 9 Apr 2026 15:38:44 +0200 Subject: [PATCH 2/2] remove IMPROVEMENT-PLAN.md from branch --- IMPROVEMENT-PLAN.md | 387 -------------------------------------------- 1 file changed, 387 deletions(-) delete mode 100644 IMPROVEMENT-PLAN.md diff --git a/IMPROVEMENT-PLAN.md b/IMPROVEMENT-PLAN.md deleted file mode 100644 index 28f9dba..0000000 --- a/IMPROVEMENT-PLAN.md +++ /dev/null @@ -1,387 +0,0 @@ -# WebPerf Snippets — Plan de Mejoras Consolidado - -> Fusión de análisis realizados el 2026-03-30 por Claude, Codex y Gemini CLI. -> Los ítems marcados con ★ son consenso entre al menos dos de los tres análisis. - ---- - -## Resumen ejecutivo - -El proyecto tiene una base sólida: 47 snippets bien organizados, documentación visual con diagramas Mermaid, soporte de Agent Skills y paridad entre fuentes, páginas y artefactos generados. Las mejoras se agrupan en cinco vectores: - -1. **Robustez** — Evitar inconsistencias entre fuentes y artefactos generados -2. **Consistencia editorial** — Homogeneizar la profundidad de cada página -3. **Descubribilidad** — Mejorar el onboarding y la navegación por contexto -4. **Expansión** — Nuevos snippets para APIs modernas -5. **Medios y visuales** — Hacer el output comprensible a primera vista - ---- - -## Fase 1 — Robustez y mantenibilidad ★ - -**Objetivo:** Reducir el riesgo de inconsistencias entre contenido fuente y artefactos generados. - -### 1.1 Script de consistencia ★ - -Crear `scripts/check-consistency.js` expuesto como: - -```json -"check:consistency": "node scripts/check-consistency.js" -``` - -Debe validar: -- Cada snippet tiene página MDX asociada (o excepción explícita) -- Cada página tiene snippet asociado (o está marcada como página editorial) -- `_meta.json` está alineado con los `.mdx` existentes -- `skills/` y `dist/` no están desactualizados respecto a `snippets/` y `pages/` -- Los conteos publicados en `README.md` y `SKILLS.md` no han quedado obsoletos - -### 1.2 Check de artefactos generados ★ - -```json -"generate-skills:check": "node scripts/generate-skills.js && git diff --exit-code -- skills dist" -``` - -Detecta si alguien modifica fuentes sin regenerar salidas. Corrige también la desalineación actual entre `package.json` (v1.2.0) y `skills/webperf/SKILL.md` (v1.1.0). - -### 1.3 Modelo explícito para páginas editoriales - -`Get-Your-Head-in-Order.mdx` es contenido editorial válido, pero no está formalizado. Opciones: - -- Frontmatter `type: guide` para páginas sin snippet asociado -- Lista de excepciones explícitas en el check de consistencia - -Recomendación: usar frontmatter — es autodocumentado y legible por automatizaciones. - -### 1.4 CI para pull requests ★ - -Workflow de GitHub Actions en cada PR: - -```yaml -- npm ci -- npm run lint -- npm run build -- npm run check:consistency -- npm run generate-skills:check -``` - -Es la mejora con mejor retorno inmediato: evita que errores de consistencia lleguen a `main`. - ---- - -## Fase 2 — Documentación y experiencia de contribución ★ - -**Objetivo:** Reducir la curva de entrada para nuevos contribuidores y consumidores. - -### 2.1 Plantilla estándar por página ★ - -Esqueleto mínimo para cada snippet: - -``` -1. Descripción breve (2-3 párrafos) -2. Tabla de umbrales (Good / Needs Improvement / Poor) — donde aplique -3. Diagrama Mermaid (flujo o secuencia) -4. Snippet ejecutable con botón de copia -5. Explicación del output (qué significa cada campo) -6. Causas comunes y qué hacer si el resultado es malo -7. Ejemplo de integración RUM (al menos 1) -8. Tabla de compatibilidad de navegadores -9. Further Reading -``` - -Páginas prioritarias a enriquecer: - -| Página | Qué le falta | -|--------|-------------| -| `LCP-Trail.mdx` | Tabla de campos, diagrama, sección de interpretación | -| `LCP.mdx` | Ejemplo de integración RUM | -| `FCP.mdx` | Diagrama de fases, tabla de causas comunes | -| `LongTask.mdx` | Migración FID→INP, comparativa con LoAF | -| `Scroll-Performance.mdx` | Tabla de umbrales, ejemplos de causas | -| `Content-Visibility.mdx` | Diagrama before/after, casos de uso | -| `Back-Forward-Cache.mdx` | Tabla de razones de fallo más comunes | - -### 2.2 Tabla de compatibilidad de APIs ★ - -En cada página, tabla de soporte real: - -| API | Chrome | Firefox | Safari | Edge | -|-----|--------|---------|--------|------| -| PerformanceObserver | ✅ 52 | ✅ 57 | ✅ 11 | ✅ 79 | -| Long Animation Frames | ✅ 123 | ❌ | ❌ | ✅ 123 | - -Actualmente solo `INP.mdx` tiene esta tabla. Evita frustración cuando un snippet no devuelve datos. - -### 2.3 Documento de arquitectura - -Crear `docs/ARCHITECTURE.md` con: - -- Flujo: fuente → docs → skills → dist -- Qué archivos son fuente de verdad vs. generados -- Cuándo ejecutar `generate-skills` -- Cómo se relacionan `pages/`, `snippets/`, `skills/` y `dist/` - -### 2.4 Guía de release - -Crear `docs/RELEASING.md`: - -- Cómo subir versión -- Cómo regenerar skills -- Cómo validar que `SKILL.md` usa la versión correcta -- Qué assets genera el workflow de release - -### 2.5 Plantilla para nuevos snippets ★ - -Archivos `templates/snippet.js` y `templates/snippet-page.mdx` con la estructura mínima esperada. Futuro comando: - -```json -"new:snippet": "node scripts/new-snippet.js" -``` - -Evita errores de naming, estructura y quality bar en cada aportación nueva. - -### 2.6 Páginas índice por categoría - -Añadir páginas de entrada por categoría: - -- `CoreWebVitals/index.mdx` — Diagrama relacional LCP / INP / CLS. Cuándo afecta cada uno al score de PageSpeed -- `Loading/index.mdx` — Jerarquía TTFB → FCP → LCP. Flujo de decisión: "¿qué medir primero?" -- `Interaction/index.mdx` — INP como métrica principal, LoAF como herramienta de diagnóstico -- `Media/index.mdx` — Impacto de imágenes y vídeos en LCP y CLS - -### 2.7 Guía de inicio rápido por perfil - -En `index.mdx` o como página separada: - -``` -¿Eres...? -├── Developer → LCP Sub-Parts + Resource Hints -├── Analista → Core Web Vitals + TTFB -├── DevOps → TTFB + Service Worker Analysis -└── Diseñador → CLS + Image Element Audit -``` - -### 2.8 Glosario técnico - -Crear `pages/glossary.mdx`: -BPP, LoAF, hadRecentInput, TAO, TTFB sub-parts, Core Web Vitals vs. Lighthouse metrics, RUM vs. Lab data. - ---- - -## Fase 3 — Medios y documentación visual ★ - -**Objetivo:** Hacer el output comprensible a primera vista, especialmente para usuarios que llegan desde buscadores o IA. - -### 3.1 Capturas de output en DevTools ★ - -Para los snippets principales (`LCP`, `CLS`, `INP`, `TTFB`, `FCP`, `Long-Animation-Frames`, `Image-Element-Audit`): - -- Captura del output en consola (imagen o GIF animado) -- Breve interpretación del resultado -- Ejemplo de output "bueno" vs. "problemático" - -Formato recomendado: imágenes en Cloudinary integradas con `CldImage`. La infraestructura ya existe. - -### 3.2 Sección RUM integration en páginas clave ★ - -Replicar el patrón de `INP.mdx` (GA4, DataDog, New Relic) en: -`LCP.mdx`, `CLS.mdx`, `TTFB.mdx`, `Long-Animation-Frames.mdx` - -### 3.3 Videos cortos por workflow ★ - -Priorizar vídeos de 30–90 segundos orientados a intención concreta: - -- "Cómo depurar TTFB lento" -- "Cómo detectar render-blocking resources" -- "Cómo investigar un LCP basado en imagen" -- "Cómo usar WebPerf Skills con un agente" -- "Cómo leer el output de INP" - -Un problema, una demo, una conclusión. La infraestructura Cloudinary ya está disponible. - -### 3.4 Playbooks / casos de uso reales - -Crear `pages/playbooks/`: - -- Auditar una landing lenta -- Depurar terceros que bloquean render -- Investigar mala experiencia móvil en red 3G -- Analizar una página Next.js con exceso de hydration data - -Conecta el catálogo técnico con problemas reales y mejora la descubribilidad desde buscadores. - -### 3.5 Botón de "Demo Live" - -Implementar páginas de prueba diseñadas para fallar en una métrica concreta (imagen pesada sin optimizar para LCP, shifts de layout para CLS) vinculadas desde su snippet. Los usuarios pueden abrir la página y ejecutar el snippet al instante. - ---- - -## Fase 4 — Tooling interno - -**Objetivo:** Reducir trabajo manual y convertir el repo en un sistema más autosuficiente. - -### 4.1 `scripts/new-snippet.js` - -Genera automáticamente: -- Archivo en `snippets//` -- Página en `pages//` -- Entrada en `_meta.json` - -### 4.2 Registro de snippets como fuente de verdad ★ - -Centralizar metadatos en `snippets-registry.json` o expandir `lib/snippets-registry.js` para que sea generado: -- Imports, títulos, descripciones base, URLs -- Genera `_meta.json` y tabla de contenidos en `README.md` -- Proporciona API interna para Agent Skills - -### 4.3 `scripts/docs-stats.js` - -Genera estadísticas del proyecto: -- Snippets por categoría -- Páginas editoriales vs. páginas con snippet -- Últimas incorporaciones -- Cobertura de tablas de compatibilidad y capturas - -Útil para refrescar README o una página "Project Stats". - -### 4.4 Smoke tests para scripts críticos - -Tests básicos de filesystem y outputs esperados para: -`generate-skills.js`, `install-skills.js`, `install-global.js`, `install-from-release.js`, futuro `check-consistency.js` - -### 4.5 Badges de salud en README - -- Última release -- Estado del CI -- Número de snippets -- Licencia - ---- - -## Fase 5 — Nuevos snippets - -**Objetivo:** Cubrir APIs modernas donde el proyecto puede ganar diferenciación. - -### 5.1 Core Web Vitals - -| Snippet | API clave | Chrome mín. | -|---------|-----------|-------------| -| `INP-Attribution.js` | PerformanceObserver + LoAF | 123 | -| `CLS-Source-Attribution.js` | LayoutShift attribution | 84 | -| `LCP-Initiator-Chain.js` | Resource Timing + LoAF | 123 | - -### 5.2 Loading ★ - -| Snippet | API clave | Chrome mín. | -|---------|-----------|-------------| -| `Speculation-Rules-Audit.js` ★ | document.ruleSets | 109 | -| `Fetch-Priority-Audit.js` | Resource Timing priority | 102 | -| `Early-Hints-Detection.js` ★ | Resource Timing | 103 | -| `Third-Party-Impact-Score.js` ★ | LoAF + Resource Timing | 123 | -| `HTTP-Cache-Audit.js` ★ | Resource Timing cache | 52 | -| `Compression-Audit.js` | Resource Timing encoding | 52 | -| `Module-Preload-Audit.js` | Resource Timing + ES modules | 66 | - -### 5.3 Interaction - -| Snippet | API clave | Chrome mín. | -|---------|-----------|-------------| -| `INP-Long-Tasks-Correlation.js` | LoAF + INP | 123 | -| `Pointer-Event-Latency.js` | Event Timing | 76 | -| `Soft-Navigation-Tracking.js` ★ | Soft Navigations API (exp.) | 123 | - -### 5.4 Media - -| Snippet | API clave | Chrome mín. | -|---------|-----------|-------------| -| `Image-Format-Audit.js` | Resource Timing + DOM | 52 | -| `Responsive-Images-Audit.js` | DOM inspection | 52 | -| `Font-Display-Audit.js` ★ | CSS OM + Resource Timing | 52 | -| `Font-Face-Detailed-Timing.js` ★ | FontFaceSet API | 35 | -| `Image-CDN-Policy-Audit.js` | Resource Timing + DOM | 52 | - -### 5.5 Nueva categoría: Security & Privacy Performance - -| Snippet | API clave | Chrome mín. | -|---------|-----------|-------------| -| `Permissions-Policy-Audit.js` | document.featurePolicy | 74 | -| `Cross-Origin-Isolation-Audit.js` | crossOriginIsolated | 87 | - -### 5.6 Nueva categoría: Resilience & Offline - -| Snippet | API clave | Chrome mín. | -|---------|-----------|-------------| -| `Service-Worker-Cache-Strategy.js` | SW Cache API | 40 | -| `Offline-Fallback-Audit.js` | SW + Fetch | 40 | - ---- - -## Fase 6 — Mejoras en snippets existentes - -| Script | Mejora | -|--------|--------| -| `TTFB.js` | Añadir parsing de `Server-Timing` header | -| `First-And-Third-Party-Script-Info.js` | Correlacionar con LoAF para indicar impacto en LCP/INP | -| `Resource-Hints.js` | Verificar si preloads y prefetches se usaron realmente | -| `Fonts-Preloaded-Loaded-and-used-above-the-fold.js` | Detectar font subsetting con `unicode-range` | -| `Long-Animation-Frames.js` | Mini timeline ASCII en consola para identificar patrones | - ---- - -## Fase 7 — Internacionalización - -Dado que el autor y parte de la comunidad de WebPerf es hispanohablante, traducir la documentación al español aportaría valor diferencial. Configurar Nextra para `/en/` y `/es/` y traducir guías y explicaciones de métricas en fases. - ---- - -## Priorización - -### Alta prioridad — impacto inmediato - -1. ★ Corregir desalineación de versión `package.json` vs `SKILL.md` -2. ★ `check-consistency.js` + `generate-skills:check` -3. ★ CI para pull requests (lint + build + consistency + artifacts) -4. ★ Capturas de output en consola para los 7 snippets principales -5. ★ Enriquecer páginas con poco contenido (LCP-Trail, FCP, LongTask, Scroll-Performance) -6. ★ `Speculation-Rules-Audit.js` — API moderna, alta demanda en 2025-2026 -7. ★ `Third-Party-Impact-Score.js` — pregunta más frecuente en auditorías - -### Media prioridad — mejora la calidad global - -8. Páginas índice por categoría -9. ★ RUM integration en LCP, CLS, TTFB -10. ★ `HTTP-Cache-Audit.js` y `Fetch-Priority-Audit.js` -11. ★ Plantilla para nuevos snippets + `new-snippet.js` -12. `docs/ARCHITECTURE.md` y `docs/RELEASING.md` -13. ★ Tabla de compatibilidad de APIs en cada página -14. Glosario técnico -15. Guía de inicio rápido por perfil - -### Baja prioridad — madurez del proyecto - -16. Testing de snippets (smoke tests + Playwright) -17. Nueva categoría Security & Privacy Performance -18. Nueva categoría Resilience & Offline -19. Videos cortos por workflow -20. Playbooks de casos de uso reales -21. Botón de "Demo Live" -22. Internacionalización (español) -23. Snippet output schema (TypeScript types) - ---- - -## Quick wins muy concretos - -Acciones de menos de 1 hora cada una: - -- [ ] Corregir versión en `skills/webperf/SKILL.md` (1.1.0 → 1.2.0) -- [ ] Añadir frontmatter `type: guide` a `Get-Your-Head-in-Order.mdx` -- [ ] Añadir `npm run check:consistency` al script section de `package.json` -- [ ] Añadir sección "Start here" en `README.md` -- [ ] Añadir badges (release, CI status, snippet count) en `README.md` -- [ ] Añadir tabla de compatibilidad a `LCP.mdx`, `CLS.mdx`, `TTFB.mdx` -- [ ] Añadir al menos 3 capturas de consola en snippets clave - ---- - -*Fusión consolidada — 2026-04-05*