Compétence Vidéo Hero pour Page d'Atterrissage
Guide pour intégrer efficacement des vidéos de démonstration de produit dans les pages d'atterrissage et sites web.
Motifs pris en charge
- Hero Background Video — Vidéo pleine page en arrière-plan dans l'en-tête
- Product Demo Section — Vidéo en ligne dans la section de présentation du produit
- Floating Device Mockup — Vidéo affichée dans un cadre d'appareil
- Lightbox / Modal Video — Lecture agrandie au clic
Étape 0 : Liste de vérification préalable
Avant d'intégrer la vidéo, vérifier les points suivants :
Vérification du matériel vidéo
- Le fichier vidéo est-il prêt ? (Sinon, créer d'abord une UI avec un espace réservé)
- Formats :
.mp4(H.264) +.webm(VP9), idéalement les deux - Résolution : 1920x1080 recommandé pour le Hero, 1280x720 suffisant pour les cartes
- Taille du fichier : 5-15 MB pour le Hero, 2-5 MB pour les cartes
- Durée : 10-30 secondes en boucle pour la lecture automatique
Création du matériel vidéo (avec Screen Studio ou autre)
- Enregistrer le flux d'opérations le plus impressionnant du produit en 15-30 secondes
- Enregistrer en résolution 1920x1080 ou plus
- À l'exportation, configurer :
- MP4 (H.264) : Qualité Moyenne-Haute, débit 4-8 Mbps
- WebM (VP9) : Exportation supplémentaire à qualité équivalente (si l'outil le permet)
- Commandes de conversion avec ffmpeg :
# Optimisation MP4 (Web, avec faststart)
ffmpeg -i input.mov -c:v libx264 -crf 23 -preset slow \
-movflags +faststart -an -vf scale=1920:-2 output.mp4
# Conversion WebM
ffmpeg -i input.mov -c:v libvpx-vp9 -crf 30 -b:v 0 \
-an -vf scale=1920:-2 output.webm
# Pour les cartes (plus petit)
ffmpeg -i input.mov -c:v libx264 -crf 26 -preset slow \
-movflags +faststart -an -vf scale=1280:-2 output-card.mp4
Hébergement
- Local (
/public/videos/): Pour les petits sites, déploiements Vercel / Netlify - CDN / Stockage objet: Pour les grands sites avec plusieurs vidéos (Cloudflare R2, AWS S3 + CloudFront)
- Intégration YouTube / Vimeo: Pour le SEO, si vous souhaitez partager en externe
Pattern 1 : Vidéo d'Arrière-plan Hero
Affiche une vidéo de démonstration du produit en plein écran en haut de la page. Avec superposition de texte.
Composant React
interface HeroVideoProps {
/** Chemin de la vidéo MP4 */
videoSrc: string;
/** Chemin de la vidéo WebM (optionnel) */
videoSrcWebm?: string;
/** Image d'affiche à afficher avant le chargement de la vidéo */
posterSrc?: string;
/** Éléments enfants de la superposition (titre, etc.) */
children?: React.ReactNode;
/** Opacité de la superposition (0-1, défaut 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">
{/* Vidéo d'arrière-plan */}
<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>
{/* Superposition sombre */}
<div
className="absolute inset-0"
style={{ backgroundColor: `rgba(0, 0, 0, ${overlayOpacity})` }}
/>
{/* Dégradé vers l'arrière-plan de la page */}
<div className="absolute inset-x-0 bottom-0 h-32 bg-gradient-to-t from-background to-transparent" />
{/* Contenu */}
{children && (
<div className="absolute inset-0 z-10 flex items-end">
<div className="container pb-16 md:pb-24">{children}</div>
</div>
)}
</div>
);
}
Exemple d'utilisation
<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">
Réinventer la conversation avec la technologie.
</h1>
</HeroVideo>
Zone d'application dans la LP Plural Reality
En remplacement du composant LifeGameHero actuel, ou en coexistence :
// Home.tsx motif de remplacement
// Option A : Remplacement complet
<HeroVideo videoSrc="/videos/hero-demo.mp4" posterSrc="/images/IMG_5298.jpeg">
<h1 ...>{h.heroTitle}</h1>
</HeroVideo>
// Option B : Conserver LifeGameHero et ajouter une section vidéo en dessous
<LifeGameHero>...</LifeGameHero>
<Section className="py-0">
<HeroVideo videoSrc="/videos/product-overview.mp4" overlayOpacity={0.2}>
<h2>Présentation du produit</h2>
</HeroVideo>
</Section>
Pattern 2 : Vidéo de Carte Produit
Dans chaque carte d'une liste de produits, une vidéo de démonstration est lue au survol, remplaçant une image statique.
Composant 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>
);
}
Exemple d'utilisation (remplacer l'image d'une carte existante)
// Avant :
<ParallaxImage src="/images/kouchoai.png" alt={t.products.kouchouAI} />
// Après :
<ProductCardVideo
videoSrc="/videos/kouchou-ai-demo.mp4"
posterSrc="/images/kouchoai.png"
alt={t.products.kouchouAI}
/>
Pattern 3 : Vidéo dans un Maquette d'Appareil
Affiche une vidéo de démonstration dans le cadre d'un navigateur ou d'un smartphone (pour renforcer la crédibilité).
function BrowserMockupVideo({
videoSrc,
posterSrc,
title,
}: {
videoSrc: string;
posterSrc?: string;
title?: string;
}) {
return (
<div className="border border-border bg-background overflow-hidden">
{/* Chrome du navigateur */}
<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>
{/* Contenu vidéo */}
<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>
);
}
Pattern 4 : Vidéo en Lightbox Modale
Affiche une vidéo dans une modale au clic sur une vignette. Prend également en charge l'intégration 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" />
{/* Superposition du bouton de lecture */}
<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>
{/* Modale */}
{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>
)}
</>
);
}
Liste de vérification de mise en œuvre
Lors de l'ajout d'une vidéo, vérifier les points suivants :
Performance
- Définir les 4 attributs
autoPlay muted loop playsInline(obligatoire pour iOS) - Spécifier une image
posterpour l'image de la première image (améliore l'expérience pendant le chargement) - Utiliser
preload="metadata"pour alléger le chargement initial des vidéos dans les cartes - Appliquer
-movflags +faststartau fichier vidéo (lecture progressive) - Fournir les deux formats MP4 + WebM via
<source>(compatibilité navigateur)
Responsive
- Utiliser
object-cover/object-containpour un ajustement approprié - Envisager d'afficher une image statique à la place de la vidéo sur mobile
const isMobile = window.innerWidth < 768; // Sur mobile, afficher uniquement l'image poster, ne pas charger la vidéo
Accessibilité
- Les vidéos en lecture automatique doivent impérativement être
muted(la lecture audio sans interaction utilisateur est bloquée) - Prendre en charge
prefers-reduced-motionet revenir à une image statique lorsque les animations sont désactivées@media (prefers-reduced-motion: reduce) { video { display: none; } .video-poster-fallback { display: block; } }
Design (Conformité Plural Reality)
- Pas de coins arrondis (
border-radius: 0px) - Bordure :
1px solid var(--border) - Superposition en dégradé noir (garder le monochromatique)
- Icône du bouton de lecture : utiliser
Playde Lucide React
Règles de placement des fichiers
client/
public/
videos/
hero-demo.mp4 # Vidéo Hero (MP4)
hero-demo.webm # Vidéo Hero (WebM)
hero-poster.jpg # Image d'affiche de la vidéo Hero
kouchou-ai-demo.mp4 # Vidéo de démonstration Kouchou AI
baisoku-survey-demo.mp4 # Vidéo de démonstration Baisoku Survey
baisoku-kaigi-demo.mp4 # Vidéo de démonstration Baisoku Kaigi
src/
components/
HeroVideo.tsx # Composant vidéo pleine page Hero
ProductCardVideo.tsx # Composant vidéo avec lecture au survol dans une carte
BrowserMockupVideo.tsx # Vidéo avec cadre de navigateur
VideoLightbox.tsx # Composant vidéo modale
Exemples de configurations courantes
SaaS / Page d'atterrissage produit (configuration recommandée)
Vidéo pleine page Hero (boucle de 15 à 30 secondes, lecture automatique muette)
↓
Section de texte (mission, valeurs)
↓
Vidéo maquette de navigateur (démo du produit principal)
↓
Cartes produit (lecture de vidéos de démonstration individuelles au survol)
↓
Études de cas / Réalisations
↓
CTA
Site corporatif (configuration sobre)
Image Hero + bouton de lecture (clic pour ouvrir la vidéo en lightbox)
↓
Section de texte
↓
Cartes produit (image statique, vidéo sur la page de détail)
Pattern 5 : Maquette UI animée en CSS (recommandé)
Sans fichier vidéo, dessiner directement la sensation d'utilisation du produit avec des animations CSS / SVG / Canvas. C'est l'approche adoptée par Stripe, Linear, Vercel.
Avantages
- Aucun fichier vidéo nécessaire (zéro octet ajouté)
- Entièrement responsive (ne se casse à aucune taille d'écran)
- Rendu natif net (pas d'artefacts de compression)
- Peut être interactif (changement au survol, au clic)
- Compatible automatiquement avec les modes sombre et clair
Prérequis : Actifs d'animation existants
La LP Plural Reality a déjà implémenté les animations de haute qualité suivantes dans SolutionAnimations.tsx :
| Composant | Contenu | Technologie |
|---|---|---|
NetworkOntologyAnimation | Ontologie à 3 couches (DATA→ONTOLOGY→APPLICATION) | SVG + framer-motion |
DataFlowAnimation | Pipeline collecter → organiser → connecter | SVG + framer-motion |
ClusterAnimation | Visualisation du clustering d'opinions | SVG + framer-motion |
ConversationBranchAnimation | Arbre de branches d'entretien approfondi | SVG + framer-motion |
FeedbackLoopAnimation | Cycle de conversation continue | SVG + framer-motion |
IntegrationMeshAnimation | Diagramme d'intégration de plateforme | SVG + framer-motion |
ParticleFieldAnimation | Réseau de particules | Canvas |
IsometricLayersAnimation | Structure de couches en projection isométrique | SVG + framer-motion |
De plus, une animation Canvas de la Terre → du Japon se trouve dans LifeGameHero.tsx.
Principes de conception communs
- Fond : noir (#000000), dessin en éléments blancs
- Monochromatique (ne pas utiliser de couleur ou seulement Brand Teal comme accent)
- Police : var(--font-mono) pour les étiquettes techniques, var(--font-sans) pour les éléments UI
- Pas de coins arrondis : rx={0} / border-radius: 0px
- Animation : useInView + motion.* de framer-motion pour un déclenchement au scroll
- Fond de grille : motif de grille à lignes blanches fines (utilitaire GridBg)
- Lueur : feDropShadow pour une émission lumineuse discrète (utilitaire GlowDef)
Pattern 5A : Cadre de navigateur + UI animée
Affiche une UI maquette animée à l'intérieur du chrome d'un navigateur (barre de titre + points).
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 du navigateur */}
<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>
{/* Contenu animé */}
<div className="aspect-video relative overflow-hidden">
{children}
</div>
</div>
);
}
Pattern 5B : Animation de démonstration KouchouAI
Flux où les données textuelles entrent → L'IA effectue le clustering → Le résultat est affiché.
// Conception conceptuelle (se référer à ClusterAnimation de SolutionAnimations lors de l'implémentation)
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">
{/* Phase 1 : Les lignes de texte glissent depuis la gauche */}
{phase === "input" && (
<g>
{["Je souffre des transports peu pratiques", "J'aimerais plus de soutien à l'éducation des enfants", "Je m'inquiète des mesures anti-catastrophe"].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>
)}
{/* Phase 2 : Les particules convergent vers le centre */}
{phase === "processing" && (
<motion.text x={400} y={225} textAnchor="middle" fill="white" fontSize={12} fontFamily="var(--font-mono)"
initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ duration: 0.5 }}>
Traitement en cours...
</motion.text>
)}
</svg>
</ProductDemoMock>
);
}