CULTIVA IA — Web Performance

Auditoría Core Web Vitals — Velozito

URL: https://app.velozito.com
Stack: Next.js 13 · Tailwind · Vercel
Dispositivo: Móvil 4G simulado
Fecha: 14 jun 2026
Herramienta: Chrome DevTools MCP + Lighthouse
34
LIGHTHOUSE MOBILE
▲ potencial: 82+
67%
URLS CON PROBLEMAS CWV
CrUX — Search Console
5
Issues Críticos
~2.7s
ahorrable
en LCP con los fixes
🎯
82+
Score objetivo alcanzable
📦
1.4MB
JS transferido (sin comprimir)
📊
1. Core Web Vitals — Resumen
LCP
5.2s
✖ Deficiente
Objetivo: < 2.5s · Umbral: 4s
INP
380ms
✖ Necesita mejora
Objetivo: < 200ms · Umbral: 500ms
CLS
0.19
⚠ Necesita mejora
Objetivo: < 0.1 · Umbral: 0.25
TTFB
1.9s
✖ Deficiente
Objetivo: < 800ms · Umbral: 1.8s
FCP
3.4s
✖ Deficiente
Objetivo: < 1.8s · Umbral: 3s
TBT
780ms
✖ Deficiente
Objetivo: < 200ms · Umbral: 600ms
Speed Index
6.1s
✖ Deficiente
Objetivo: < 3.4s · Umbral: 5.8s
Lighthouse Desktop
61
⚡ Aceptable
Brecha mobile/desktop: 27 pts
🔥
2. Top Issues — Causas Raíz
1
Hero image no optimizada — 1.8MB PNG sin dimensiones IMPACTO ALTO LCP CLS
El elemento LCP es hero-dashboard.png (1.8MB, 3200×2000px). Se sirve sin atributos width/height, provoca layout shift al cargar (CLS +0.12), y no usa WebP ni AVIF. Sin fetchpriority="high" ni preload, el navegador la descubre tarde en el ciclo de render.
Ahorro estimado: −2.1s en LCP · −0.12 en CLS
2
Google Tag Manager + Hotjar síncronos en <head> IMPACTO ALTO FCP TBT
GTM (gtm.js 143KB) y Hotjar (hotjar.js 89KB) se cargan como recursos render-blocking desde <head>. Bloquean el primer pintado 620ms adicionales. Hotjar además activa un MutationObserver que contribuye +180ms al TBT.
Ahorro estimado: −620ms en FCP · −200ms en TBT
3
Bundle JS monolítico — sin code splitting dinámico IMPACTO ALTO TBT INP
El chunk principal de Next.js incluye chart.js (350KB), @fullcalendar/react (210KB) y lodash completo (71KB) — todos usados solo en páginas internas del dashboard, no en la landing. El main thread queda bloqueado parseando 631KB de JS innecesario.
Ahorro estimado: −380ms en TBT · −1.4MB transferidos
4
Fuentes Google Fonts sin font-display ni preconnect efectivo IMPACTO MEDIO FCP CLS
Inter y Manrope se cargan con font-display: auto (por defecto), causando FOIT de hasta 3s. Hay un <link rel="preconnect"> a fonts.googleapis.com pero falta el preconnect a fonts.gstatic.com (origen real de los .woff2). Cadena de red: Document → CSS de Google Fonts → .woff2 (3 saltos).
Ahorro estimado: −0.07 en CLS por font swap · −280ms en FCP
5
Sin caché eficiente en assets estáticos (Vercel default headers) IMPACTO MEDIO TTFB
JS y CSS chunks de Next.js tienen Cache-Control: public, max-age=0, must-revalidate (Vercel default sin fingerprinting configurado). En revisitas, el navegador revalida cada asset con 304. Configurar max-age=31536000, immutable en chunks hasheados eliminaría estas peticiones.
Ahorro estimado: −310ms en carga repetida (visitas de retorno)
🌐
3. Análisis de Red — Recursos Críticos
Recurso Tipo Tamaño Carga Cache-Control Estado
hero-dashboard.png Image 1.8MB 1320ms no-cache LCP bloqueante
gtm.js (Google Tag Manager) Script 143KB 890ms max-age=0 RENDER BLOCKING
hotjar.js Script 89KB 740ms no-store RENDER BLOCKING
_next/static/chunks/main-app.js Script 1.4MB 2100ms max-age=0, must-revalidate SIN CODE SPLIT
fonts.googleapis.com/css2?... Stylesheet 4KB 320ms max-age=86400 FOIT RISK
Inter-Regular.woff2 Font 92KB 680ms (tarde) max-age=31536000 SIN PRELOAD
screenshot-carousel-1.jpg Image 340KB 820ms no-cache DIFERIBLE
_next/static/css/styles.css Stylesheet 210KB 410ms max-age=0 TAILWIND SIN PURGE
4. Recomendaciones con Código
🖼️ Fix #1 — Optimizar hero image con next/image + fetchpriority Esfuerzo: bajo
Reemplaza el <img> estático por el componente next/image con priority, añade dimensiones explícitas y convierte a WebP/AVIF (Next.js lo hace automáticamente). Esto elimina el CLS de la imagen y activa fetchpriority="high" en el LCP element.
// Antes (app/page.tsx)
<img src="/hero-dashboard.png" className="w-full" />

// Después
import Image from 'next/image'

<Image
  src="/hero-dashboard.png"
  alt="Dashboard de pedidos Velozito"
  width={1600}
  height={1000}
  priority        // fetchpriority="high" + preload automático
  placeholder="blur"   // Evita el FOUC mientras carga
  sizes="(max-width: 768px) 100vw, 50vw"
/>

// next.config.mjs — habilitar AVIF
export default {
  images: {
    formats: ['image/avif', 'image/webp'],
    minimumCacheTTL: 31536000,
  }
}
📜 Fix #2 — Mover GTM y Hotjar a after-interactive Esfuerzo: bajo
Usar el componente next/script con strategy="afterInteractive" descarga los scripts de analytics fuera del camino crítico. El render ya habrá ocurrido antes de que se ejecuten.
import Script from 'next/script'

// app/layout.tsx
<Script
  src=`https://www.googletagmanager.com/gtm.js?id=GTM-XXXX`
  strategy="afterInteractive"
/>

<Script
  id="hotjar"
  strategy="afterInteractive"
>
  {`(function(h,o,t,j,a,r){ h.hj=h.hj||function(){...}; })(window,...)`}
</Script>

// Impacto: −620ms FCP, −180ms TBT
Fix #3 — Dynamic imports para librerías del dashboard Esfuerzo: medio
Las librerías pesadas (Chart.js, FullCalendar, Lodash) solo se usan en rutas autenticadas. Importarlas dinámicamente evita que entren en el bundle de la landing.
// Antes (en app/page.tsx — se incluye en el bundle público)
import { Chart } from 'chart.js'      // 350KB
import Calendar from '@fullcalendar/react'  // 210KB
import _ from 'lodash'               // 71KB

// Después (en app/dashboard/[...]/page.tsx)
const Chart = dynamic(() => import('chart.js').then(m => m.Chart), {
  ssr: false, loading: () => <ChartSkeleton />
})
const Calendar = dynamic(() => import('@fullcalendar/react'), { ssr: false })

// Para lodash: importar solo las funciones usadas
import groupBy from 'lodash/groupBy'  // 5KB vs 71KB
🔤 Fix #4 — Auto-alojar fuentes con font-display: swap Esfuerzo: medio
Next.js 13+ incluye next/font que auto-aloja Google Fonts en Vercel Edge, eliminando la cadena de red de 3 saltos y aplicando font-display: swap automáticamente.
// app/layout.tsx
import { Inter, Manrope } from 'next/font/google'

const inter = Inter({
  subsets: ['latin'],
  display: 'swap',    // Evita FOIT
  preload: true,
  variable: '--font-inter',
})

const manrope = Manrope({
  subsets: ['latin'],
  display: 'swap',
  variable: '--font-manrope',
  weight: ['400', '600', '700'], // Solo los weights usados
})

// Eliminar estos tags del <head>:
// <link href="https://fonts.googleapis.com/css2?..." />
// <link rel="preconnect" href="https://fonts.gstatic.com" />
🗜️ Fix #5 — PurgeCSS en producción + caché inmutable en chunks Esfuerzo: bajo
Tailwind en producción requiere configurar el campo content para tree-shake clases no usadas. Además, los chunks de Next.js con hash en el nombre deben ser inmutables. Añadir vercel.json con headers de caché.
// tailwind.config.ts — Fix PurgeCSS
export default {
  content: [
    './app/**/*.{js,ts,jsx,tsx,mdx}',
    './components/**/*.{js,ts,jsx,tsx}',
  ],
  // Sin esto, Tailwind incluye ~4MB de CSS en dev-mode
}

// vercel.json — Cache-Control inmutable para chunks hasheados
{
  "headers": [{
    "source": "/_next/static/(.*)",
    "headers": [{
      "key": "Cache-Control",
      "value": "public, max-age=31536000, immutable"
    }]
  }]
}
5. Accesibilidad — Gaps Detectados
🎨
Contraste insuficiente en CTAs
Botón primario (#6366f1 sobre #fff): ratio 3.2:1 < 4.5:1 WCAG AA para texto normal.
✖ FALLO WCAG AA
🖱️
Sin indicador de foco visible
CSS global: *:focus { outline: none } elimina el anillo de foco en todos los elementos interactivos.
✖ FALLO WCAG 2.4.7
🏷️
Imágenes sin alt text
4 imágenes en el carrusel de screenshots tienen alt="" vacío o ausente.
⚠ MEJORA RECOMENDADA
📱
Target táctil < 44×44px
Íconos de redes sociales en footer: 24×24px. iOS/Android requieren mínimo 44px por WCAG 2.5.5.
⚠ MEJORA RECOMENDADA
🗺️
6. Matriz de Priorización — Impacto vs Esfuerzo
Acción
Impacto LCP
Esfuerzo
Score Delta
Fix #1 — Hero image con next/image
−2.1s
Bajo (2h)
+22 pts
Fix #2 — Scripts de analytics afterInteractive
−620ms FCP
Bajo (1h)
+14 pts
Fix #3 — Dynamic imports dashboard libs
−380ms TBT
Medio (4h)
+10 pts
Fix #4 — Auto-hosting fuentes next/font
−280ms FCP
Bajo (1h)
+6 pts
Fix #5 — PurgeCSS + caché Vercel
Visitas repetidas
Bajo (2h)
+6 pts
🎯
Resultado estimado tras implementar los 5 fixes
Lighthouse Mobile: 34 → 82+  ·  LCP: 5.2s → ~2.0s  ·  TBT: 780ms → ~200ms  ·  CLS: 0.19 → 0.04
Esfuerzo total estimado: ~10 horas de desarrollo · Plazo recomendado: sprint de 2 días