Retour au catalogue
Download Centered
Section download centree avec boutons par plateforme (macOS, Windows, Linux).
downloadsimple Both Responsive a11y
minimalcorporatesaasuniversalcentered
Theme
"use client";
import { motion } from "framer-motion";
import { Monitor, Apple, Download } from "lucide-react";
import type { LucideIcon } from "lucide-react";
interface Platform {
icon: string;
label: string;
url: string;
version: string;
}
interface DownloadCenteredProps {
title?: string;
titleAccent?: string;
description?: string;
platforms?: Platform[];
note?: string;
}
const EASE = [0.16, 1, 0.3, 1] as const;
const ICON_MAP: Record<string, LucideIcon> = {
apple: Apple,
monitor: Monitor,
download: Download,
};
export default function DownloadCentered({
title = "Telechargez pour votre",
titleAccent = "plateforme",
description = "Disponible sur macOS, Windows et Linux.",
platforms = [],
note = "Necessite 200 Mo d'espace disque",
}: DownloadCenteredProps) {
const defaultPlatforms: Platform[] = platforms.length
? platforms
: [
{ icon: "apple", label: "macOS", url: "#", version: "v2.4.1 — Apple Silicon" },
{ icon: "monitor", label: "Windows", url: "#", version: "v2.4.1 — 64 bits" },
{ icon: "download", label: "Linux", url: "#", version: "v2.4.1 — .deb / .AppImage" },
];
return (
<section
style={{
position: "relative",
overflow: "hidden",
paddingTop: "var(--section-padding-y-lg)",
paddingBottom: "var(--section-padding-y-lg)",
background: "var(--color-background)",
}}
>
<div
style={{
width: "100%",
maxWidth: "var(--container-max-width)",
margin: "0 auto",
padding: "0 var(--container-padding-x)",
textAlign: "center",
}}
>
<motion.h2
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, ease: EASE }}
style={{
fontFamily: "var(--font-sans)",
fontSize: "clamp(2rem, 4vw, 3.5rem)",
fontWeight: 700,
lineHeight: 1.1,
letterSpacing: "-0.03em",
color: "var(--color-foreground)",
marginBottom: "1rem",
}}
>
{title}{" "}
<em style={{ fontFamily: "var(--font-serif)", fontStyle: "italic", fontWeight: 400, color: "var(--color-accent)" }}>
{titleAccent}
</em>
</motion.h2>
<motion.p
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.08, ease: EASE }}
style={{
fontSize: "1.125rem",
lineHeight: 1.7,
color: "var(--color-foreground-muted)",
maxWidth: "500px",
margin: "0 auto 3rem",
}}
>
{description}
</motion.p>
{/* Platform buttons */}
<motion.div
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.15, ease: EASE }}
style={{
display: "flex",
justifyContent: "center",
gap: "1.25rem",
flexWrap: "wrap",
marginBottom: "2rem",
}}
>
{defaultPlatforms.map((p, i) => {
const Icon = ICON_MAP[p.icon] ?? Download;
return (
<motion.a
key={p.label}
href={p.url}
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.45, delay: 0.2 + i * 0.08, ease: EASE }}
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
gap: "0.75rem",
padding: "1.5rem 2.5rem",
borderRadius: "var(--radius-lg)",
border: "1px solid var(--color-border)",
background: "var(--color-background-card)",
textDecoration: "none",
minWidth: 200,
transition: "border-color var(--duration-normal) var(--ease-out)",
}}
>
<Icon style={{ width: 32, height: 32, color: "var(--color-accent)" }} />
<span style={{ fontWeight: 700, fontSize: "1.0625rem", color: "var(--color-foreground)" }}>
{p.label}
</span>
<span style={{ fontSize: "0.75rem", color: "var(--color-foreground-muted)" }}>
{p.version}
</span>
</motion.a>
);
})}
</motion.div>
{note && (
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.4, delay: 0.4, ease: EASE }}
style={{ fontSize: "0.8125rem", color: "var(--color-foreground-muted)", opacity: 0.7 }}
>
{note}
</motion.p>
)}
</div>
</section>
);
}