Retour au catalogue

Hero Elastic Columns

Grille 3 colonnes de cards portrait avec effet parallax décalé au scroll, colonne gauche remonte, droite descend, centre fixe. Titre centré superposé avec vignette radiale. Idéal portfolio, agence, SaaS visuellement ambitieux.

heromedium Both Responsive a11y
elegantboldeditorialagencyportfoliosaasgrid
Theme

Cómo crear un hero de columnas parallax elásticas en React

Un hero React de columnas parallax elásticas coloca tres columnas de cards detrás de un titular centrado y desplaza la columna izquierda hacia arriba y la derecha hacia abajo a medida que el usuario hace scroll, usando useScroll y useTransform de Framer Motion para convertir el progreso del scroll en desplazamientos verticales. La columna central permanece fija mientras una viñeta radial difumina las columnas hacia el fondo, manteniendo legible el texto del primer plano.

  • Stack: React + Framer Motion 11 + Lucide React, ~310 líneas, sin dependencias adicionales.
  • API clave: useScroll (target + offset), useTransform para los valores y por columna, animaciones de montaje escalonadas.
  • La columna izquierda pasa de -6% a 0%, la derecha de +6% a 0% mientras la sección sale de la vista al hacer scroll.
  • Accesible: las etiquetas de las cards son elementos de texto reales, las capas decorativas llevan aria-hidden.
  • El efecto parallax se basa en el scroll y funciona en dispositivos táctiles; no requiere puntero.

Este componente hero React coloca tres columnas de cards en formato retrato en el fondo y las anima a distintas velocidades de scroll, creando una ilusión de profundidad elástica detrás de un titular centrado. La columna izquierda sube con suavidad, la derecha baja y el centro se mantiene fijo. Una viñeta radial gestiona el contraste para que el CTA siga limpio sin importar el contenido de las cards.

Anatomía

La sección tiene cuatro capas visuales apiladas en posición absoluta dentro de un contenedor min-height:100vh. Al fondo se sitúa un patrón de cuadrícula CSS de 60px enmascarado por un degradado radial para difuminarse hacia los bordes. Detrás de la cuadrícula, una elipse desenfocada en color de acento crea un brillo suave. A continuación viene la cuadrícula de columnas (CSS grid, tres columnas iguales, max-width 900px, centrada); cada columna es una motion.div controlada por su propio transform y. Una viñeta radial (centro transparente, fondo sólido en los bordes) difumina las columnas. Encima, el contenido de primer plano se sitúa en una div centrada con flex: badge, h1 con un span de acento en degradado, párrafo de descripción y un ancla CTA.

Cómo funciona

useScroll de Framer Motion sigue el progreso del scroll en el elemento section con offset ['start start', 'end start'], de modo que los valores van de 0 (parte superior de la sección en la parte superior del viewport) a 1 (parte inferior de la sección en la parte superior del viewport). useTransform convierte ese rango de 0 a 1 en desplazamientos verticales: la columna izquierda pasa de -6% a 0% (empieza más arriba y se asienta), la derecha de 6% a 0% (empieza más abajo y sube), y el centro se queda en 0% todo el tiempo. Estos MotionValues se pasan como prop y de style a los componentes Column. Al montar, cada card dentro de una columna aparece con un retardo escalonado (i * 0.09s por card, más un offset por columna de 0, 0.06, 0.12s) usando la curva de Bézier personalizada [0.16, 1, 0.3, 1].

Cómo crearlo en React

  1. Configurar el seguimiento del scroll en la sección

    Asocia un ref al elemento section y pásalo a useScroll con el par de offset que cubre la entrada y la salida de la sección del viewport. Esto da un valor scrollYProgress de 0 a 1 que puedes canalizar hacia los transforms.

    const sectionRef = useRef<HTMLElement>(null);
    const { scrollYProgress } = useScroll({
      target: sectionRef,
      offset: ["start start", "end start"],
    });
  2. Derivar los desplazamientos y por columna con useTransform

    Crea tres valores de transform a partir del mismo scrollYProgress. Las columnas exteriores reciben rangos opuestos para que converjan hacia 0% a medida que el usuario hace scroll, produciendo el efecto de pinza elástica. El centro permanece constante.

    const yLeft  = useTransform(scrollYProgress, [0, 1], ["-6%", "0%"]);
    const yMid   = useTransform(scrollYProgress, [0, 1], ["0%",  "0%"]);
    const yRight = useTransform(scrollYProgress, [0, 1], ["6%",  "0%"]);
  3. Construir el componente Column

    Envuelve la columna en una motion.div que reciba la MotionValue y como prop style. Cada card del interior se anima de opacity 0 / scale 0.94 a la opacidad completa al montar, con un retardo escalonado basado en el índice de la card y un offset por columna.

    <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>
  4. Superponer la viñeta y el contenido de primer plano

    Coloca la cuadrícula de columnas en posición absoluta detrás de todo, luego añade una div con degradado radial (centro transparente, fondo opaco hacia los bordes) para difuminar las cards. El titular, la descripción y el CTA van en un contenedor relative con z-index:10 para que siempre queden por encima de la capa parallax.

Cuándo usarlo

Usa este hero en portfolios de agencias, landings de herramientas de diseño y homepages SaaS visualmente ambiciosas donde quieras profundidad sin vídeo ni assets pesados. La animación basada en el scroll recompensa a los usuarios que interactúan sin coste en el primer renderizado. Evítalo en páginas de e-commerce o transaccionales donde el objetivo principal es un formulario o un botón de compra, el movimiento del fondo compite por la atención. Evítalo también si tus cards contienen imágenes complejas en lugar de simples etiquetas de texto, ya que la viñeta no enmascarará del todo los visuales recargados.

Usado por

  • Stripe, Utiliza cuadrículas de cards superpuestas con sutiles desplazamientos de profundidad en las secciones hero de sus páginas de marketing de producto.
  • Framer, Muestra los diseños de columnas parallax como patrón de plantilla central en su builder de sitios web y en su propio sitio de marketing.
  • Lottiefiles, Despliega una cuadrícula de cards multicolumna con desplazamientos verticales basados en el scroll en su hero, dando la impresión de una biblioteca de assets viva.
  • Webflow, Utiliza con frecuencia columnas de cards escalonadas a distintas velocidades de scroll para demostrar la profundidad de diseño en sus landing pages.

FAQ

¿Por qué la columna izquierda empieza en -6% y se mueve hacia 0% en lugar de al revés?

El offset ['start start', 'end start'] significa que scrollYProgress es 0 cuando la parte superior de la sección se alinea con la parte superior del viewport. En ese momento quieres las columnas separadas (-6%/+6%), para que converjan hacia 0% a medida que el usuario baja y la sección sale, eso es la pinza elástica.

¿Este efecto parallax perjudica el rendimiento en móvil?

No, Framer Motion aplica los transforms y mediante la Web Animations API usando el hilo del compositor, por lo que no hay reflow de maquetación. El efecto se basa en la posición del scroll sin JavaScript ejecutándose en cada frame.

¿Cómo añado una cuarta columna sin romper la simetría del parallax?

Añade un valor yFar con un rango mayor (p. ej. [-10%, 0%]) para la columna exterior y desplaza las columnas existentes hacia dentro. Mantén signos opuestos en las dos columnas más exteriores para que la convergencia se lea correctamente. Actualiza el CSS grid de tres a cuatro columnas iguales.

¿Puedo reemplazar el parallax basado en el scroll por un parallax al pasar el ratón?

Sí. Reemplaza useScroll/useTransform por useMotionValue para mouseX/mouseY, luego convierte esos valores en desplazamientos de columnas con useTransform sobre un rango más pequeño. Añade un useSpring encima para la inercia. El componente Column permanece idéntico, solo cambia la fuente del valor y.

"use client";

import { useRef } from "react";
import { motion, useScroll, useTransform, type MotionValue } from "framer-motion";
import { ArrowRight } from "lucide-react";

interface Card {
  label: string;
  accent?: boolean;
}

interface HeroElasticColumnsProps {
  title?: string;
  titleAccent?: string;
  description?: string;
  ctaLabel?: string;
  ctaUrl?: string;
  badge?: string;
  col1Cards?: Card[];
  col2Cards?: Card[];
  col3Cards?: Card[];
}

Code complet réservé à Pro

Code source intégral, export multi-framework et playground.

Passer en Pro, 9,99€/mois

Reseñas

Hero React columnas parallax con Framer Motion, Tutorial