Créer un hero React à colonnes parallax élastiques
Un hero React à colonnes parallax élastiques place trois colonnes de cards derrière un titre centré et décale la colonne gauche vers le haut et la droite vers le bas au scroll, via useScroll et useTransform de Framer Motion qui convertissent la progression de scroll en décalage vertical. La colonne centrale reste fixe pendant qu'une vignette radiale estompe les colonnes pour garder le texte lisible au premier plan.
- Stack : React + Framer Motion 11 + Lucide React, ~310 lignes, aucune dépendance supplémentaire.
- API clé : useScroll (target + offset), useTransform pour les valeurs y par colonne, animations de montée décalées.
- La colonne gauche passe de -6% à 0%, la droite de +6% à 0% pendant que la section défile hors écran.
- Accessible : les labels des cards sont de vrais éléments texte, les couches décoratives portent aria-hidden.
- L'effet parallax est piloté par le scroll et fonctionne sur mobile ; aucun pointeur nécessaire.
Ce composant hero React place trois colonnes de cards portrait en arrière-plan et les anime à des vitesses de scroll différentes, créant une illusion de profondeur élastique derrière un titre centré. La colonne gauche remonte doucement, la droite descend, le centre reste fixe. Une vignette radiale gère le contraste pour que le CTA reste lisible quelle que soit la couleur des cards.
Anatomie
La section contient quatre couches visuelles empilées en absolu dans un conteneur min-height:100vh. Tout au fond, un motif de grille CSS de 60px masqué par un dégradé radial pour s'estomper vers les bords. Derrière la grille, une ellipse floue en couleur d'accent crée une lumière douce. La grille de colonnes (CSS grid, trois colonnes égales, max-width 900px, centrée) vient ensuite ; chaque colonne est une motion.div pilotée par son propre transform y. Une vignette radiale (centre transparent, fond plein sur les bords) estompe les colonnes. Au-dessus, le contenu de premier plan dans une div flex centrée : badge, h1 avec un span accent en dégradé, paragraphe de description et ancre CTA.
Comment ça marche
useScroll de Framer Motion suit la progression du scroll sur l'élément section avec offset ['start start', 'end start'], ce qui donne des valeurs de 0 (haut de la section en haut de la fenêtre) à 1 (bas de la section en haut de la fenêtre). useTransform convertit ce range 0-1 en décalages verticaux : la colonne gauche passe de -6% à 0% (elle part plus haut et se stabilise), la droite de 6% à 0% (part plus bas et remonte), le centre reste à 0%. Ces MotionValues sont passées en prop y de style aux composants Column. Au montage, chaque card dans une colonne apparaît avec un délai décalé (i * 0.09s par card, plus un offset par colonne de 0, 0.06, 0.12s) via la courbe de Bézier custom [0.16, 1, 0.3, 1].
Comment le coder en React
Mettre en place le suivi de scroll sur la section
Attache un ref à l'élément section et passe-le à useScroll avec la paire d'offset qui couvre l'entrée et la sortie de la section du viewport. Cela donne une valeur scrollYProgress de 0 à 1 que tu peux diriger vers des transforms.
const sectionRef = useRef<HTMLElement>(null); const { scrollYProgress } = useScroll({ target: sectionRef, offset: ["start start", "end start"], });Dériver les décalages y par colonne avec useTransform
Crée trois valeurs de transform à partir du même scrollYProgress. Les colonnes extérieures reçoivent des plages opposées pour qu'elles convergent vers 0% au fil du scroll, produisant l'effet de pince élastique. Le centre reste constant.
const yLeft = useTransform(scrollYProgress, [0, 1], ["-6%", "0%"]); const yMid = useTransform(scrollYProgress, [0, 1], ["0%", "0%"]); const yRight = useTransform(scrollYProgress, [0, 1], ["6%", "0%"]);Construire le composant Column
Enveloppe la colonne dans une motion.div qui reçoit la MotionValue y en prop style. Chaque card à l'intérieur s'anime de opacity 0 / scale 0.94 vers l'opacité pleine au montage, avec un délai décalé basé sur l'index de la card et un offset par colonne.
<motion.div style={{ y }}> {cards.map((card, i) => ( <motion.div key={i} initial={{ opacity: 0, scale: 0.94 }} animate={{ opacity: 1, scale: 1 }} transition={{ duration: 0.6, delay: delay + i * 0.09, ease: EASE }} /> ))} </motion.div>Superposer la vignette et le contenu de premier plan
Place la grille de colonnes en position absolue derrière tout, puis ajoute une div en dégradé radial (centre transparent, fond opaque vers les bords) pour estomper les cards. Le titre, la description et le CTA vont dans un conteneur z-index:10 en relative pour qu'ils restent toujours au-dessus de la couche parallax.
Quand l'utiliser
À utiliser sur les portfolios d'agences, les landings d'outils de design et les homepages SaaS visuellement ambitieuses où tu veux de la profondeur sans vidéo ni asset lourd. L'animation pilotée par le scroll récompense les utilisateurs qui s'engagent sans coût au premier rendu. À éviter sur les pages e-commerce ou transactionnelles où l'objectif principal est un formulaire ou un bouton d'achat, le mouvement en arrière-plan entre en concurrence avec l'attention. Évite-le aussi si tes cards contiennent des images complexes plutôt que de simples labels texte, car la vignette ne masquera pas complètement des visuels chargés.
Utilisé par
- Stripe, Utilise des grilles de cards superposées avec de légers décalages de profondeur dans les heros de ses pages marketing produit.
- Framer, Présente des mises en page à colonnes parallax comme pattern de template central dans son builder de site et sur son propre site marketing.
- Lottiefiles, Déploie une grille de cards multi-colonnes avec des décalages verticaux pilotés par le scroll dans son hero, donnant l'impression d'une bibliothèque d'assets vivante.
- Webflow, Utilise régulièrement des colonnes de cards décalées à des vitesses de scroll différentes pour montrer la profondeur de design sur ses landing pages.
FAQ
Pourquoi la colonne gauche part-elle de -6% et va vers 0% plutôt que l'inverse ?
L'offset ['start start', 'end start'] signifie que scrollYProgress est à 0 quand le haut de la section s'aligne avec le haut du viewport. À ce moment, tu veux les colonnes écartées (-6%/+6%), pour qu'elles convergent vers 0% au fur et à mesure que l'utilisateur défile vers le bas et que la section sort, c'est la pince élastique.
Cet effet parallax nuit-il aux performances sur mobile ?
Non, Framer Motion applique les transforms y via l'API Web Animations sur le thread du compositeur, sans reflow de mise en page. L'effet est piloté par la position de scroll sans JavaScript qui tourne à chaque frame.
Comment ajouter une quatrième colonne sans casser la symétrie parallax ?
Ajoute une valeur yFar avec un range plus grand (ex. [-10%, 0%]) pour la colonne externe et décale les colonnes existantes vers l'intérieur. Garde des signes opposés sur les deux colonnes les plus extérieures pour que la convergence soit lisible. Mets à jour le CSS grid de trois à quatre colonnes égales.
Peut-on remplacer le parallax basé sur le scroll par un parallax au survol de la souris ?
Oui. Remplace useScroll/useTransform par useMotionValue pour mouseX/mouseY, puis convertis ces valeurs en décalages de colonnes avec useTransform sur un range plus petit. Ajoute un useSpring par-dessus pour l'inertie. Le composant Column reste identique, seule la source de la valeur y change.