Retour au catalogue
Portfolio Awards Wall
Portfolio affiche avec badges de recompenses et rubans. Layout style mur de trophees avec compteur de prix et mise en avant des distinctions.
portfoliomedium Both Responsive a11y
elegantboldagencysaasportfoliogrid
Theme
"use client";
import { motion } from "framer-motion";
import { Trophy, Award, Star } from "lucide-react";
interface AwardItem {
title: string;
client: string;
image: string;
awards: string[];
year: string;
}
interface PortfolioAwardsWallProps {
title?: string;
subtitle?: string;
items?: AwardItem[];
}
const EASE = [0.16, 1, 0.3, 1] as const;
const ICONS = [Trophy, Award, Star];
export default function PortfolioAwardsWall({
title = "Projets primes",
subtitle = "Palmares",
items = [],
}: PortfolioAwardsWallProps) {
const totalAwards = items.reduce((acc, item) => acc + item.awards.length, 0);
return (
<section style={{ paddingTop: "var(--section-padding-y, 6rem)", paddingBottom: "var(--section-padding-y, 6rem)", background: "var(--color-background)" }}>
<div style={{ maxWidth: "var(--container-max-width, 72rem)", margin: "0 auto", padding: "0 var(--container-padding-x, 1.5rem)" }}>
<motion.div initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ duration: 0.6, ease: EASE }} style={{ textAlign: "center", marginBottom: "1.5rem" }}>
<Trophy style={{ width: 32, height: 32, color: "var(--color-accent)", marginBottom: "1rem", display: "inline-block" }} />
<p style={{ fontSize: "0.8125rem", fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.1em", color: "var(--color-accent)", marginBottom: "0.5rem" }}>{subtitle}</p>
<h2 style={{ fontFamily: "var(--font-sans)", fontSize: "clamp(2rem, 4vw, 3rem)", fontWeight: 700, letterSpacing: "-0.03em", color: "var(--color-foreground)" }}>{title}</h2>
</motion.div>
<motion.div initial={{ opacity: 0 }} whileInView={{ opacity: 1 }} viewport={{ once: true }} transition={{ delay: 0.2 }} style={{ textAlign: "center", marginBottom: "3.5rem" }}>
<span style={{ fontSize: "3rem", fontWeight: 800, color: "var(--color-accent)", letterSpacing: "-0.03em" }}>{totalAwards}</span>
<span style={{ fontSize: "1rem", color: "var(--color-foreground-muted)", marginLeft: "0.5rem" }}>distinctions obtenues</span>
</motion.div>
<div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(320px, 1fr))", gap: "1.5rem" }}>
{items.map((item, i) => {
const Icon = ICONS[i % ICONS.length];
return (
<motion.div
key={item.title + i}
initial={{ opacity: 0, y: 24 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: i * 0.1, ease: EASE }}
style={{ position: "relative", borderRadius: "var(--radius-xl, 1.5rem)", overflow: "hidden", border: "1px solid var(--color-border)", background: "var(--color-background-card)" }}
>
{/* Ribbon */}
<div style={{ position: "absolute", top: "1rem", right: "-2rem", transform: "rotate(45deg)", padding: "0.25rem 2.5rem", background: "var(--color-accent)", zIndex: 5 }}>
<span style={{ fontSize: "0.625rem", fontWeight: 700, color: "var(--color-background)", textTransform: "uppercase", letterSpacing: "0.05em" }}>{item.year}</span>
</div>
<div style={{ aspectRatio: "16/10", overflow: "hidden" }}>
<img src={item.image} alt={item.title} style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }} />
</div>
<div style={{ padding: "1.5rem" }}>
<span style={{ fontSize: "0.6875rem", fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.08em", color: "var(--color-foreground-muted)" }}>{item.client}</span>
<h3 style={{ fontSize: "1.125rem", fontWeight: 700, color: "var(--color-foreground)", marginTop: "0.25rem" }}>{item.title}</h3>
<div style={{ display: "flex", flexWrap: "wrap", gap: "0.5rem", marginTop: "1rem" }}>
{item.awards.map((award) => (
<span key={award} style={{ display: "inline-flex", alignItems: "center", gap: "0.25rem", fontSize: "0.6875rem", fontWeight: 500, padding: "0.25rem 0.625rem", borderRadius: "var(--radius-full, 9999px)", background: "color-mix(in srgb, var(--color-accent) 10%, var(--color-background))", color: "var(--color-accent)", border: "1px solid color-mix(in srgb, var(--color-accent) 20%, transparent)" }}>
<Icon style={{ width: 12, height: 12 }} /> {award}
</span>
))}
</div>
</div>
</motion.div>
);
})}
</div>
</div>
</section>
);
}