Retour au catalogue

About Comparison

Avant/apres montrant la transformation de l'entreprise.

aboutmedium Both Responsive a11y
boldcorporatesaasagencyuniversalsplit
Theme

Cómo crear una sección de comparación antes/después en React

Una sección de comparación antes/después en React muestra dos tarjetas laterales en una grid CSS (1fr auto 1fr) separadas por una flecha animada. Cada tarjeta aparece con un fade hacia arriba al hacer scroll mediante Framer Motion whileInView con un retardo escalonado. La tarjeta 'después' usa un borde de acento para destacar visualmente el estado mejorado.

  • Stack: React + Framer Motion + Tailwind v4 + lucide-react, ~75 líneas en total.
  • Animación: whileInView con once:true, se activa una sola vez cuando cada tarjeta entra en el viewport.
  • El icono de flecha (lucide ArrowRight) pasa de scale 0 a 1 con un retardo de 300ms, generando un orden de revelación natural de izquierda a derecha.
  • Totalmente adaptado al tema mediante CSS custom properties; sin colores codificados de forma fija.
  • En móvil la grid se apila en una sola columna; la flecha permanece visible entre las dos tarjetas.

About Comparison es una sección React que cuenta la historia de una transformación en un diseño lado a lado: una tarjeta 'antes' a la izquierda, una flecha animada en el centro y una tarjeta 'después' a la derecha. Cada tarjeta enumera las métricas o datos clave como filas de etiqueta/valor, haciendo que la diferencia entre dos estados sea legible de un vistazo. El fade-in activado al hacer scroll mantiene la atención sin exigir más interacción que el desplazamiento.

Anatomía

La sección externa contiene un bloque de encabezado centrado (badge, h2, subtítulo) seguido de una grid de tres columnas. La grid usa la plantilla `1fr auto 1fr` para que la columna de la flecha ocupe solo el espacio estrictamente necesario. Cada SideCard es un motion.div independiente con un borde redondeado, una imagen opcional en un contenedor con ratio 3:2 en la parte superior, y una lista de pares etiqueta/valor separados por finas líneas horizontales. La tarjeta 'después' recibe un borde de acento coloreado y un título en color de acento para guiar la mirada.

Cómo funciona

Tres animaciones Framer Motion `whileInView` impulsan la revelación. El encabezado sube con fade sin retardo. La tarjeta 'antes' sigue 100ms después. La tarjeta 'después' aparece a los 200ms. La flecha entre ambas usa una animación de scale distinta desde 0, retardada a 300ms, para que surja después de que las dos tarjetas ya hayan empezado a entrar, reforzando la narrativa de izquierda a derecha. Todas las animaciones usan `viewport={{ once: true }}` para no repetirse al volver a hacer scroll.

Cómo crearlo en React

  1. Definir la forma de los datos

    Crea una interface `ComparisonSide` con un label, una URL de imagen opcional y un array de `{ label, value }`. Pasa `before` y `after` como props opcionales para que la sección se degrade con elegancia si falta uno de los lados.

    interface ComparisonSide {
      label: string;
      image?: string;
      points: { label: string; value: string }[];
    }
  2. Construir el subcomponente SideCard

    Envuelve la tarjeta en un `motion.div` con `initial={{ opacity: 0, y: 30 }}` y `whileInView={{ opacity: 1, y: 0 }}`. Acepta un `delay` y un booleano `accent`. Cuando `accent` es true, cambia el borde y el título al token de acento, esto distingue el estado 'después' sin ninguna clase adicional.

    <motion.div
      initial={{ opacity: 0, y: 30 }}
      whileInView={{ opacity: 1, y: 0 }}
      viewport={{ once: true }}
      transition={{ delay, duration: 0.5 }}
      style={{
        borderColor: accent
          ? "var(--color-accent)"
          : "var(--color-border)",
      }}
    >
  3. Montar la grid de tres columnas con la flecha

    Usa `grid-cols-[1fr_auto_1fr]` en pantallas grandes (columna única en móvil). Entre las dos tarjetas, renderiza un `motion.div` que pasa de `scale: 0` a `scale: 1` con 300ms de retardo. Rellena un círculo de 48x48 con el fondo de acento y coloca el icono ArrowRight en su interior.

    <div className="grid grid-cols-1 lg:grid-cols-[1fr_auto_1fr] gap-6 items-center">
      <SideCard side={before} delay={0.1} accent={false} />
      <motion.div
        initial={{ opacity: 0, scale: 0 }}
        whileInView={{ opacity: 1, scale: 1 }}
        viewport={{ once: true }}
        transition={{ delay: 0.3, duration: 0.4 }}
      >
        <div className="flex h-12 w-12 items-center justify-center rounded-full"
          style={{ backgroundColor: "var(--color-accent)" }}>
          <ArrowRight style={{ color: "var(--color-background)" }} />
        </div>
      </motion.div>
      <SideCard side={after} delay={0.2} accent={true} />
    </div>
  4. Conectar el encabezado y pasar los datos reales

    Añade un encabezado centrado encima de la grid: un span badge, un h2 y un párrafo de subtítulo, cada uno renderizado de forma condicional. Anima todo el bloque como un único `motion.div` con retardo 0. Luego pasa tus datos reales de antes/después, tres a cinco puntos por lado son el óptimo de legibilidad.

Cuándo usarlo

Recurre a esta sección en landing pages de SaaS, páginas de portfolio de agencias o cualquier historia de producto donde puedas cuantificar la transformación con métricas concretas. Funciona mejor a media página, después del hero, cuando el usuario tiene suficiente contexto para apreciar lo que significan las cifras. Evítala cuando la diferencia entre antes y después no se pueda expresar en pares cortos de etiqueta/valor, una timeline o una narración en prosa encaja mejor en ese caso. No coloques dos de estas secciones seguidas en la misma página.

Usado por

  • Notion, Usa tarjetas de métricas lado a lado en sus páginas 'para equipos' para mostrar las ganancias de productividad antes y después de la adopción.
  • Linear, Emplea un diseño antes/después estructurado en sus casos de estudio para contrastar los flujos de trabajo antiguos con los impulsados por Linear.
  • Stripe, Usa diseños comparativos en sus páginas de producto Payments y Billing para mostrar la fricción de un checkout heredado frente al flujo de Stripe.
  • Webflow, Presenta comparaciones antes/después de flujos de trabajo de desarrollador frente a diseñador para posicionar su builder no-code frente a los sitios codificados a mano.

FAQ

¿Cuántos puntos por lado debería incluir?

Tres a cinco puntos logran el equilibrio justo. Por debajo de tres, la tarjeta parece vacía; por encima de cinco, los usuarios dejan de leer. Prioriza las métricas que más importan a tu lector objetivo.

¿Puedo usar esto sin la imagen opcional?

Sí. El bloque de imagen está protegido por `side.image &&` así que la tarjeta se muestra correctamente sin ella. La lista de estadísticas empieza justo en la parte superior de la zona de padding.

¿Cómo cambio el color de acento de la tarjeta 'después'?

El componente lee `var(--color-accent)` del preset de tema activo. Cambia el tema a nivel del atributo `data-theme` y tanto el borde como el título se actualizan automáticamente.

¿La animación se vuelve a reproducir cuando el usuario sube de nuevo la página?

No. Los tres motion.div usan `viewport={{ once: true }}`, así que cada tarjeta se anima únicamente la primera vez que entra en el viewport.

"use client";

import React from "react";
import { motion } from "framer-motion";
import { ArrowRight } from "lucide-react";

interface ComparisonSide {
  label: string;
  image?: string;
  points: { label: string; value: string }[];
}

interface AboutComparisonProps {
  badge?: string;
  title?: string;
  subtitle?: string;
  before?: ComparisonSide;
  after?: ComparisonSide;
}

function SideCard({ side, delay, accent }: { side: ComparisonSide; delay: number; accent: boolean }) {
  return (

Code complet réservé à Pro

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

Passer en Pro, 9,99€/mois

Reseñas

Sección de comparación antes/después en React, Código +