Retour au catalogue

Pricing Cards

Grille de 3 cartes de prix avec highlight sur la formule recommandee. Clean et lisible.

pricingsimple Both Responsive a11y
minimalcorporatesaasagencyuniversalgrid
Theme
"use client";

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

interface Tier {
  name: string;
  price: number;
  currency: string;
  period: string;
  description: string;
  features: string[];
  highlighted: boolean;
  ctaLabel: string;
}

interface PricingCardsProps {
  badge?: string;
  title?: string;
  subtitle?: string;
  tiers?: Tier[];
}

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

export default function PricingCards({
  badge = "Tarifs",
  title = "Des offres adaptees a vos besoins",
  subtitle = "",
  tiers = [],
}: PricingCardsProps) {
  return (
    <section
      className="py-20 lg:py-28"
      style={{ background: "var(--color-background)" }}
    >
      <div className="mx-auto max-w-6xl px-6">
        {/* Header */}
        <motion.div
          initial={{ opacity: 0, y: 20 }}
          whileInView={{ opacity: 1, y: 0 }}
          transition={{ duration: 0.6, ease }}
          viewport={{ once: true }}
          className="text-center mb-16"
        >
          {badge && (
            <span
              className="inline-block mb-4 text-xs font-medium tracking-widest uppercase"
              style={{ color: "var(--color-accent)" }}
            >
              {badge}
            </span>
          )}
          <h2
            className="text-3xl md:text-4xl lg:text-5xl font-bold"
            style={{ color: "var(--color-foreground)" }}
          >
            {title}
          </h2>
          {subtitle && (
            <p
              className="mt-4 text-lg max-w-2xl mx-auto"
              style={{ color: "var(--color-foreground-muted)" }}
            >
              {subtitle}
            </p>
          )}
        </motion.div>

        {/* Cards grid */}
        <div className="grid md:grid-cols-3 gap-6 lg:gap-8">
          {tiers.map((tier, i) => (
            <motion.div
              key={tier.name}
              initial={{ opacity: 0, y: 30 }}
              whileInView={{ opacity: 1, y: 0 }}
              transition={{ duration: 0.5, ease, delay: i * 0.1 }}
              viewport={{ once: true }}
              className="relative flex flex-col rounded-xl p-8"
              style={{
                background: tier.highlighted
                  ? "var(--color-accent)"
                  : "var(--color-background-card)",
                border: tier.highlighted
                  ? "none"
                  : "1px solid var(--color-border)",
              }}
            >
              {tier.highlighted && (
                <span
                  className="absolute -top-3 left-1/2 -translate-x-1/2 text-xs font-semibold tracking-wide uppercase px-4 py-1 rounded-full"
                  style={{
                    background: "var(--color-background)",
                    color: "var(--color-accent)",
                  }}
                >
                  Populaire
                </span>
              )}

              <h3
                className="text-lg font-semibold"
                style={{
                  color: tier.highlighted
                    ? "var(--color-background)"
                    : "var(--color-foreground)",
                }}
              >
                {tier.name}
              </h3>
              <p
                className="mt-1 text-sm"
                style={{
                  color: tier.highlighted
                    ? "var(--color-background)"
                    : "var(--color-foreground-muted)",
                  opacity: tier.highlighted ? 0.8 : 1,
                }}
              >
                {tier.description}
              </p>

              <div className="mt-6 mb-8">
                <span
                  className="text-4xl font-bold"
                  style={{
                    color: tier.highlighted
                      ? "var(--color-background)"
                      : "var(--color-foreground)",
                  }}
                >
                  {tier.price}
                  <span className="text-lg font-normal ml-0.5">
                    {tier.currency === "EUR" ? "\u20ac" : "$"}
                  </span>
                </span>
                <span
                  className="text-sm ml-1"
                  style={{
                    color: tier.highlighted
                      ? "var(--color-background)"
                      : "var(--color-foreground-muted)",
                    opacity: tier.highlighted ? 0.7 : 1,
                  }}
                >
                  {tier.period}
                </span>
              </div>

              <ul className="flex-1 flex flex-col gap-3 mb-8">
                {tier.features.map((f) => (
                  <li key={f} className="flex items-start gap-3 text-sm">
                    <Check
                      size={16}
                      className="flex-shrink-0 mt-0.5"
                      style={{
                        color: tier.highlighted
                          ? "var(--color-background)"
                          : "var(--color-accent)",
                      }}
                    />
                    <span
                      style={{
                        color: tier.highlighted
                          ? "var(--color-background)"
                          : "var(--color-foreground)",
                        opacity: tier.highlighted ? 0.9 : 1,
                      }}
                    >
                      {f}
                    </span>
                  </li>
                ))}
              </ul>

              <button
                className="w-full py-3 rounded-lg text-sm font-semibold transition-opacity hover:opacity-90"
                style={{
                  background: tier.highlighted
                    ? "var(--color-background)"
                    : "var(--color-accent)",
                  color: tier.highlighted
                    ? "var(--color-accent)"
                    : "var(--color-background)",
                }}
              >
                {tier.ctaLabel}
              </button>
            </motion.div>
          ))}
        </div>
      </div>
    </section>
  );
}

Avis

Pricing Cards — React Pricing Section — Incubator