Retour au catalogue
Before/After Text
Comparaison textuelle avant/apres avec animations de barrage et de revelation.
comparisonmedium Both Responsive a11y
boldcorporatesaasagencyuniversalstacked
Theme
"use client";
import { motion } from "framer-motion";
import { ArrowRight, XCircle, CheckCircle2 } from "lucide-react";
interface Pair {
before: string;
after: string;
label: string;
}
interface ComparisonBeforeAfterTextProps {
title?: string;
subtitle?: string;
pairs?: Pair[];
}
const E: [number, number, number, number] = [0.16, 1, 0.3, 1];
export default function ComparisonBeforeAfterText({
title = "Avant / Apres",
subtitle = "",
pairs = [],
}: ComparisonBeforeAfterTextProps) {
return (
<section style={{ padding: "var(--section-padding-y) 0", background: "var(--color-background)" }}>
<div style={{ maxWidth: "var(--container-max-width)", margin: "0 auto", padding: "0 var(--container-padding-x)" }}>
<motion.div initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ duration: 0.6, ease: E }} style={{ textAlign: "center", marginBottom: "3rem" }}>
<h2 style={{ fontFamily: "var(--font-serif)", fontSize: "clamp(1.75rem, 3vw, 2.5rem)", fontWeight: 600, color: "var(--color-foreground)", marginBottom: "0.75rem" }}>{title}</h2>
{subtitle && <p style={{ fontSize: "1rem", color: "var(--color-foreground-muted)", fontFamily: "var(--font-sans)" }}>{subtitle}</p>}
</motion.div>
<div style={{ display: "flex", flexDirection: "column", gap: "1.5rem", maxWidth: "800px", margin: "0 auto" }}>
{/* Header row */}
<div style={{ display: "grid", gridTemplateColumns: "1fr auto 1fr", gap: "1rem", alignItems: "center" }}>
<div style={{ textAlign: "center" }}>
<span style={{ fontSize: "0.75rem", fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.08em", color: "var(--color-foreground-muted)", fontFamily: "var(--font-sans)" }}>Avant</span>
</div>
<div style={{ width: "32px" }} />
<div style={{ textAlign: "center" }}>
<span style={{ fontSize: "0.75rem", fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.08em", color: "var(--color-accent)", fontFamily: "var(--font-sans)" }}>Apres</span>
</div>
</div>
{pairs.map((p, i) => (
<motion.div
key={p.label}
initial={{ opacity: 0, y: 16 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: i * 0.1, ease: E }}
>
<p style={{ fontSize: "0.6875rem", fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.06em", color: "var(--color-foreground-muted)", fontFamily: "var(--font-sans)", textAlign: "center", marginBottom: "0.5rem" }}>{p.label}</p>
<div style={{ display: "grid", gridTemplateColumns: "1fr auto 1fr", gap: "1rem", alignItems: "center" }}>
{/* Before */}
<motion.div
initial={{ opacity: 0, x: -12 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: i * 0.1 + 0.1, ease: E }}
style={{ padding: "1rem 1.25rem", borderRadius: "var(--radius-lg)", background: "var(--color-background-alt)", border: "1px solid var(--color-border)", position: "relative" }}
>
<div style={{ display: "flex", alignItems: "flex-start", gap: "0.5rem" }}>
<XCircle size={16} style={{ color: "var(--color-foreground-light)", flexShrink: 0, marginTop: "2px" }} />
<p style={{ fontSize: "0.875rem", lineHeight: 1.5, color: "var(--color-foreground-muted)", fontFamily: "var(--font-sans)", textDecoration: "line-through", textDecorationColor: "var(--color-foreground-light)" }}>{p.before}</p>
</div>
</motion.div>
{/* Arrow */}
<motion.div
initial={{ scale: 0 }}
whileInView={{ scale: 1 }}
viewport={{ once: true }}
transition={{ delay: i * 0.1 + 0.2, type: "spring", stiffness: 300 }}
style={{ width: "32px", height: "32px", borderRadius: "var(--radius-full)", background: "var(--color-accent)", display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0 }}
>
<ArrowRight size={14} style={{ color: "var(--color-background)" }} />
</motion.div>
{/* After */}
<motion.div
initial={{ opacity: 0, x: 12 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: i * 0.1 + 0.2, ease: E }}
style={{ padding: "1rem 1.25rem", borderRadius: "var(--radius-lg)", background: "var(--color-accent-subtle)", border: "1px solid var(--color-accent)", borderColor: "color-mix(in srgb, var(--color-accent) 30%, transparent)" }}
>
<div style={{ display: "flex", alignItems: "flex-start", gap: "0.5rem" }}>
<CheckCircle2 size={16} style={{ color: "var(--color-accent)", flexShrink: 0, marginTop: "2px" }} />
<p style={{ fontSize: "0.875rem", lineHeight: 1.5, color: "var(--color-foreground)", fontFamily: "var(--font-sans)", fontWeight: 500 }}>{p.after}</p>
</div>
</motion.div>
</div>
</motion.div>
))}
</div>
</div>
</section>
);
}