Retour au catalogue

Comparison Cards

Cartes de comparaison cote a cote avec prix, features et badge populaire.

comparisonmedium Both Responsive a11y
minimalboldsaasuniversalgrid
Theme
"use client";

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

interface ComparisonPlan {
  name: string;
  price: string;
  description: string;
  features: string[];
  highlighted?: boolean;
  ctaLabel: string;
}

interface ComparisonCardsProps {
  title?: string;
  subtitle?: string;
  plans?: ComparisonPlan[];
}

const ease: [number, number, number, number] = [0.16, 1, 0.3, 1];

export default function ComparisonCards({
  title = "Comparez nos offres",
  subtitle = "",
  plans = [],
}: ComparisonCardsProps) {
  return (
    <section className="py-20 lg:py-28 px-6" style={{ background: "var(--color-background)" }}>
      <div className="mx-auto max-w-6xl">
        <motion.div
          initial={{ opacity: 0, y: 16 }}
          whileInView={{ opacity: 1, y: 0 }}
          transition={{ duration: 0.5, ease }}
          viewport={{ once: true }}
          className="text-center mb-14"
        >
          <h2 className="text-3xl md:text-4xl font-bold" style={{ color: "var(--color-foreground)" }}>{title}</h2>
          {subtitle && <p className="mt-3 text-base" style={{ color: "var(--color-foreground-muted)" }}>{subtitle}</p>}
        </motion.div>

        <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
          {plans.map((plan, i) => (
            <motion.div
              key={i}
              initial={{ opacity: 0, y: 20 }}
              whileInView={{ opacity: 1, y: 0 }}
              transition={{ duration: 0.5, ease, delay: i * 0.1 }}
              viewport={{ once: true }}
              className="relative rounded-2xl p-6 flex flex-col"
              style={{
                background: plan.highlighted ? "var(--color-accent)" : "var(--color-background-card)",
                border: plan.highlighted ? "none" : "1px solid var(--color-border)",
              }}
            >
              {plan.highlighted && (
                <span className="absolute -top-3 left-1/2 -translate-x-1/2 px-3 py-1 text-[10px] font-bold uppercase tracking-wider rounded-full" style={{ background: "var(--color-background)", color: "var(--color-accent)" }}>
                  Populaire
                </span>
              )}
              <h3 className="text-lg font-semibold" style={{ color: plan.highlighted ? "var(--color-background)" : "var(--color-foreground)" }}>{plan.name}</h3>
              <div className="mt-2 text-3xl font-bold" style={{ color: plan.highlighted ? "var(--color-background)" : "var(--color-foreground)" }}>{plan.price}</div>
              <p className="mt-2 text-sm" style={{ color: plan.highlighted ? "var(--color-background)" : "var(--color-foreground-muted)", opacity: plan.highlighted ? 0.8 : 1 }}>{plan.description}</p>
              <ul className="mt-6 flex-1 flex flex-col gap-2.5">
                {plan.features.map((f, fi) => (
                  <li key={fi} className="flex items-center gap-2 text-sm" style={{ color: plan.highlighted ? "var(--color-background)" : "var(--color-foreground-muted)" }}>
                    <Check size={14} style={{ color: plan.highlighted ? "var(--color-background)" : "var(--color-accent)", flexShrink: 0 }} />
                    {f}
                  </li>
                ))}
              </ul>
              <motion.button
                whileHover={{ scale: 1.02 }}
                whileTap={{ scale: 0.98 }}
                className="mt-6 flex items-center justify-center gap-2 w-full py-2.5 text-sm font-medium rounded-xl transition-colors"
                style={{
                  background: plan.highlighted ? "var(--color-background)" : "var(--color-accent)",
                  color: plan.highlighted ? "var(--color-accent)" : "var(--color-background)",
                }}
              >
                {plan.ctaLabel}
                <ArrowRight size={14} />
              </motion.button>
            </motion.div>
          ))}
        </div>
      </div>
    </section>
  );
}

Avis

Comparison Cards — React Comparison Section — Incubator