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

Avis

Download Centered — React Download Section — Incubator