Communitygithub.com

yuidvg/lp-hero-video

Skill para integrar demostraciones de producto en landing pages. Implementa patrones de demo como vídeo de héroe, vídeo en tarjetas, mockups de UI con animaciones CSS, y más. Ejemplos de disparo: 'pon un vídeo', 'vídeo de héroe', 'incrusta demo', 'hero video', 'product video', 'LP video', 'vídeo en landing page', 'producto demo', 'mostrar con animación', 'UI mock'

¿Qué es lp-hero-video?

Este skill proporciona una guía completa para añadir vídeos de demostración de producto a landing pages y sitios web, cubriendo múltiples patrones de implementación: vídeo de fondo en héroe, secciones de demo de producto, mockups de dispositivos flotantes, vídeos en lightbox/modal, y animaciones CSS de UI. Es compatible con React y frameworks modernos, y ofrece componentes reutilizables con TypeScript. El skill incluye desde la preparación del material de vídeo (formatos, resoluciones, optimización con ffmpeg) hasta la implementación técnica detallada, con énfasis en rendimiento (carga progresiva, múltiples formatos), responsividad (fallback a imágenes en móvil), accesibilidad (respeto de prefers-reduced-motion) y diseño monocromático. También promueve el uso de animaciones CSS/SVG/Canvas como alternativa a vídeos reales, siguiendo el enfoque de empresas como Stripe, Linear y Vercel.

Compatible con~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/yuidvg/skills/tree/HEAD/skills/lp-hero-video

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Skill de Vídeo Héroe para LP

Guía para integrar vídeos de demostración de producto en landing pages y sitios web de forma efectiva.

Patrones compatibles

  1. Hero Background Video — Vídeo de fondo a pantalla completa en la parte superior de la página
  2. Product Demo Section — Vídeo en línea dentro de una sección de presentación del producto
  3. Floating Device Mockup — Vídeo mostrado dentro de un marco de dispositivo
  4. Lightbox / Modal Video — Reproducción ampliada al hacer clic

Paso 0: Lista de verificación previa

Antes de incorporar el vídeo, verifica lo siguiente:

Verificación del material de vídeo

  • ¿El archivo de vídeo está listo? (Si no, crear primero una interfaz de usuario con marcador de posición)
  • Formato: ideal tener dos formatos .mp4 (H.264) + .webm (VP9)
  • Resolución: para Hero se recomienda 1920x1080, para tarjetas 1280x720 es suficiente
  • Tamaño de archivo: para Hero entre 5-15 MB, para tarjetas entre 2-5 MB
  • Duración: para reproducción automática, lo óptimo es un bucle de 10-30 segundos

Cómo crear material de vídeo (grabando con Screen Studio, etc.)

  1. Graba el flujo de operación más impactante del producto en 15-30 segundos
  2. Graba con una resolución de 1920x1080 o superior
  3. Al exportar, configura:
    • MP4 (H.264): calidad Media-Alta, tasa de bits 4-8 Mbps
    • WebM (VP9): exportación adicional con calidad equivalente (si la herramienta lo permite)
  4. Comandos de conversión con ffmpeg:
# Optimización MP4 (para web, con faststart)
ffmpeg -i input.mov -c:v libx264 -crf 23 -preset slow \
  -movflags +faststart -an -vf scale=1920:-2 output.mp4

# Conversión WebM
ffmpeg -i input.mov -c:v libvpx-vp9 -crf 30 -b:v 0 \
  -an -vf scale=1920:-2 output.webm

# Para tarjetas (más pequeño)
ffmpeg -i input.mov -c:v libx264 -crf 26 -preset slow \
  -movflags +faststart -an -vf scale=1280:-2 output-card.mp4

Alojamiento

  • Local (/public/videos/): para sitios pequeños, despliegues en Vercel / Netlify
  • CDN / Almacenamiento de objetos: para sitios grandes o múltiples vídeos (Cloudflare R2, AWS S3 + CloudFront)
  • Incrustación de YouTube / Vimeo: para SEO, o si se desea compartir externamente

Patrón 1: Hero Background Video

Muestra un vídeo de demostración del producto a pantalla completa en la parte superior de la página. Con superposición de texto.

Componente React

interface HeroVideoProps {
  /** Ruta del vídeo MP4 */
  videoSrc: string;
  /** Ruta del vídeo WebM (opcional) */
  videoSrcWebm?: string;
  /** Imagen de póster para mostrar antes de cargar el vídeo */
  posterSrc?: string;
  /** Elementos hijos de la superposición (títulos, etc.) */
  children?: React.ReactNode;
  /** Oscuridad de la superposición (0-1, por defecto 0.4) */
  overlayOpacity?: number;
}

function HeroVideo({
  videoSrc,
  videoSrcWebm,
  posterSrc,
  children,
  overlayOpacity = 0.4,
}: HeroVideoProps) {
  const videoRef = useRef<HTMLVideoElement>(null);

  return (
    <div className="relative h-screen w-full overflow-hidden bg-black">
      {/* Vídeo de fondo */}
      <video
        ref={videoRef}
        autoPlay
        muted
        loop
        playsInline
        poster={posterSrc}
        className="absolute inset-0 w-full h-full object-cover"
      >
        {videoSrcWebm && <source src={videoSrcWebm} type="video/webm" />}
        <source src={videoSrc} type="video/mp4" />
      </video>

      {/* Superposición oscura */}
      <div
        className="absolute inset-0"
        style={{ backgroundColor: `rgba(0, 0, 0, ${overlayOpacity})` }}
      />

      {/* Degradado hacia el fondo de la página */}
      <div className="absolute inset-x-0 bottom-0 h-32 bg-gradient-to-t from-background to-transparent" />

      {/* Contenido */}
      {children && (
        <div className="absolute inset-0 z-10 flex items-end">
          <div className="container pb-16 md:pb-24">{children}</div>
        </div>
      )}
    </div>
  );
}

Ejemplo de uso

<HeroVideo
  videoSrc="/videos/product-demo.mp4"
  videoSrcWebm="/videos/product-demo.webm"
  posterSrc="/images/hero-poster.jpg"
  overlayOpacity={0.35}
>
  <h1 className="text-4xl md:text-6xl font-normal text-white tracking-tight">
    Redescubriendo la conversación con tecnología.
  </h1>
</HeroVideo>

Puntos de aplicación en Plural Reality LP

Sustituyendo o coexistiendo con el componente LifeGameHero actual:

// Home.tsx  patrón de sustitución
// Opción A: Sustitución completa
<HeroVideo videoSrc="/videos/hero-demo.mp4" posterSrc="/images/IMG_5298.jpeg">
  <h1 ...>{h.heroTitle}</h1>
</HeroVideo>

// Opción B: Mantener LifeGameHero y añadir sección de vídeo debajo
<LifeGameHero>...</LifeGameHero>
<Section className="py-0">
  <HeroVideo videoSrc="/videos/product-overview.mp4" overlayOpacity={0.2}>
    <h2>Introducción al producto</h2>
  </HeroVideo>
</Section>

Patrón 2: Product Card Video

En cada tarjeta de la lista de productos, reproduce un vídeo de demostración al pasar el ratón, en lugar de una imagen estática.

Componente React

interface ProductCardVideoProps {
  videoSrc: string;
  posterSrc: string;
  alt: string;
  className?: string;
}

function ProductCardVideo({
  videoSrc,
  posterSrc,
  alt,
  className,
}: ProductCardVideoProps) {
  const videoRef = useRef<HTMLVideoElement>(null);

  const handleMouseEnter = () => {
    videoRef.current?.play();
  };

  const handleMouseLeave = () => {
    const video = videoRef.current;
    if (video) {
      video.pause();
      video.currentTime = 0;
    }
  };

  return (
    <div
      className={cn("relative aspect-video bg-muted overflow-hidden", className)}
      onMouseEnter={handleMouseEnter}
      onMouseLeave={handleMouseLeave}
    >
      <video
        ref={videoRef}
        muted
        loop
        playsInline
        poster={posterSrc}
        preload="metadata"
        className="w-full h-full object-cover"
      >
        <source src={videoSrc} type="video/mp4" />
      </video>
    </div>
  );
}

Ejemplo de uso (sustituir imagen en tarjeta existente)

// Antes:
<ParallaxImage src="/images/kouchoai.png" alt={t.products.kouchouAI} />

// Después:
<ProductCardVideo
  videoSrc="/videos/kouchou-ai-demo.mp4"
  posterSrc="/images/kouchoai.png"
  alt={t.products.kouchouAI}
/>

Patrón 3: Device Mockup Video

Muestra el vídeo de demostración dentro del marco de un navegador o smartphone (aumenta la confianza).

function BrowserMockupVideo({
  videoSrc,
  posterSrc,
  title,
}: {
  videoSrc: string;
  posterSrc?: string;
  title?: string;
}) {
  return (
    <div className="border border-border bg-background overflow-hidden">
      {/* Chrome del navegador */}
      <div className="flex items-center gap-2 px-4 py-3 bg-secondary/30 border-b border-border">
        <div className="flex gap-1.5">
          <div className="w-3 h-3 rounded-full bg-muted-foreground/20" />
          <div className="w-3 h-3 rounded-full bg-muted-foreground/20" />
          <div className="w-3 h-3 rounded-full bg-muted-foreground/20" />
        </div>
        {title && (
          <div className="flex-1 text-center">
            <span className="text-xs text-muted-foreground font-mono">{title}</span>
          </div>
        )}
      </div>
      {/* Contenido del vídeo */}
      <div className="aspect-video">
        <video
          autoPlay
          muted
          loop
          playsInline
          poster={posterSrc}
          className="w-full h-full object-cover"
        >
          <source src={videoSrc} type="video/mp4" />
        </video>
      </div>
    </div>
  );
}

Patrón 4: Lightbox Modal Video

Muestra el vídeo en un modal al hacer clic en la miniatura. También compatible con incrustación de YouTube.

function VideoLightbox({
  thumbnailSrc,
  videoSrc,
  youtubeId,
  alt,
}: {
  thumbnailSrc: string;
  videoSrc?: string;
  youtubeId?: string;
  alt: string;
}) {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <>
      <button
        onClick={() => setIsOpen(true)}
        className="relative group cursor-pointer aspect-video w-full overflow-hidden"
      >
        <img src={thumbnailSrc} alt={alt} className="w-full h-full object-cover" />
        {/* Superposición del botón de reproducción */}
        <div className="absolute inset-0 flex items-center justify-center bg-black/20 group-hover:bg-black/30 transition-colors">
          <div className="w-16 h-16 flex items-center justify-center bg-white/90 group-hover:bg-white transition-colors">
            <svg className="w-6 h-6 text-foreground ml-1" fill="currentColor" viewBox="0 0 24 24">
              <path d="M8 5v14l11-7z" />
            </svg>
          </div>
        </div>
      </button>

      {/* Modal */}
      {isOpen && (
        <div
          className="fixed inset-0 z-50 flex items-center justify-center bg-black/80"
          onClick={() => setIsOpen(false)}
        >
          <div className="w-full max-w-5xl aspect-video" onClick={(e) => e.stopPropagation()}>
            {youtubeId ? (
              <iframe
                src={`https://www.youtube.com/embed/${youtubeId}?autoplay=1`}
                className="w-full h-full"
                allow="autoplay; encrypted-media"
                allowFullScreen
              />
            ) : (
              <video
                src={videoSrc}
                autoPlay
                controls
                className="w-full h-full object-contain bg-black"
              />
            )}
          </div>
        </div>
      )}
    </>
  );
}

Lista de verificación de implementación

Al añadir vídeos, verifica lo siguiente:

Rendimiento

  • Establecer los 4 atributos autoPlay muted loop playsInline (obligatorio para compatibilidad con iOS)
  • Especificar imagen de poster para el fotograma inicial (mejora la experiencia durante la carga)
  • Usar preload="metadata" para aligerar la carga inicial de vídeos en tarjetas
  • Aplicar -movflags +faststart al archivo de vídeo (reproducción progresiva)
  • Proporcionar los dos formatos MP4 + WebM con <source> (compatibilidad con navegadores)

Diseño responsive

  • Usar object-cover / object-contain para un ajuste adecuado
  • Considerar la opción de mostrar una imagen estática en lugar del vídeo en dispositivos móviles
    const isMobile = window.innerWidth < 768;
    // En móvil, mostrar solo la imagen de poster, no cargar el vídeo
    

Accesibilidad

  • Los vídeos de reproducción automática deben ir siempre con muted (la reproducción de audio sin interacción del usuario está bloqueada)
  • Respetar prefers-reduced-motion y mostrar una imagen estática como alternativa cuando las animaciones estén desactivadas
    @media (prefers-reduced-motion: reduce) {
      video { display: none; }
      .video-poster-fallback { display: block; }
    }
    

Diseño (conforme a Plural Reality)

  • Sin bordes redondeados (border-radius: 0px)
  • Borde: 1px solid var(--border)
  • Superposición con degradado negro (mantener monocromático)
  • El icono del botón de reproducción debe ser Play de Lucide React

Convención de ubicación de archivos

client/
  public/
    videos/
      hero-demo.mp4           # Vídeo de héroe (MP4)
      hero-demo.webm           # Vídeo de héroe (WebM)
      hero-poster.jpg           # Imagen de póster del vídeo de héroe
      kouchou-ai-demo.mp4      # Vídeo de demostración de Kouchou AI
      baisoku-survey-demo.mp4  # Vídeo de demostración de Baisoku Survey
      baisoku-kaigi-demo.mp4   # Vídeo de demostración de Baisoku Kaigi
  src/
    components/
      HeroVideo.tsx             # Componente de vídeo a pantalla completa de héroe
      ProductCardVideo.tsx      # Componente de vídeo con reproducción al pasar el ratón en tarjeta
      BrowserMockupVideo.tsx    # Vídeo con marco de navegador
      VideoLightbox.tsx         # Componente de vídeo modal

Ejemplos de configuración comunes

LP de SaaS / Producto (configuración recomendada)

Vídeo a pantalla completa de héroe (bucle de 15-30 segundos, reproducción automática silenciada)
  ↓
Sección de texto (misión, valores)
  ↓
Vídeo en mockup de navegador (demostración del producto principal)
  ↓
Tarjetas de producto (reproducción de vídeo individual al pasar el ratón)
  ↓
Casos de estudio / Logros
  ↓
CTA

Sitio corporativo (configuración comedida)

Imagen de héroe + botón de reproducción (clic para abrir vídeo en Lightbox)
  ↓
Sección de texto
  ↓
Tarjetas de producto (imagen estática, vídeo en página de detalle)

Patrón 5: Mockup de UI de producto con animaciones CSS (recomendado)

Sin necesidad de archivos de vídeo, dibuja directamente la sensación de interacción del producto con animaciones CSS / SVG / Canvas. Es el enfoque adoptado por Stripe, Linear y Vercel.

Ventajas

  • No requiere archivos de vídeo (cero bytes adicionales)
  • Completamente responsive (no se rompe en ningún tamaño de pantalla)
  • Renderizado nativo y nítido (sin artefactos de compresión)
  • Posible interacción (cambios al pasar el ratón, al hacer clic)
  • Compatibilidad automática con modo oscuro / modo claro

Requisito previo: Activos de animación existentes

Plural Reality LP ya tiene implementadas las siguientes animaciones de alta calidad en SolutionAnimations.tsx:

ComponenteContenidoTecnología
NetworkOntologyAnimationOntología de 3 capas (DATA→ONTOLOGY→APPLICATION)SVG + framer-motion
DataFlowAnimationPipeline de recoger→ordenar→conectarSVG + framer-motion
ClusterAnimationVisualización de agrupación de opinionesSVG + framer-motion
ConversationBranchAnimationÁrbol de ramificación de entrevistas en profundidadSVG + framer-motion
FeedbackLoopAnimationCiclo de diálogo continuoSVG + framer-motion
IntegrationMeshAnimationDiagrama de integración de plataformasSVG + framer-motion
ParticleFieldAnimationRed de partículasCanvas
IsometricLayersAnimationEstructura de capas en proyección isométricaSVG + framer-motion

Además, LifeGameHero.tsx contiene una animación en Canvas de la Tierra → Japón.

Principios de diseño comunes

- Fondo: negro (#000000), dibujar con elementos blancos
- Monocromático (no usar color, o solo Brand Teal como acento)
- Fuente: var(--font-mono) para etiquetas técnicas, var(--font-sans) para elementos de UI
- Sin bordes redondeados: rx={0} / border-radius: 0px
- Animación: usar useInView + motion.* de framer-motion para activación por scroll
- Fondo de cuadrícula: patrón de cuadrícula con líneas blancas tenues (utilidad GridBg)
- Resplandor: feDropShadow para un brillo sutil (utilidad GlowDef)

Patrón 5A: Marco de navegador + UI animada

Muestra una interfaz de usuario simulada con animación dentro del cromo de un navegador (barra de título + puntos).

function ProductDemoMock({
  title,
  children,
  className,
}: {
  title: string;
  children: React.ReactNode;
  className?: string;
}) {
  return (
    <div className={cn("border border-border bg-black overflow-hidden", className)}>
      {/* Chrome del navegador */}
      <div className="flex items-center gap-2 px-4 py-3 bg-[#1c1c1e] border-b border-border">
        <div className="flex gap-1.5">
          <div className="w-2.5 h-2.5 rounded-full bg-white/10" />
          <div className="w-2.5 h-2.5 rounded-full bg-white/10" />
          <div className="w-2.5 h-2.5 rounded-full bg-white/10" />
        </div>
        <div className="flex-1 text-center">
          <span className="text-[10px] text-white/40 font-mono">{title}</span>
        </div>
      </div>
      {/* Contenido animado */}
      <div className="aspect-video relative overflow-hidden">
        {children}
      </div>
    </div>
  );
}

Patrón 5B: Animación de demostración de Kouchou AI

Flujo donde los datos de texto entran → la IA los agrupa en clusters → se muestran los resultados.

// Diseño conceptual (para la implementación, consultar ClusterAnimation en SolutionAnimations)
function KouchouAIDemoAnimation() {
  const [phase, setPhase] = useState<"input" | "processing" | "result">("input");

  useEffect(() => {
    const timers = [
      setTimeout(() => setPhase("processing"), 2000),
      setTimeout(() => setPhase("result"), 4000),
    ];
    const loop = setInterval(() => {
      setPhase("input");
      timers.push(setTimeout(() => setPhase("processing"), 2000));
      timers.push(setTimeout(() => setPhase("result"), 4000));
    }, 8000);
    return () => { timers.forEach(clearTimeout); clearInterval(loop); };
  }, []);

  return (
    <ProductDemoMock title="kouchou-ai.plural-reality.com">
      <svg viewBox="0 0 800 450" className="w-full h-full">
        {/* Fase 1: Líneas de texto se deslizan desde la izquierda */}
        {phase === "input" && (
          <g>
            {["El transporte es incómodo", "Necesitamos más apoyo para la crianza", "Me preocupa la prevención de desastres"].map((text, i) => (
              <motion.g
                key={text}
                initial={{ opacity: 0, x: -40 }}
                animate={{ opacity: 1, x: 0 }}
                transition={{ delay: i * 0.4 }}
              >
                <rect x={40} y={60 + i * 50} width={350} height={36} fill="white" fillOpacity={0.05} stroke="white" strokeWidth={0.5} />
                <text x={56} y={82 + i * 50} fill="white" fontSize={11} fontFamily="var(--font-sans)" opacity={0.8}>
                  {text}
                </text>
              </motion.g>
            ))}
          </g>
        )}

        {/* Fase 2: Partículas se reúnen en el centro */}
        {phase === "processing" && (
          <motion.text x={400} y={225} textAnchor="middle" fill="white" fontSize={12} fontFamily="var(--font-mono)"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
          >
            Procesando...
          </motion.text>
        )}

        {/* Fase 3: Clústeres de resultados */}
        {phase === "result" && (
          <g>
            <motion.circle cx={200} cy={225} r={60} fill="white" fillOpacity={0.1} stroke="white" strokeWidth={0.5}
              initial={{ r: 0 }} animate={{ r: 60 }} transition={{ type: "spring", damping: 10 }}
            />
            <text x={200} y={225} textAnchor="middle" fill="white" fontSize={11} fontFamily="var(--font-sans)">
              Infraestructura
            </text>
            <motion.circle cx={400} cy={225} r={60} fill="white" fillOpacity={0.1} stroke="white" strokeWidth={0.5}
              initial={{ r: 0 }} animate={{ r: 60 }} transition={{ type: "spring", damping: 10, delay: 0.3 }}
            />
            <text x={400} y={225} textAnchor="middle" fill="white" fontSize={11} fontFamily="var(--font-sans)">
              Bienestar
            </text>
            <motion.circle cx={600} cy={225} r={60} fill="white" fillOpacity={0.1} stroke="white" strokeWidth={0.5}
              initial={{ r: 0 }} animate={{ r: 60 }} transition={{ type: "spring", damping: 10, delay: 0.6 }}
            />
            <text x={600} y={225} textAnchor="middle" fill="white" fontSize={11} fontFamily="var(--font-sans)">
              Seguridad
            </text>
          </g>
        )}
      </svg>
    </ProductDemoMock>
  );
}

Skills relacionados