Créer une section À propos avec intro équipe en React
Une section about-team-intro en React utilise une grille en deux colonnes : la colonne gauche contient un badge, un titre, une description et des lignes de points clés qui glissent depuis la gauche au scroll ; la colonne droite affiche une image carrée de l'équipe qui apparaît en fondu et léger scale. Les deux colonnes utilisent whileInView de Framer Motion avec once:true pour n'animer qu'une seule fois.
- Stack : React 18, Framer Motion 11, Lucide React, Tailwind v4. ~145 lignes, zéro dépendance runtime supplémentaire.
- Les icônes sont résolues dynamiquement depuis l'espace de noms lucide-react, tout nom d'icône Lucide peut être passé en prop string.
- Entièrement themable via des propriétés CSS custom (--color-accent, --color-background-alt, --radius-md, etc.), aucune couleur codée en dur.
- Accessible : section sémantique + h2, icônes décoratives (aria-hidden par défaut via Lucide), responsive jusqu'à 375px.
- Toutes les props sont optionnelles, badge, image et highlights peuvent être omis sans casser la mise en page.
About Team Intro est une section React en deux colonnes qui associe un bloc texte à une photo d'équipe pour donner un visage au produit. La colonne gauche porte badge, titre, paragraphe et une liste verticale de points clés illustrés par des icônes ; la colonne droite encadre une image carrée qui entre légèrement en scale au scroll. L'ensemble transmet confiance et culture sans surcharger cognitivement le visiteur.
Anatomie
Une section pleine largeur avec un conteneur centré max-w-6xl contient une grille CSS responsive (1 colonne sur mobile, 2 sur md+). La colonne gauche est une motion.div qui regroupe un badge pill optionnel, un titre h2, un paragraphe de description et une liste flex-col de lignes de points clés. Chaque ligne est sa propre motion.div avec un conteneur d'icône optionnel (carré arrondi 10×10 avec fond accent teinté) et une paire de textes (label en semibold + description en muted). La colonne droite est une motion.div carrée overflow:hidden qui affiche l'image d'équipe en background-image CSS.
Comment ça marche
Deux patterns d'entrée Framer Motion distincts tournent en parallèle. La colonne gauche utilise initial:{opacity:0, y:20} avec whileInView:{opacity:1, y:0}, une durée de 500ms et une marge viewport de -80px pour que l'animation se déclenche légèrement avant que l'élément entre dans le viewport. Chaque ligne de points clés ajoute un délai stagné de 100ms + (index × 80ms) et glisse depuis x:-12 au lieu de y, donnant à la liste un effet cascade. La colonne droite utilise initial:{opacity:0, scale:0.96} avec une courbe cubic-bezier [0.16, 1, 0.3, 1] sur 600ms pour un léger pop façon spring. Toutes les animations se déclenchent une seule fois (once:true) peu importe le re-scroll.
Comment le coder en React
Mettre en place la grille deux colonnes
Crée une section avec un conteneur centré max-w-6xl. À l'intérieur, utilise une grille CSS qui passe à une colonne sur mobile et à deux sur le breakpoint md. Ajoute items-center pour aligner verticalement les colonnes quand leurs hauteurs diffèrent.
<div className="grid grid-cols-1 gap-12 items-center md:grid-cols-2"> {/* text column */} {/* image column */} </div>Animer la colonne texte au scroll
Enveloppe la colonne texte dans une motion.div avec une opacity initiale à 0 et un décalage y de 20px. Utilise whileInView pour les restaurer et règle viewport à once:true avec une marge de -80px pour que le fade commence avant que l'élément entre complètement à l'écran.
<motion.div initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true, margin: "-80px" }} transition={{ duration: 0.5 }} >Décaler les lignes de points clés
Chaque ligne est une motion.div séparée qui glisse depuis x:-12. Ajoute un délai calculé depuis l'index, 0.1s de base plus 0.08s par item. Cela crée une cascade naturelle sans aucune API d'orchestration supplémentaire.
<motion.div initial={{ opacity: 0, x: -12 }} whileInView={{ opacity: 1, x: 0 }} viewport={{ once: true }} transition={{ delay: 0.1 + i * 0.08, duration: 0.4 }} >Faire apparaître l'image avec un scale pop
La colonne image est une motion.div carrée (aspect-square) avec overflow:hidden et un token de border radius. Règle le scale initial à 0.96 et anime-le à 1 avec une courbe custom [0.16, 1, 0.3, 1] sur 600ms, cela imite un spring sans importer l'API spring.
<motion.div initial={{ opacity: 0, scale: 0.96 }} whileInView={{ opacity: 1, scale: 1 }} viewport={{ once: true, margin: "-60px" }} transition={{ duration: 0.6, ease: [0.16, 1, 0.3, 1] }} className="aspect-square rounded-[var(--radius-xl)] overflow-hidden" >
Quand l'utiliser
Utilise cette section en milieu de page sur les landing pages d'agences, produits SaaS et startups qui ont besoin d'établir la confiance avant un bloc pricing ou témoignages. Elle fonctionne bien en deuxième ou troisième section, après un hero. Passe ton chemin si ton audience connaît déjà bien l'équipe (outils internes, docs développeurs) ou si la page est purement orientée conversion et qu'une photo d'équipe serait une distraction. Sur mobile la grille se réduit à une colonne, donc vérifie que le ratio de l'image paraît intentionnel à 375px plutôt que coupé maladroitement.
Utilisé par
- Stripe, Layout split associant texte éditorial et photo d'équipe/bureau sur la page about pour humaniser une marque orientée développeurs.
- Linear, Narration d'entreprise avec bullets de valeurs illustrées par des icônes à côté d'une image, le pattern exact que ce composant implémente.
- Loom, Sections about en deux colonnes avec révélations de texte staggerées et photographie d'équipe pour communiquer la culture.
FAQ
Comment résoudre une icône Lucide par son nom string à l'exécution ?
Caste l'espace de noms lucide-react en Record<string, React.ElementType> et cherche le nom de l'icône comme clé. Retourne null si la clé est absente pour que la mise en page se dégrade proprement sans conteneur d'icône.
Puis-je remplacer l'image par une vidéo ou une grille de portraits ?
La colonne droite est un simple div en overflow:hidden, remplace le div background-image par n'importe quel élément. Une vidéo (autoplay muted loop) ou une grille CSS de portraits s'intègre sans toucher au code d'animation.
Pourquoi utiliser background-image CSS plutôt qu'une balise img pour la photo ?
background-image avec background-size:cover gère n'importe quel ratio sans layout shift. En production, remplace-le par un composant Next.js Image avec objectFit:cover pour obtenir l'optimisation automatique et le lazy loading.
L'animation en cascade paraît trop lente sur les longues listes. Comment la régler ?
Réduis l'incrément de délai par item de 0.08 à 0.05 ou moins, et plafonne le délai total avec Math.min(0.1 + i * 0.05, 0.4) pour que les items au-delà du cinquième n'attendent pas sensiblement plus longtemps.