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>
);
}